Registration icon indicating copy to clipboard operation
Registration copied to clipboard

Registering Native and RTD based Async functions

Open ateene opened this issue 6 years ago • 0 comments

The order of the ExcelRegistion statement needs to be different for registering RTD or Native async functions.

case #1

Here is my example that works for registering RTD async functions and what I think the wrapped functions look like. In this case all three async functions work as expected with the dnaDelayedTaskHello working as RTD based async function, and the postAsyncReturnConfig works for both the dnaDelayedTaskHello and dnaRtdWrapped functions, returning #GETTING_DATA while processing.

using System; using System.Linq.Expressions; using System.Threading.Tasks; using ExcelDna.Integration; using ExcelDna.Registration; using ExcelDna.Registration.Utils; using ExcelDna.IntelliSense;

namespace ExcelDnaRegistration { public class AddIn : IExcelAddIn { public void AutoOpen() { // Register Funtion settings var postAsyncReturnConfig = GetPostAsyncReturnConversionConfig(); ExcelRegistration.GetExcelFunctions() .ProcessAsyncRegistrations(nativeAsyncIfAvailable: false) .ProcessParameterConversions(postAsyncReturnConfig) .RegisterFunctions();

        // Intellinse Sense
        IntelliSenseServer.Register();
    }
    
    public void AutoClose()
    {
    }
    
    static ParameterConversionConfiguration GetPostAsyncReturnConversionConfig()
    {
        // This conversion replaces the default #N/A return value of async functions with the #GETTING_DATA value.
        var rval = ExcelError.ExcelErrorGettingData;
        return new ParameterConversionConfiguration()
            .AddReturnConversion((type, customAttributes) => type != typeof(object) ? null : ((Expression<Func<object, object>>)
                                            ((object returnValue) => returnValue.Equals(ExcelError.ExcelErrorNA) ? rval : returnValue)));
    }
}

public class Functions
{
    // .NET 4.5 function with async/await
    [ExcelAsyncFunction(Description = "Excel Dna Async Test")]
    public static async Task<string> dnaDelayedTaskHello(
        [ExcelArgument(Name = "name")] string name,
        [ExcelArgument(Name = "delay(ms)")] int msDelay)
    {
        await Task.Delay(msDelay);
        return $"Hello {name}";
    }

    // This is what I think the generated RTD based Task wrappers looks like.
    [ExcelFunction]
    public static object dnaRtdWrapper(string name, int msDelay)
    {
        return AsyncTaskUtil.RunTask("dnaExplicitWrap", new object[] { name, msDelay }, () => dnaDelayedTaskHello(name, msDelay));
    }

    // This is what I think the generated Native based async wrapper looks like
    [ExcelFunction]
    public static void dnaNativeWrapper(string name, int msDelay, ExcelAsyncHandle asyncHandle)
    {
        NativeAsyncTaskUtil.RunTask(() => dnaDelayedTaskHello(name, msDelay), asyncHandle);
    }
}

}

case #2

When I make the following change for registering Native async functions

ExcelRegistration.GetExcelFunctions() .ProcessAsyncRegistrations(nativeAsyncIfAvailable: true) .ProcessParameterConversions(postAsyncReturnConfig) .RegisterFunctions();

I get a System.ArgumentOutOfRangeException error for the ExcelRegistraion statement

case #3

I can fix this by changing the order of statement to the following, then all three async functions work as expected. The dnaDelayedTaskHello works as Native async function

ExcelRegistration.GetExcelFunctions()
            .ProcessParameterConversions(postAsyncReturnConfig)
            .ProcessAsyncRegistrations(nativeAsyncIfAvailable: true)
            .RegisterFunctions();

case #4

When I switch back to the nativeAsyncIfAvable back to false keeping the same order

ExcelRegistration.GetExcelFunctions() .ProcessParameterConversions(postAsyncReturnConfig) .ProcessAsyncRegistrations(nativeAsyncIfAvailable: false) .RegisterFunctions();

All three async function work, however the postAsyncReturnConfig does not work for the dnaDelayTaskHello function (it still returns the #N/A while processing), whereas the dnaRtdWrapper does return the #GETTIN_DATA while processing.

So It looks like I have to case #1 for registering RTD async functions and case #3 for registering Native async functions.

ateene avatar Feb 06 '18 14:02 ateene