Hi Graeme,
The Excel C API (which you access via XlCall.Excel...) can (mostly) only be used on the main Excel thread.
So, when you're in the 'async' part of a function it is expected to fail.
For the 'Works()' case, the code up to the first 'await' runs on the thread that enters the function, which is still the Excel calculation thread.
The Excel-DNA Registration rewrites your non-async function to an async one by just wrapping it into a call to Task.Run.
That means all the code in your function will then run on the Task's threadpool thread.
One workaround would be to write the wrapper yourself, in terms of a Task<T> returning function.
Otherwise, you can also implement the async function in terms of the AsyncTaskUtil helper yourself.
Something like
[ExcelFunction]
public static object DownloadStringFromURL(string url)
{
var functionName = nameof(DownloadStringFromURL);
var parameters = new object[] { url };
HttpClient myHttpClient = new HttpClient();
// Here you can still get hold of the caller ...
return AsyncTaskUtil.RunTask(functionName, parameters, async () =>
{
//The actual asyncronous block of code to execute.
return await myHttpClient.GetStringAsync(url);
})
}
You do have to be pretty careful in how you use the ExcelReference that is returned from xlfCaller later.
First, you have to be back on the main thread to use it.
Then, it's not easy to determine whether the reference (particularly the sheet pointer in the reference) is still valid.
If it's no longer valid (say the sheet does not exist anymore) then you can get Excel to crash by using the ExcelReference.
One workaround is to convert to ExcelReference to a string immediately after you get it giving you something like [Sheet1]!A1.
That's safe to store and then use later, but now the sheet might have been renamed, and your string reference is no longer valid.
So, by trying to capture the caller ExcelReference for later us in an async function, you are playing with fire.
Can you say more about your use case?
-Govert