Difference in async and params registration order produces different functions

160 views
Skip to first unread message

Kenneth Gabriel Birkedahl Fabricius

unread,
Jul 21, 2022, 5:09:24 AM7/21/22
to Excel-DNA
Hi Govert,

I have followed your example on ParamsFunctionExamples, which works perfectly.

However, I have noted some issues with the registration, when having both params and async functions.

I need most of my functionality to be async, which means I am using both [ExcelAsyncFunction]-attribute and [ExcelFunction]-attribute with explicit ExcelAsyncHandle.

Just to let you know, there is a difference between calling:
ExcelRegistration.GetExcelFunctions().ProcessAsyncRegistrations(nativeAsyncIfAvailable: true).ProcessParamsRegistrations().RegisterFunctions();
and
ExcelRegistration.GetExcelFunctions().ProcessParamsRegistrations().ProcessAsyncRegistrations(nativeAsyncIfAvailable: true).RegisterFunctions();

i.e. whether we process async registrations before or after param registrations. The first does not produce any params-like functions, since IsParamsMethod(registration) is always false. This stems from the reg.FunctionLambda.Parameters.Last() pointing to the ExcelAsyncHandle instead of the actual params object[] parameter.

I don't know, if it is by design, or there are some technical limitations, but if not, one would expect that the registration order did not matter in this case.

As always, thanks for a great framework!

Best,
Kenneth

Kenneth Gabriel Birkedahl Fabricius

unread,
Jul 22, 2022, 7:56:27 AM7/22/22
to Excel-DNA
Hi Govert,

I dug a little deeper, and it just so happens, that if I process params registrations prior to async registrations, when targeting .Net 6, Excel-DNA throws errors upon registering an async params function:

Initialization [Warning] Assembly SYSTEM.SECURITY.CRYPTOGRAPHY.PRIMITIVES could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.SECURITY.CRYPTOGRAPHY.ALGORITHMS could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.WINDOWS.FORMS.PRIMITIVES could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.COMPONENTMODEL.PRIMITIVES could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.THREADING.TASKS.PARALLEL could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.RUNTIME could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.LINQ could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.COLLECTIONS could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.LINQ.EXPRESSIONS could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.RUNTIME.INTEROPSERVICES could not be loaded from resources.
Exception while registering method AsyncParamsFunction - System.NotSupportedException: A non-collectible assembly may not reference a collectible assembly.
   at System.Reflection.Emit.ModuleBuilder.GetTypeRef(QCallModule module, String strFullName, QCallModule refedModule, String strRefedModuleFileName, Int32 tkResolution)
   at System.Reflection.Emit.ModuleBuilder.GetTypeRefNested(Type type, Module refedModule, String strRefedModuleFileName)
   at System.Reflection.Emit.ModuleBuilder.GetTypeTokenWorkerNoLock(Type type, Boolean getGenericDefinition)
   at System.Reflection.Emit.ModuleBuilder.GetTypeTokenInternal(Type type, Boolean getGenericDefinition)
   at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelperWorker(Type clsArgument, Boolean lastWasGenericInst)
   at System.Reflection.Emit.SignatureHelper.AddOneArgTypeHelper(Type clsArgument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
   at System.Reflection.Emit.SignatureHelper.AddArgument(Type argument, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
   at System.Reflection.Emit.SignatureHelper.AddArguments(Type[] arguments, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers)
   at System.Reflection.Emit.SignatureHelper.GetMethodSigHelper(Module scope, CallingConventions callingConvention, Int32 cGenericParam, Type returnType, Type[] requiredReturnTypeCustomModifiers, Type[] optionalReturnTypeCustomModifiers, Type[] parameterTypes, Type[][] requiredParameterTypeCustomModifiers, Type[][] optionalParameterTypeCustomModifiers)
   at System.Reflection.Emit.MethodBuilder.GetMethodSignature()
   at System.Reflection.Emit.MethodBuilder.GetTokenNoLock()
   at System.Reflection.Emit.MethodBuilder.GetToken()
   at System.Reflection.Emit.MethodBuilder.SetImplementationFlags(MethodImplAttributes attributes)
   at System.Linq.Expressions.Compiler.DelegateHelpers.MakeNewCustomDelegate(Type[] types)
   at System.Linq.Expressions.Compiler.DelegateHelpers.MakeNewDelegate(Type[] types)
   at System.Linq.Expressions.Compiler.DelegateHelpers.MakeDelegateType(Type[] types)
   at System.Linq.Expressions.Expression.Lambda(Expression body, String name, Boolean tailCall, IEnumerable`1 parameters)
   at ExcelDna.Registration.AsyncRegistration.WrapMethodNativeAsyncTask(LambdaExpression functionLambda)
   at ExcelDna.Registration.AsyncRegistration.ProcessAsyncRegistrations(IEnumerable`1 registrations, Boolean nativeAsyncIfAvailable)+MoveNext()
Initialization [Warning] Assembly SYSTEM.DRAWING.COMMON could not be loaded from resources.
Initialization [Warning] Assembly SYSTEM.DRAWING.PRIMITIVES could not be loaded from resources.
 
If I process registrations the other way around (i.e. async registrations prior to params registrations), the params-part is ignored, that is I end up with only async functions even though I expected async params.

Please find an example project targeting both net472 and net6.0 running the 1.6.0-preview3 version of the Excel-DNA packages. Included is an example Excel-file.

Best,
Kenneth
RegistrationTester.zip

Govert van Drimmelen

unread,
Jul 22, 2022, 1:35:31 PM7/22/22
to exce...@googlegroups.com

Hi Kenneth,

 

Thank you for reporting the problem and sending a sample project to test with easily.

I see the same error and will have to dig in a bit to see what’s going on.

 

Note that you should target “net6.0-windows” as the TargetFramework, not just “net6.0”.’

But this is not the cause of the issue you see with the “NotSupportedException”.

 

Also, there is a switch in version 1.6.0-preview4 that allows the hosting AssemblyLoadContext to not be marked as “collectible”:

<ExcelAddInDisableAssemblyContextUnload>true</ExcelAddInDisableAssemblyContextUnload>

but sadly this does not seem to change the behaviour either.

 

Most likely the type that fails to load is the “ExcelAsyncHandle” type in ExcelDna.Integration.

But I can’t tell which assembly in this context is “non-collectible”.

If the module being created for the dynamic wrapper function is non-collectible, then I imagine the “ExcelAddInDisableAssemblyContextUnload” flag should mean that ExcelAsyncHandle is now in a non-collectible assembly too.

 

So I’m not sure what is going, and it will need some more debugging.

 

-Govert

--
You received this message because you are subscribed to the Google Groups "Excel-DNA" group.
To unsubscribe from this group and stop receiving emails from it, send an email to exceldna+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/exceldna/94277b08-6522-4279-8516-0b311e3c0d5cn%40googlegroups.com.

Sergey Vlasov

unread,
Aug 3, 2022, 7:43:56 AM8/3/22
to Excel-DNA
Kenneth,

As your project uses a .dna file, instead of ExcelAddInDisableAssemblyContextUnload property, you need to add DisableAssemblyContextUnload to the .dna file like this:

 

<DnaLibrary Name="ProcessRegistrationTest Excel Add-In" RuntimeVersion="v4.0" DisableAssemblyContextUnload="true">

 

With latest ExcelDna packages:

    <PackageReference Include="ExcelDna.AddIn" Version="1.6.0-preview5" />

    <PackageReference Include="ExcelDna.Integration" Version="1.6.0-preview5" />

    <PackageReference Include="ExcelDna.Registration" Version="1.6.0-preview5" />

 

It prevents the System.NotSupportedException for me.

 

Can you please test it on your machine?

Govert van Drimmelen

unread,
Aug 4, 2022, 4:47:16 PM8/4/22
to exce...@googlegroups.com

Hi Kenneth and Sergey,

 

In addition to the option of running the add-in inside a “non unloadable AssemblyLoadContext” by setting the DisableAssemblyContextUnload flag, you can also wrap the problematic registration code inside a “ContextualReflectionContext” where the dynamic code will get generated in the right place.

 

For this your AutoOpen would becode:

 

        public void AutoOpen()

        {

            using (var ctx = System.Runtime.Loader.AssemblyLoadContext.EnterContextualReflection(this.GetType().Assembly))

            {

                ExcelRegistration

                    .GetExcelFunctions()

                    .ProcessParamsRegistrations()

                    .ProcessAsyncRegistrations(nativeAsyncIfAvailable: true)

                    .RegisterFunctions();

            }

        }

 

So that’s a second option you can test.

 

-Govert

 

 

From: exce...@googlegroups.com <exce...@googlegroups.com> On Behalf Of Sergey Vlasov
Sent: 3 August 2022 13:44
To: Excel-DNA <exce...@googlegroups.com>
Subject: [ExcelDna] Re: Difference in async and params registration order produces different functions

 

Kenneth,

--

You received this message because you are subscribed to the Google Groups "Excel-DNA" group.
To unsubscribe from this group and stop receiving emails from it, send an email to exceldna+u...@googlegroups.com.

Reply all
Reply to author
Forward
0 new messages