Delegates not called after registration

Jul 10, 2014 at 10:16 AM
Hi govert,

First of all, I wanted to thank you for your work and all the features you have given to Excel-DNA.

I contact you today as I am working on a project requiring dynamic delegate registration.

The aim is to load and unload functions from an appdomain and by marshalling and reflection, construct dynamically a delegate to them from another appdomain and resgister them in Excel.

I have been doing well so far and have verifyied that the delgates were working after all of this process.

The delegates are as well registered in a good format in excel and accessible from the function library but when you call them, nothing happens.

Here is a part of my code about the delegate construction based on expression trees :

List<Delegate> delegates = new List<Delegate>();
        List<object> funcOrCommandAttribs = new List<object>();
        List<List<object>> excelArguments = new List<List<object>>();

        // Get the right marshall object to link onto for the delegates 
        AssemblyLoader loader = LoadingInfos.getInstance().GetAppDomainProxyManager(dllPath).Proxy;
        // Get the methodinfo needed to construct the delegates expression tree
        MethodInfo executeMethod = loader.GetType().GetMethod("ExecuteMethod");

        foreach (var methodInfo in methods)
        {
            List<object> methodArguments = new List<object>();
            List<ParameterExpression> paramExp = new List<ParameterExpression>();


            // For each argument, we include them in the expression tree and declare them for excel description
            foreach (var param in methodInfo.GetParameters())
            {
                paramExp.Add(Expression.Parameter(param.ParameterType, param.Name));
                methodArguments.Add(new ExcelArgumentAttribute {Name = param.Name});
            }

            excelArguments.Add(methodArguments);

            ConstantExpression methodName = Expression.Constant(methodInfo.Name);

            //We construct an array of object variable, useful for linking the delegate declaration to the reflection call
            var array = Expression.NewArrayInit(typeof(object),
                                                paramExp.Select(param => Expression.Convert(param, typeof(object))));

            // Call to the reflection method, body construction
            var call =
                Expression.Call(Expression.Constant(loader), executeMethod,
                    methodName, array);

            // We keep as well each function attribute to have the same description in excel than it were a classical addin manager
            foreach (Attribute attr in methodInfo.GetCustomAttributes(false))
            {
                Type attribType = attr.GetType();
                if (attribType.FullName == "ExcelDna.AddinManager.Attributes.ExcelDnaFunctionAttribute")
                {
                    ExcelDnaFunctionAttribute convert = attr as ExcelDnaFunctionAttribute;

                    ExcelFunctionAttribute attrib = new ExcelFunctionAttribute
                                                        {
                                                            Name = convert.Name,
                                                            Category = convert.Category,
                                                            Description = convert.Description,
                                                            ExplicitRegistration = convert.ExplicitRegistration,
                                                            HelpTopic = convert.HelpTopic,
                                                            IsClusterSafe = convert.IsClusterSafe,
                                                            IsExceptionSafe = convert.IsExceptionSafe,
                                                            IsHidden = convert.IsHidden,
                                                            IsMacroType = convert.IsMacroType,
                                                            IsThreadSafe = convert.IsThreadSafe,
                                                            IsVolatile = convert.IsVolatile
                                                        };
                    funcOrCommandAttribs.Add(attrib);
                }

                if (attribType.FullName == "ExcelDna.AddinManager.Attributes.ExcelDnaCommandAttribute")
                {
                    ExcelDnaCommandAttribute convert = attr as ExcelDnaCommandAttribute;

                    ExcelCommandAttribute attrib = new ExcelCommandAttribute
                                                       {
                                                           Name = convert.Name,
                                                           Description = convert.Description,
                                                           HelpTopic = convert.HelpTopic,
                                                           ShortCut = convert.ShortCut,
                                                           MenuName = convert.MenuName,
                                                           MenuText = convert.MenuText,
                                                           IsExceptionSafe = convert.IsExceptionSafe,
                                                           ExplicitRegistration = convert.ExplicitRegistration
                                                       }; 
                    funcOrCommandAttribs.Add(attrib);
                }
            }

            // We create the lambda expression as (param1, .., paramN) => 
            // (MethdodInfo)ExecuteMethod.(loader, object[]{param1, .., paramN})
            var func = Expression.Lambda(Expression.Convert(call, methodInfo.ReturnType),methodInfo.Name, paramExp);

            delegates.Add(func.Compile());
        }

        ExcelIntegration.RegisterDelegates(delegates, funcOrCommandAttribs, excelArguments);
Do you have any clue about why it is not working or can at least help me to solve this issue ?

Thanks
Coordinator
Jul 10, 2014 at 10:51 AM
Hi,

I understand that the functions do appear in Excel, but it just seems as if the delegate is not called?
I presume you get #VALUE back in Excel? This is probably due to an exception in the call.

You can Register an UnhandlerExceptionHandler (call ExcelIntegration.RegisterUnhandledExceptionHandler from you AutoOpen) and then try to return the exception information. That might give you a clue.

Otherwise, I suggest you start with a simpler case, where the function is not in a different AppDomain, but a just a local function. If that does not work, you can make a small example project and I can have a look.

-Govert
Jul 10, 2014 at 1:00 PM
Thanks for your answer,

I just debugged my code and thanks to the handler I understand a little bit more what is happening.

This is the error :

Type 'ExcelDna.Integration.ExcelEmpty' in assembly 'ExcelDna.Integration, Version=0.32.5236.31783, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

To me, it is quite simple, the delegate is linked directly to Excel and when it does want to pass the different types of parameters, as long as, excel types of values (#value, #na, etc..) are not serializable such as native value types, they won't be accessible in the other appdomain linked where every computation step is done.

Do you think about any workaround for that, like converting the types before going through the app domains or something nicer ?

Thanks
Coordinator
Jul 10, 2014 at 6:08 PM
Reading your initial post again, I would suggest a different direction. Instead of trying to marshal across AppDomains with delegates, could you not load the code as a separate add-in?

At the moment Excel-DNA has no support for unloading function or parts of an add-in, while separate add-ins can be cleanly loaded and unloaded.

-Govert
Jul 11, 2014 at 7:46 AM
Hi govert,

Thanks for the advice, but actually that our previous method to load addins in different xlls and the problem was that versionning is not easy with that as every xll is then registered strongly in Excel with registry keys.

I solved this issue by just using homemade serializable attributes that I convert to ExcelDna Attributes before registering the delegates.

The solution is now fully operational.

Thanks a lot