Пример #1
0
        static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

            XlAddIn.SetJump(index, mi.FunctionPointer);
            string exportedProcName = string.Format("f{0}", index);

            object[] registerParameters = GetRegisterParameters(mi, exportedProcName);
            string   registerName       = (string)registerParameters[3];

            if (!registeredNames.Add(registerName))
            {
                // Not added to the set of names, so it was already present
                // This function will be registered with a name that has already been used (by this add-in)
                if (mi.SuppressOverwriteError)
                {
                    // Logged at Info level - to allow re-registration without error popup
                    Logger.Registration.Info("Repeated function name: '{0}' - previous registration will be overwritten. ", registerName);
                }
                else
                {
                    // This logged as an error, but the registration continues - the last function with the name wins, for backward compatibility.
                    Logger.Registration.Error("Repeated function name: '{0}' - previous registration will be overwritten. ", registerName);
                }
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Logger.Registration.Info("Register - XllPath={0}, ProcName={1}, FunctionType={2}, Name={3} - Result={4}",
                                         registerParameters[0], registerParameters[1], registerParameters[2], registerName,
                                         xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    Logger.Registration.Error("xlfRegister call failed for function or command: '{0}'", mi.Name);
                }
                // Now clear out the xll path and store the parameters to support RegistrationInfo access.
                registerParameters[0] = null;
                registrationInfo.Add(registerParameters);
            }
            catch (Exception e)
            {
                Logger.Registration.Error(e, "Registration failed for function or command: '{0}'", mi.Name);
            }
        }
Пример #2
0
 static string Truncate(string s, int length, string logDetail, XlMethodInfo mi)
 {
     if (s == null || s.Length <= length)
     {
         return(s);
     }
     Logger.Registration.Warn("Truncated " + logDetail + " of function '{0}'", mi.Name);
     return(s.Substring(0, length));
 }
Пример #3
0
        static void Register(List <MethodInfo> methods, List <object> targets, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            Debug.Assert(targets == null || targets.Count == methods.Count);

            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods, targets, methodAttributes, argumentAttributes);

            xlMethods.ForEach(RegisterXlMethod);
            // Increment the registration version (safe to call a few times)
            registrationInfoVersion += 1.0;
        }
Пример #4
0
 static void RegisterShortCut(XlMethodInfo mi)
 {
     if (!string.IsNullOrEmpty(mi.ShortCut))
     {
         object xlCallResult;
         XlCallImpl.TryExcelImpl(XlCallImpl.xlcOnKey, out xlCallResult, mi.ShortCut, mi.Name);
         // CONSIDER: We ignore result and suppress errors - maybe log?
         addedShortCuts.Add(mi.ShortCut);
     }
 }
Пример #5
0
        // This is the main conversion function called from XlLibrary.RegisterMethods
        // targets may be null - the typical case
        public static List <XlMethodInfo> ConvertToXlMethodInfos(List <MethodInfo> methods, List <object> targets, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            List <XlMethodInfo> xlMethodInfos = new List <XlMethodInfo>();

            // Set up assembly
            // Examine the methods, built the types and infos
            // Bake the assembly and export the function pointers

            AssemblyBuilder assemblyBuilder;
            ModuleBuilder   moduleBuilder;
            TypeBuilder     wrapperTypeBuilder;

            assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName("ExcelDna.DynamicDelegateAssembly"),
                AssemblyBuilderAccess.Run /*AndSave*/);
            moduleBuilder      = assemblyBuilder.DefineDynamicModule("DynamicDelegates");
            wrapperTypeBuilder = moduleBuilder.DefineType("WrapperType");

            for (int i = 0; i < methods.Count; i++)
            {
                MethodInfo    mi           = methods[i];
                object        target       = (targets == null) ? null : targets[i];
                object        methodAttrib = (methodAttributes != null && i < methodAttributes.Count) ? methodAttributes[i] : null;
                List <object> argAttribs   = (argumentAttributes != null && i < argumentAttributes.Count) ? argumentAttributes[i] : null;
                try
                {
                    XlMethodInfo xlmi = new XlMethodInfo(mi, target, methodAttrib, argAttribs);
                    // Skip if suppressed
                    if (xlmi.ExplicitRegistration)
                    {
                        Logger.Registration.Info("Suppressing due to ExplictRegistration attribute: '{0}.{1}'", mi.DeclaringType.Name, mi.Name);
                        continue;
                    }
                    // otherwise continue with delegate type and method building
                    xlmi.CreateDelegateTypeAndMethodInfo(moduleBuilder, wrapperTypeBuilder, mi, target);

                    // ... and add to list for further processing and registration
                    xlMethodInfos.Add(xlmi);
                }
                catch (DnaMarshalException e)
                {
                    Logger.Registration.Error(e, "Method not registered due to unsupported signature: '{0}.{1}'", mi.DeclaringType.Name, mi.Name);
                }
            }

            Type wrapperType = wrapperTypeBuilder.CreateType();

            foreach (XlMethodInfo xlmi in xlMethodInfos)
            {
                xlmi.CreateDelegateAndFunctionPointer(wrapperType);
            }

            //			assemblyBuilder.Save(@"ExcelDna.DynamicDelegateAssembly.dll");
            return(xlMethodInfos);
        }
Пример #6
0
        static void Register(List <MethodInfo> methods, List <object> targets, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            Debug.Assert(targets == null || targets.Count == methods.Count);
            Logger.Registration.Verbose("Registering {0} methods", methods.Count);
            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods, targets, methodAttributes, argumentAttributes);

            // Sort by name in reverse order before registering - this is inspired by the article http://www.benf.org/excel/regcost/
            // Makes a small but measurable difference in the Excel registration calls
            xlMethods.Sort(delegate(XlMethodInfo mi1, XlMethodInfo mi2) { return(-string.CompareOrdinal(mi1.Name.ToLower(), mi2.Name.ToLower())); });
            xlMethods.ForEach(RegisterXlMethod);
            // Increment the registration version (safe to call a few times)
            registrationInfoVersion += 1.0;
        }
Пример #7
0
        static Delegate GetLazyDelegate(XlMethodInfo methodInfo)
        {
            var lazyLambda = new XlDirectMarshalLazy(() => GetNativeDelegate(methodInfo));

            // now we need to return the right method from lazyLambda, to be sure it can be assigned to the intended delegate type
            if (methodInfo.HasReturnType)
            {
                var delegateType = XlDirectMarshalTypes.XlFuncs[methodInfo.Parameters.Length];
                var method       = typeof(XlDirectMarshalLazy).GetMethod($"Func{methodInfo.Parameters.Length}");
                return(Delegate.CreateDelegate(delegateType, lazyLambda, method));
            }
            else
            {
                var delegateType = XlDirectMarshalTypes.XlActs[methodInfo.Parameters.Length];
                var method       = typeof(XlDirectMarshalLazy).GetMethod($"Act{methodInfo.Parameters.Length}");
                return(Delegate.CreateDelegate(delegateType, lazyLambda, method));
            }
        }
Пример #8
0
        // This is the main conversion function called from XlLibrary.RegisterMethods
        // targets may be null - the typical case
        public static List <XlMethodInfo> ConvertToXlMethodInfos(List <MethodInfo> methods, List <object> targets, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            List <XlMethodInfo> xlMethodInfos = new List <XlMethodInfo>();

            // Set up assembly
            // Examine the methods, built the types and infos
            // Bake the assembly and export the function pointers

            AssemblyBuilder assemblyBuilder;
            ModuleBuilder   moduleBuilder;

            assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName("ExcelDna.DynamicDelegateAssembly"),
                AssemblyBuilderAccess.Run /*AndSave*/);
            moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDelegates");

            for (int i = 0; i < methods.Count; i++)
            {
                MethodInfo    mi           = methods[i];
                object        target       = (targets == null) ? null : targets[i];
                object        methodAttrib = (methodAttributes != null && i < methodAttributes.Count) ? methodAttributes[i] : null;
                List <object> argAttribs   = (argumentAttributes != null && i < argumentAttributes.Count) ? argumentAttributes[i] : null;
                try
                {
                    XlMethodInfo xlmi = new XlMethodInfo(moduleBuilder, mi, target, methodAttrib, argAttribs);
                    // Skip if suppressed
                    if (xlmi.ExplicitRegistration)
                    {
                        Debug.Print("ExcelDNA -> Suppressing registration for " + mi.Name);
                        continue;
                    }
                    // otherwise add
                    xlMethodInfos.Add(xlmi);
                }
                catch (DnaMarshalException e)
                {
                    // TODO: What to do here  (maybe logging)?
                    Debug.Print("ExcelDNA -> Inappropriate Method: " + mi.Name + " - " + e.Message);
                }
            }

            //			assemblyBuilder.Save(@"ExcelDna.DynamicDelegateAssembly.dll");
            return(xlMethodInfos);
        }
Пример #9
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

            XlAddIn.SetJump(index, mi.FunctionPointer);
            String exportedProcName = String.Format("f{0}", index);

            object[] registerParameters = GetRegisterParameters(mi, exportedProcName);
            if (!mi.IsCommand && !mi.IsHidden)
            {
                RecordFunctionInfo(registerParameters);
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Debug.Print("Register - XllPath={0}, ProcName={1}, FunctionType={2}, MethodName={3} - Result={4}",
                            registerParameters[0], registerParameters[1], registerParameters[2], registerParameters[3],
                            xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    // TODO: What to do here? LogDisplay??
                    Debug.Print("Registration Error! - Register call failed for method {0}", mi.Name);
                }
            }
            catch (Exception e)
            {
                // TODO: What to do here? LogDisplay??
                Debug.WriteLine("Registration Error! - " + e.Message);
            }
        }
Пример #10
0
        public static void RegisterDelegatesWithAttributes(List <Delegate> delegates, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            // I'm missing LINQ ...
            List <MethodInfo> methods = new List <MethodInfo>();
            List <object>     targets = new List <object>();

            for (int i = 0; i < delegates.Count; i++)
            {
                Delegate del = delegates[i];
                // Using del.Method and del.Target from here is a problem
                // - then we have to deal with the open/closed situation very carefully.
                // We'll pass and invoke the actual delegate, which means the method signature is correct.
                // Overhead should be negligible.
                methods.Add(del.GetType().GetMethod("Invoke"));
                targets.Add(del);
            }
            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods, targets, null, methodAttributes, argumentAttributes);

            RegisterXlMethods(xlMethods);
        }
Пример #11
0
        // This is the main conversion function called from XlLibrary.RegisterMethods
        // targets may be null - the typical case
        public static List<XlMethodInfo> ConvertToXlMethodInfos(List<MethodInfo> methods, List<object> targets, List<object> methodAttributes, List<List<object>> argumentAttributes)
        {
            List<XlMethodInfo> xlMethodInfos = new List<XlMethodInfo>();

            // Set up assembly
            // Examine the methods, built the types and infos
            // Bake the assembly and export the function pointers

            AssemblyBuilder assemblyBuilder;
            ModuleBuilder moduleBuilder;
            assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName("ExcelDna.DynamicDelegateAssembly"),
                AssemblyBuilderAccess.Run /*AndSave*/);
            moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDelegates");

            for (int i = 0; i < methods.Count; i++)
            {
                MethodInfo mi  = methods[i];
                object target = (targets == null) ? null : targets[i];
                object methodAttrib = (methodAttributes != null && i < methodAttributes.Count) ? methodAttributes[i] : null;
                List<object> argAttribs = (argumentAttributes != null && i < argumentAttributes.Count) ? argumentAttributes[i] : null;
                try
                {
                    XlMethodInfo xlmi = new XlMethodInfo(moduleBuilder, mi, target, methodAttrib, argAttribs);
                    // Skip if suppressed
                    if (xlmi.ExplicitRegistration)
                    {
                        Logger.Registration.Info("Suppressing due to ExplictRegistration attribute: '{0}.{1}'", mi.DeclaringType.Name, mi.Name);
                        continue;
                    }
                    // otherwise add
                    xlMethodInfos.Add(xlmi);
                }
                catch (DnaMarshalException e)
                {
                    Logger.Registration.Error(e, "Method not registered due to unsupported signature: '{0}.{1}'", mi.DeclaringType.Name, mi.Name);
                }
            }

            //			assemblyBuilder.Save(@"ExcelDna.DynamicDelegateAssembly.dll");
            return xlMethodInfos;
        }
Пример #12
0
 private static void RegisterMenu(XlMethodInfo mi)
 {
     if (mi.MenuName != null && mi.MenuName != ""
         && mi.MenuText != null && mi.MenuText != "")
     {
         IntegrationHelpers.AddCommandMenu(mi.Name, mi.MenuName, mi.MenuText, mi.Description, mi.ShortCut, mi.HelpTopic);
     }
 }
Пример #13
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;
            SetJump(index, mi.FunctionPointer);
            String procName = String.Format("f{0}", index);

            string functionType;
            if ( mi.IsCommand)
            {
                if (mi.Parameters.Length == 0)
                    functionType = "";  // OK since no other types will be added
                else
                    functionType = ">"; // Use the void / inplace indicator if needed.
            }
            else
                functionType = mi.ReturnType.XlType;

            string argumentNames = "";
            bool showDescriptions = false;
            string[] argumentDescriptions = new string[mi.Parameters.Length];
            string helpTopic;

            for (int j = 0; j < mi.Parameters.Length; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;
                if (j > 0)
                    argumentNames += ",";
                argumentNames += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                    showDescriptions = true;

                // DOCUMENT: Here is the patch for the Excel Function Description bug.
                // DOCUMENT: I add ". " to the last parameters.
                if (j == mi.Parameters.Length - 1)
                    argumentDescriptions[j] += ". ";

            } // for each parameter

            if (mi.IsClusterSafe && ProcessHelper.SupportsClusterSafe)
                functionType += "&"; 
            
            if (mi.IsMacroType)
                functionType += "#";

            if (!mi.IsMacroType && mi.IsThreadSafe && XlAddIn.xlCallVersion >= 12)
                functionType += "$";

            if (mi.IsVolatile)
                functionType += "!";
            // DOCUMENT: If # is set and there is an R argument, Excel considers the function volatile anyway.
            // You can call xlfVolatile, false in beginning of function to clear.

			// DOCUMENT: There is a bug? in Excel 2007 that limits the total argumentname string to 255 chars.
            // TODO: Check whether this is fixed in Excel 2010 yet.
			// DOCUMENT: I truncate the argument string for all versions.
			if (argumentNames.Length > 255)
				argumentNames = argumentNames.Substring(0, 255);

			// DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: I add ". " if the function takes no parameters and has a description.
            string functionDescription = mi.Description;
            if (mi.Parameters.Length == 0 && functionDescription != "")
                functionDescription += ". ";

			// DOCUMENT: When there is no description, we don't add any.
			// This allows the user to work around the Excel bug where an extra parameter is displayed if
			// the function has no parameter but displays a description
			if (mi.Description != "")
                showDescriptions = true;

			int numArguments;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.xlCallVersion < 12) ? 20 : 245;
            int numArgumentDescriptions;
            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(argumentDescriptions.Length, maxDescriptions);
                numArguments = 10 + numArgumentDescriptions;
            }
            else
            {
                numArgumentDescriptions = 0;
                numArguments = 9;
            }

            // Make HelpTopic without full path relative to xllPath
            if (string.IsNullOrEmpty(mi.HelpTopic))
            {
                helpTopic = mi.HelpTopic;
            }
            else
            {
                // DOCUMENT: If HelpTopic is not rooted - it is expanded relative to .xll path.
                if (Path.IsPathRooted(mi.HelpTopic))
                {
                    helpTopic = mi.HelpTopic;
                }
                else
                {
                    helpTopic = Path.Combine(Path.GetDirectoryName(pathXll), mi.HelpTopic);
                }
            }

            object[] registerParameters = new object[numArguments];
            registerParameters[0] = pathXll;
            registerParameters[1] = procName;
            registerParameters[2] = functionType;
            registerParameters[3] = mi.Name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2 /*macro*/
                                                          : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = mi.Category;
            registerParameters[7] = mi.ShortCut; /*shortcut_text*/
            registerParameters[8] = helpTopic; /*help_topic*/ ;

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Debug.Print("Register - XllPath={0}, ProcName={1}, FunctionType={2}, MethodName={3} - Result={4}", registerParameters[0], registerParameters[1], registerParameters[2], registerParameters[3], xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                    }
                }
                else
                {
                    // TODO: What to do here? LogDisplay??
                    Debug.Print("Registration Error! - Register call failed for method {0}", mi.Name);
                }
            }
            catch (Exception e)
            {
                // TODO: What to do here? LogDisplay??
                Debug.WriteLine("Registration Error! - " + e.Message);
            }
        }
Пример #14
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

            SetJump(index, mi.FunctionPointer);
            String procName = String.Format("f{0}", index);

            string functionType     = mi.ReturnType == null ? "" : mi.ReturnType.XlType.ToString();
            string argumentNames    = "";
            bool   showDescriptions = false;

            string[] argumentDescriptions = new string[mi.Parameters.Length];
            string   helpTopic;

            for (int j = 0; j < mi.Parameters.Length; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;
                if (j > 0)
                {
                    argumentNames += ",";
                }
                argumentNames          += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                {
                    showDescriptions = true;
                }

                // DOCUMENT: Here is the patch for the Excel Function Description bug.
                // DOCUMENT: I add ". " to the last parameters.
                if (j == mi.Parameters.Length - 1)
                {
                    argumentDescriptions[j] += ". ";
                }
            } // for each parameter

            if (mi.IsMacroType)
            {
                functionType += "#";
            }
            else if (mi.IsThreadSafe && XlAddIn.xlCallVersion >= 12)
            {
                functionType += "$";
            }

            if (mi.IsVolatile)
            {
                functionType += "!";
            }
            // DOCUMENT: If # is set and there is an R argument,
            // Excel considers the function volatile
            // You can call xlfVolatile, false in beginning of function to clear.

            // DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: I add ". " if the function takes no parameters and has a description.
            string functionDescription = mi.Description;

            if (mi.Parameters.Length == 0 && functionDescription != "")
            {
                functionDescription += ". ";
            }

            // DOCUMENT: When there is no description, we don't add any.
            // This allows the user to work around the Excel bug where an extra parameter is displayed if
            // the function has no parameter but displays a description
            if (mi.Description != "")
            {
                showDescriptions = true;
            }

            int numArguments;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.xlCallVersion < 12) ? 20 : 245;
            int numArgumentDescriptions;

            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(argumentDescriptions.Length, maxDescriptions);
                numArguments            = 10 + numArgumentDescriptions;
            }
            else
            {
                numArgumentDescriptions = 0;
                numArguments            = 9;
            }

            // Make HelpTopic without full path relative to xllPath
            if (mi.HelpTopic == null || mi.HelpTopic == "")
            {
                helpTopic = mi.HelpTopic;
            }
            else
            {
                if (Path.IsPathRooted(mi.HelpTopic))
                {
                    helpTopic = mi.HelpTopic;
                }
                else
                {
                    helpTopic = Path.Combine(Path.GetDirectoryName(pathXll), mi.HelpTopic);
                }
            }

            object[] registerParameters = new object[numArguments];
            registerParameters[0] = pathXll;
            registerParameters[1] = procName;
            registerParameters[2] = functionType;
            registerParameters[3] = mi.Name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2                               /*macro*/
                                                          : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = mi.Category;
            registerParameters[7] = mi.ShortCut;                                   /*shortcut_text*/
            registerParameters[8] = helpTopic; /*help_topic*/;

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                mi.RegisterId = (double)xlCallResult;
                registeredMethods.Add(mi);
            }
            catch (Exception e)
            {
                // TODO: What to do here?
                Debug.WriteLine(e.Message);
            }

            if (mi.IsCommand)
            {
                RegisterMenu(mi);
            }
        }
Пример #15
0
        public static void RegisterMethods(List <MethodInfo> methods)
        {
            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods);

            xlMethods.ForEach(RegisterXlMethod);
        }
Пример #16
0
 static string Truncate(string s, int length, string logDetail, XlMethodInfo mi)
 {
     if (s == null || s.Length <= length) return s;
     Logger.Registration.Warn("Truncated " + logDetail + " of function '{0}'", mi.Name);
     return s.Substring(0, length);
 }
Пример #17
0
        static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;
            XlAddIn.SetJump(index, mi.FunctionPointer);
            string exportedProcName = string.Format("f{0}", index);

            object[] registerParameters = GetRegisterParameters(mi, exportedProcName);

            if (registrationInfo.Exists(ri => ((string)ri[3]).Equals((string)registerParameters[3], StringComparison.OrdinalIgnoreCase)))
            {
                // This function will be registered with a name that has already been used (by this add-in)
                if (mi.SuppressOverwriteError)
                {
                    // Logged at Info level - to allow re-registration without error popup
                    Logger.Registration.Info("Repeated function name: '{0}' - previous registration will be overwritten. ", registerParameters[3]);
                }
                else
                {
                    // This logged as an error, but the registration continues - the last function with the name wins, for backward compatibility.
                    Logger.Registration.Error("Repeated function name: '{0}' - previous registration will be overwritten. ", registerParameters[3]);
                }
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Logger.Registration.Info("Register - XllPath={0}, ProcName={1}, FunctionType={2}, Name={3} - Result={4}",
                            registerParameters[0], registerParameters[1], registerParameters[2], registerParameters[3],
                            xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double) xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    Logger.Registration.Error("xlfRegister call failed for function or command: '{0}'", mi.Name);
                }
                // Now clear out the xll path and store the parameters to support RegistrationInfo access.
                registerParameters[0] = null;
                registrationInfo.Add(registerParameters);
            }
            catch (Exception e)
            {
                Logger.Registration.Error(e, "Registration failed for function or command: '{0}'", mi.Name);
            }
        }
Пример #18
0
        private static object[] GetRegisterParameters(XlMethodInfo mi, string exportedProcName)
        {
            string functionType;

            if (mi.ReturnType != null)
            {
                functionType = mi.ReturnType.XlType;
            }
            else
            {
                if (mi.Parameters.Length == 0)
                {
                    functionType = "";  // OK since no other types will be added
                }
                else
                {
                    // This case is also be used for native async functions
                    functionType = ">"; // Use the void / inplace indicator if needed.
                }
            }

            // TODO: The argument names and descriptions allow some undocumented ",..." form to support paramarray style functions.
            //       E.g. check the FuncSum function in Generic.c in the SDK.
            //       We should try some support for this...
            string argumentNames    = "";
            bool   showDescriptions = false;
            // For async functions, we need to leave off the last argument
            int numArgumentDescriptions = mi.IsExcelAsyncFunction ? mi.Parameters.Length - 1 : mi.Parameters.Length;

            string[] argumentDescriptions = new string[numArgumentDescriptions];

            for (int j = 0; j < numArgumentDescriptions; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;

                if (j > 0)
                {
                    argumentNames += ",";
                }
                argumentNames          += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                {
                    showDescriptions = true;
                }

                // DOCUMENT: Truncate the argument description if it exceeds the Excel limit of 255 characters
                if (j < mi.Parameters.Length - 1)
                {
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]) &&
                        argumentDescriptions[j].Length > 255)
                    {
                        argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 255);
                        Debug.Print("Truncated argument description of {0} in method {1} as Excel limit was exceeded",
                                    pi.Name, mi.Name);
                    }
                }
                else
                {
                    // Last argument - need to deal with extra ". "
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]))
                    {
                        if (argumentDescriptions[j].Length > 253)
                        {
                            argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 253);
                            Debug.Print("Truncated field description of {0} in method {1} as Excel limit was exceeded",
                                        pi.Name, mi.Name);
                        }

                        // DOCUMENT: Here is the patch for the Excel Function Description bug.
                        // DOCUMENT: I add ". " to the last parameter.
                        argumentDescriptions[j] += ". ";
                    }
                }
            } // for each parameter

            // Add async handle
            if (mi.IsExcelAsyncFunction)
            {
                functionType += "X"; // mi.Parameters[mi.Parameters.Length - 1].XlType should be "X" anyway
            }
            // Native async functions cannot be cluster safe
            if (mi.IsClusterSafe && ProcessHelper.SupportsClusterSafe && !mi.IsExcelAsyncFunction)
            {
                functionType += "&";
            }

            if (mi.IsMacroType)
            {
                functionType += "#";
            }

            if (!mi.IsMacroType && mi.IsThreadSafe && XlAddIn.XlCallVersion >= 12)
            {
                functionType += "$";
            }

            if (mi.IsVolatile)
            {
                functionType += "!";
            }
            // DOCUMENT: If # is set and there is an R argument, Excel considers the function volatile anyway.
            // You can call xlfVolatile, false in beginning of function to clear.

            string functionDescription = mi.Description;

            // DOCUMENT: Truncate Description to 253 characters (for all versions)
            functionDescription = Truncate(functionDescription, 253);

            // DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: I add ". " if the function takes no parameters and has a description.
            if (mi.Parameters.Length == 0 && functionDescription != "")
            {
                functionDescription += ". ";
            }

            // DOCUMENT: When there is no description, we don't add any.
            // This allows the user to work around the Excel bug where an extra parameter is displayed if
            // the function has no parameter but displays a description
            if (mi.Description != "")
            {
                showDescriptions = true;
            }

            int numRegisterParameters;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.XlCallVersion < 12) ? 20 : 245;

            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(numArgumentDescriptions, maxDescriptions);
                numRegisterParameters   = 10 + numArgumentDescriptions;  // function description + arg descriptions
            }
            else
            {
                // Won't be showing any descriptions.
                numArgumentDescriptions = 0;
                numRegisterParameters   = 9;
            }

            // DOCUMENT: Additional truncations of registration info - registration fails with strings longer than 255 chars.
            argumentNames = Truncate(argumentNames, 255);
            string category  = Truncate(mi.Category, 255);
            string name      = Truncate(mi.Name, 255);
            string helpTopic = (mi.HelpTopic == null || mi.HelpTopic.Length <= 255) ? mi.HelpTopic : "";

            object[] registerParameters = new object[numRegisterParameters];
            registerParameters[0] = XlAddIn.PathXll;
            registerParameters[1] = exportedProcName;
            registerParameters[2] = functionType;
            registerParameters[3] = name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2                      /*macro*/
                                                 : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = category;
            registerParameters[7] = mi.ShortCut;                          /*shortcut_text*/
            registerParameters[8] = helpTopic;                            /*help_topic*/

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            return(registerParameters);
        }
Пример #19
0
        private static object[] GetRegisterParameters(XlMethodInfo mi, string exportedProcName)
        {
            string functionType;

            if (mi.ReturnType != null)
            {
                functionType = mi.ReturnType.XlType;
            }
            else
            {
                if (mi.Parameters.Length == 0)
                {
                    functionType = "";  // OK since no other types will be added
                }
                else
                {
                    // This case is also be used for native async functions
                    functionType = ">"; // Use the void / inplace indicator if needed.
                }
            }

            string argumentNames    = "";
            bool   showDescriptions = false;
            // For async functions, we need to leave off the last argument
            int numArgumentDescriptions = mi.IsExcelAsyncFunction ? mi.Parameters.Length - 1 : mi.Parameters.Length;

            string[] argumentDescriptions = new string[numArgumentDescriptions];

            for (int j = 0; j < numArgumentDescriptions; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;

                if (j > 0)
                {
                    argumentNames += ",";   // TODO: Should this be a comma, or the Excel list separator?
                }
                argumentNames          += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                {
                    showDescriptions = true;
                }

                // DOCUMENT: Truncate the argument description if it exceeds the Excel limit of 255 characters
                if (j < mi.Parameters.Length - 1)
                {
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]) &&
                        argumentDescriptions[j].Length > 255)
                    {
                        argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 255);
                        Logger.Registration.Warn("Truncated argument description of '{0}' in function '{1}'", pi.Name, mi.Name);
                    }
                }
                else
                {
                    // Last argument - need to deal with extra ". "
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]))
                    {
                        if (argumentDescriptions[j].Length > 253)
                        {
                            argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 253);
                            Logger.Registration.Warn("Truncated final argument description of function '{0}'", mi.Name);
                        }

                        // DOCUMENT: Here is the patch for the Excel Function Description bug.
                        // DOCUMENT: Ensure the ". " suffix exists on the last parameter.
                        if (!argumentDescriptions[j].EndsWith(". "))
                        {
                            argumentDescriptions[j] += argumentDescriptions[j].EndsWith(".") ? " " : ". ";
                        }
                    }
                }
            } // for each parameter

            // Add async handle
            if (mi.IsExcelAsyncFunction)
            {
                functionType += "X"; // mi.Parameters[mi.Parameters.Length - 1].XlType should be "X" anyway
            }
            // Native async functions cannot be cluster safe
            if (mi.IsClusterSafe && ProcessHelper.SupportsClusterSafe && !mi.IsExcelAsyncFunction)
            {
                functionType += "&";
            }

            if (mi.IsMacroType)
            {
                functionType += "#";
            }

            if (!mi.IsMacroType && mi.IsThreadSafe && XlAddIn.XlCallVersion >= 12)
            {
                functionType += "$";
            }

            if (mi.IsVolatile)
            {
                functionType += "!";
            }
            // DOCUMENT: If # is set and there is an R argument, Excel considers the function volatile anyway.
            // You can call xlfVolatile, false in beginning of function to clear.

            // DOCUMENT: Truncate Description to 253 characters (for all versions)
            string functionDescription = Truncate(mi.Description, 253, "function description", mi);

            // DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: Ensure the ". " suffix exists if the function takes no parameters and has a description.
            if (mi.Parameters.Length == 0 && functionDescription != "" && !functionDescription.EndsWith(". "))
            {
                functionDescription += functionDescription.EndsWith(".") ? " " : ". ";
            }

            // DOCUMENT: When there is no description, we don't add any.
            // This allows the user to work around the Excel bug where an extra parameter is displayed if
            // the function has no parameter but displays a description
            if (mi.Description != "")
            {
                showDescriptions = true;
            }

            int numRegisterParameters;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.XlCallVersion < 12) ? 20 : 245;

            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(numArgumentDescriptions, maxDescriptions);
                numRegisterParameters   = 10 + numArgumentDescriptions;  // function description + arg descriptions
            }
            else
            {
                // Won't be showing any descriptions.
                numArgumentDescriptions = 0;
                numRegisterParameters   = 9;
            }

            // DOCUMENT: Additional truncations of registration info - registration fails with strings longer than 255 chars.
            argumentNames = Truncate(argumentNames, 255, "argument names", mi);
            argumentNames = argumentNames.TrimEnd(','); // Also trim trailing commas (for params case)
            string category  = Truncate(mi.Category, 255, "Category name", mi);
            string name      = Truncate(mi.Name, 255, "Name", mi);
            string helpTopic = string.Empty;

            if (mi.HelpTopic != null)
            {
                if (mi.HelpTopic.Length > 255)
                {
                    // Can't safely truncate the help link
                    Logger.Registration.Warn("Ignoring HelpTopic of function '{0}' - too long", mi.Name);
                }
                else
                {
                    // It's OK
                    helpTopic = mi.HelpTopic;
                }
            }

            object[] registerParameters = new object[numRegisterParameters];
            registerParameters[0] = XlAddIn.PathXll;
            registerParameters[1] = exportedProcName;
            registerParameters[2] = functionType;
            registerParameters[3] = name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2                      /*macro*/
                                                 : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = category;
            registerParameters[7] = mi.ShortCut;                          /*shortcut_text*/
            registerParameters[8] = helpTopic;                            /*help_topic*/

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            return(registerParameters);
        }
Пример #20
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

            SetJump(index, mi.FunctionPointer);
            String procName = String.Format("f{0}", index);

            string functionType;

            if (mi.IsCommand)
            {
                if (mi.Parameters.Length == 0)
                {
                    functionType = "";  // OK since no other types will be added
                }
                else
                {
                    functionType = ">"; // Use the void / inplace indicator if needed.
                }
            }
            else
            {
                functionType = mi.ReturnType.XlType;
            }

            string argumentNames    = "";
            bool   showDescriptions = false;

            string[] argumentDescriptions = new string[mi.Parameters.Length];
            string   helpTopic;

            for (int j = 0; j < mi.Parameters.Length; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;
                if (j > 0)
                {
                    argumentNames += ",";
                }
                argumentNames          += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                {
                    showDescriptions = true;
                }

                // DOCUMENT: Truncate the argument description if it exceeds the Excel limit of 255 characters
                if (j < mi.Parameters.Length - 1)
                {
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]) &&
                        argumentDescriptions[j].Length > 255)
                    {
                        argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 255);
                        Debug.Print("Truncated argument description of {0} in method {1} as Excel limit was exceeded",
                                    pi.Name, mi.Name);
                    }
                }
                else
                {
                    // Last argument - need to deal with extra ". "
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]))
                    {
                        if (argumentDescriptions[j].Length > 253)
                        {
                            argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 253);
                            Debug.Print("Truncated field description of {0} in method {1} as Excel limit was exceeded",
                                        pi.Name, mi.Name);
                        }

                        // DOCUMENT: Here is the patch for the Excel Function Description bug.
                        // DOCUMENT: I add ". " to the last parameter.
                        argumentDescriptions[j] += ". ";
                    }
                }
            } // for each parameter

            if (mi.IsClusterSafe && ProcessHelper.SupportsClusterSafe)
            {
                functionType += "&";
            }

            if (mi.IsMacroType)
            {
                functionType += "#";
            }

            if (!mi.IsMacroType && mi.IsThreadSafe && XlAddIn.XlCallVersion >= 12)
            {
                functionType += "$";
            }

            if (mi.IsVolatile)
            {
                functionType += "!";
            }
            // DOCUMENT: If # is set and there is an R argument, Excel considers the function volatile anyway.
            // You can call xlfVolatile, false in beginning of function to clear.

            // DOCUMENT: There is a bug? in Excel 2007 that limits the total argumentname string to 255 chars.
            // TODO: Check whether this is fixed in Excel 2010 yet.
            // DOCUMENT: I truncate the argument string for all versions.
            if (argumentNames.Length > 255)
            {
                argumentNames = argumentNames.Substring(0, 255);
            }

            // DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: I add ". " if the function takes no parameters and has a description.
            string functionDescription = mi.Description;

            if (mi.Parameters.Length == 0 && functionDescription != "")
            {
                functionDescription += ". ";
            }

            // DOCUMENT: When there is no description, we don't add any.
            // This allows the user to work around the Excel bug where an extra parameter is displayed if
            // the function has no parameter but displays a description
            if (mi.Description != "")
            {
                showDescriptions = true;
            }

            int numArguments;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.XlCallVersion < 12) ? 20 : 245;
            int numArgumentDescriptions;

            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(argumentDescriptions.Length, maxDescriptions);
                numArguments            = 10 + numArgumentDescriptions;
            }
            else
            {
                numArgumentDescriptions = 0;
                numArguments            = 9;
            }

            // Make HelpTopic without full path relative to xllPath
            if (string.IsNullOrEmpty(mi.HelpTopic))
            {
                helpTopic = mi.HelpTopic;
            }
            else
            {
                // DOCUMENT: If HelpTopic is not rooted - it is expanded relative to .xll path.
                // If http url does not end with !0 it is appended.
                // I don't think https is supported, but it should not be considered an 'unrooted' path anyway.
                if (mi.HelpTopic.StartsWith("http://") || mi.HelpTopic.StartsWith("https://"))
                {
                    if (!mi.HelpTopic.EndsWith("!0"))
                    {
                        helpTopic = mi.HelpTopic + "!0";
                    }
                    else
                    {
                        helpTopic = mi.HelpTopic;
                    }
                }
                else if (Path.IsPathRooted(mi.HelpTopic))
                {
                    helpTopic = mi.HelpTopic;
                }
                else
                {
                    helpTopic = Path.Combine(Path.GetDirectoryName(pathXll), mi.HelpTopic);
                }
            }

            object[] registerParameters = new object[numArguments];
            registerParameters[0] = pathXll;
            registerParameters[1] = procName;
            registerParameters[2] = functionType;
            registerParameters[3] = mi.Name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2                               /*macro*/
                                                          : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = mi.Category;
            registerParameters[7] = mi.ShortCut;                                   /*shortcut_text*/
            registerParameters[8] = helpTopic; /*help_topic*/;

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Debug.Print("Register - XllPath={0}, ProcName={1}, FunctionType={2}, MethodName={3} - Result={4}", registerParameters[0], registerParameters[1], registerParameters[2], registerParameters[3], xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    // TODO: What to do here? LogDisplay??
                    Debug.Print("Registration Error! - Register call failed for method {0}", mi.Name);
                }
            }
            catch (Exception e)
            {
                // TODO: What to do here? LogDisplay??
                Debug.WriteLine("Registration Error! - " + e.Message);
            }
        }
Пример #21
0
        // NOTE: This is called in parallel, from a ThreadPool thread
        static Delegate GetNativeDelegate(XlMethodInfo methodInfo)
        {
            // We convert
            //
            // double MyFancyFunc(object input1, string input2) { ... }
            //
            // To
            //
            // IntPtr MyFancyFuncWrapped(IntPtr<XlOper12> xlinput0, IntPtr<XlString12 xlinput1)
            // {
            //
            //    XlMarshalContext ctx = GetContextForThisThread();
            //    try
            //    {
            //
            //      input0 = Convert1(xlinput0);
            //      input1 = Convert2(xlinput1);
            //
            //      result = MyFancyFunc(input0, input1);
            //      xlresult = ctx.ConvertRet(ctx, result),
            //      return xlresult;
            //    }
            //    catch(Exception ex)
            //    {
            //       resultx = HandleEx(ex);
            //       xlresultx = ctx.ConvertRet(resultex);
            //    }
            //
            //    return resultx
            //
            //
            // }

            // Create the new parameters and return value for the wrapper
            // TODO/DM: Consolidate to a single select
            var outerParams     = methodInfo.Parameters.Select(p => Expression.Parameter(typeof(IntPtr), p.Name)).ToArray();
            var innerParamExprs = new Expression[outerParams.Length];

            for (int i = 0; i < methodInfo.Parameters.Length; i++)
            {
                var pi = methodInfo.Parameters[i];
                if (pi.XlMarshalConvert == null)
                {
                    innerParamExprs[i] = outerParams[i];
                }
                else
                {
                    innerParamExprs[i] = Expression.Call(pi.XlMarshalConvert, outerParams[i]);
                }
            }

            Expression innerCall;

            if (methodInfo.MethodInfo != null) // Method and optional Target
            {
                innerCall = Expression.Call(methodInfo.Target == null ? null : Expression.Constant(methodInfo.Target), methodInfo.MethodInfo, innerParamExprs);
            }
            else // LambdaExpression
            {
                innerCall = Expression.Invoke(methodInfo.LambdaExpression, innerParamExprs);
            }

            // variable to hold XlMarshalContext
            var ctx       = Expression.Variable(typeof(XlMarshalContext), "xlMarshalContext");
            var assignCtx = Expression.Assign(ctx, Expression.Call(typeof(XlDirectMarshal), nameof(XlDirectMarshal.GetMarshalContext), null));

            if (methodInfo.HasReturnType)
            {
                var result     = Expression.Variable(typeof(IntPtr), "returnValue");
                var resultExpr = result; // Overwrite with conversion if applicable
                if (methodInfo.ReturnType.XlMarshalConvert != null)
                {
                    innerCall = Expression.Call(ctx, methodInfo.ReturnType.XlMarshalConvert, innerCall);
                }
            }

            // Prepare the ex(ception) local variable
            var        ex = Expression.Variable(typeof(Exception), "ex");
            Expression catchExpression;

            if (methodInfo.IsExcelAsyncFunction)
            {
                // HandleUnhandledException is called by ExcelAsyncHandle.SetException
                var asyncHandle        = Expression.TypeAs(innerParamExprs.Last(), typeof(ExcelAsyncHandleNative));
                var setExceptionMethod = typeof(ExcelAsyncHandleNative).GetMethod(nameof(ExcelAsyncHandleNative.SetException));
                // Need to get instance from parameter list
                catchExpression = Expression.Block(
                    Expression.Call(asyncHandle, setExceptionMethod, ex),                     // Ignore bool return !?
                    Expression.Empty());
            }
            else
            {
                var handlerMethod = typeof(ExcelIntegration).GetMethod(nameof(ExcelIntegration.HandleUnhandledException), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                var exHandler     = Expression.Call(handlerMethod, ex);
                if (methodInfo.HasReturnType)
                {
                    if (methodInfo.ReturnType.XlType == XlTypes.Xloper)
                    {
                        // We return whatever the result is from the unhandled exception handler
                        catchExpression = Expression.Call(ctx, XlMarshalConversions.ObjectReturn, exHandler);
                    }
                    else
                    {
                        // We return #NUM!, which is better than crashing
                        catchExpression = Expression.Block(exHandler, Expression.Constant(IntPtr.Zero));
                    }
                }
                else
                {
                    catchExpression = Expression.Block(exHandler, Expression.Empty());
                }
            }

            Type       delegateType;
            Expression body;

            if (methodInfo.HasReturnType)
            {
                delegateType = XlDirectMarshalTypes.XlFuncs[methodInfo.Parameters.Length];
                body         = Expression.Block(
                    typeof(IntPtr),
                    new ParameterExpression[] { ctx },
                    assignCtx,
                    Expression.TryCatch(
                        innerCall,
                        Expression.Catch(ex, catchExpression)));
            }
            else
            {
                delegateType = XlDirectMarshalTypes.XlActs[methodInfo.Parameters.Length];
                if (methodInfo.IsExcelAsyncFunction)
                {
                    body = Expression.Block(
                        Expression.TryCatch(
                            innerCall,
                            Expression.Catch(ex, catchExpression)));
                }
                else
                {
                    body = Expression.TryCatch(
                        innerCall,
                        Expression.Catch(ex, catchExpression));
                }
            }

            return(Expression.Lambda(delegateType, body, methodInfo.Name, outerParams).Compile());
        }
Пример #22
0
        private static object[] GetRegisterParameters(XlMethodInfo mi, string exportedProcName)
        {
            string functionType;
            if (mi.ReturnType != null)
            {
                functionType = mi.ReturnType.XlType;
            }
            else
            {
                if (mi.Parameters.Length == 0)
                {
                    functionType = "";  // OK since no other types will be added
                }
                else
                {
                    // This case is also be used for native async functions
                    functionType = ">"; // Use the void / inplace indicator if needed.
                }
            }

            string argumentNames = "";
            bool showDescriptions = false;
            // For async functions, we need to leave off the last argument
            int numArgumentDescriptions = mi.IsExcelAsyncFunction ? mi.Parameters.Length - 1 : mi.Parameters.Length;
            string[] argumentDescriptions = new string[numArgumentDescriptions];

            for (int j = 0; j < numArgumentDescriptions; j++)
            {
                XlParameterInfo pi = mi.Parameters[j];

                functionType += pi.XlType;

                if (j > 0)
                    argumentNames += ",";   // TODO: Should this be a comma, or the Excel list separator?
                argumentNames += pi.Name;
                argumentDescriptions[j] = pi.Description;

                if (pi.Description != "")
                    showDescriptions = true;

                // DOCUMENT: Truncate the argument description if it exceeds the Excel limit of 255 characters
                if (j < mi.Parameters.Length - 1)
                {
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]) &&
                        argumentDescriptions[j].Length > 255)
                    {
                        argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 255);
                        Logger.Registration.Warn("Truncated argument description of '{0}' in function '{1}'", pi.Name, mi.Name);
                    }
                }
                else
                {
                    // Last argument - need to deal with extra ". "
                    if (!string.IsNullOrEmpty(argumentDescriptions[j]))
                    {
                        if (argumentDescriptions[j].Length > 253)
                        {
                            argumentDescriptions[j] = argumentDescriptions[j].Substring(0, 253);
                            Logger.Registration.Warn("Truncated final argument description of function '{0}'", mi.Name);
                        }

                        // DOCUMENT: Here is the patch for the Excel Function Description bug.
                        // DOCUMENT: Ensure the ". " suffix exists on the last parameter.
                        if (!argumentDescriptions[j].EndsWith(". "))
                            argumentDescriptions[j] += argumentDescriptions[j].EndsWith(".") ? " " : ". ";
                    }
                }
            } // for each parameter

            // Add async handle
            if (mi.IsExcelAsyncFunction)
                functionType += "X"; // mi.Parameters[mi.Parameters.Length - 1].XlType should be "X" anyway

            // Native async functions cannot be cluster safe
            if (mi.IsClusterSafe && ProcessHelper.SupportsClusterSafe && !mi.IsExcelAsyncFunction)
                functionType += "&";

            if (mi.IsMacroType)
                functionType += "#";

            if (!mi.IsMacroType && mi.IsThreadSafe && XlAddIn.XlCallVersion >= 12)
                functionType += "$";

            if (mi.IsVolatile)
                functionType += "!";
            // DOCUMENT: If # is set and there is an R argument, Excel considers the function volatile anyway.
            // You can call xlfVolatile, false in beginning of function to clear.

            // DOCUMENT: Truncate Description to 253 characters (for all versions)
            string functionDescription = Truncate(mi.Description, 253, "function description", mi);

            // DOCUMENT: Here is the patch for the Excel Function Description bug.
            // DOCUMENT: Ensure the ". " suffix exists if the function takes no parameters and has a description.
            if (mi.Parameters.Length == 0 && functionDescription != "" && !functionDescription.EndsWith(". "))
                functionDescription += functionDescription.EndsWith(".") ? " " : ". ";

            // DOCUMENT: When there is no description, we don't add any.
            // This allows the user to work around the Excel bug where an extra parameter is displayed if
            // the function has no parameter but displays a description
            if (mi.Description != "")
                showDescriptions = true;

            int numRegisterParameters;
            // DOCUMENT: Maximum 20 Argument Descriptions when registering using Excel4 function.
            int maxDescriptions = (XlAddIn.XlCallVersion < 12) ? 20 : 245;
            if (showDescriptions)
            {
                numArgumentDescriptions = Math.Min(numArgumentDescriptions, maxDescriptions);
                numRegisterParameters = 10 + numArgumentDescriptions;    // function description + arg descriptions
            }
            else
            {
                // Won't be showing any descriptions.
                numArgumentDescriptions = 0;
                numRegisterParameters = 9;
            }

            // DOCUMENT: Additional truncations of registration info - registration fails with strings longer than 255 chars.
            argumentNames = Truncate(argumentNames, 255, "argument names", mi);
            argumentNames = argumentNames.TrimEnd(','); // Also trim trailing commas (for params case)
            string category = Truncate(mi.Category, 255, "Category name", mi);
            string name = Truncate(mi.Name, 255, "Name", mi);
            string helpTopic = string.Empty;
            if (mi.HelpTopic != null)
            {
                if (mi.HelpTopic.Length > 255)
                {
                    // Can't safely truncate the help link
                    Logger.Registration.Warn("Ignoring HelpTopic of function '{0}' - too long", mi.Name);
                }
                else
                {
                    // It's OK
                    helpTopic = mi.HelpTopic;
                }
            }

            object[] registerParameters = new object[numRegisterParameters];
            registerParameters[0] = XlAddIn.PathXll;
            registerParameters[1] = exportedProcName;
            registerParameters[2] = functionType;
            registerParameters[3] = name;
            registerParameters[4] = argumentNames;
            registerParameters[5] = mi.IsCommand ? 2 /*macro*/
                                                 : (mi.IsHidden ? 0 : 1); /*function*/
            registerParameters[6] = category;
            registerParameters[7] = mi.ShortCut; /*shortcut_text*/
            registerParameters[8] = helpTopic; /*help_topic*/

            if (showDescriptions)
            {
                registerParameters[9] = functionDescription;

                for (int k = 0; k < numArgumentDescriptions; k++)
                {
                    registerParameters[10 + k] = argumentDescriptions[k];
                }
            }

            return registerParameters;
        }
Пример #23
0
 static void RegisterMenu(XlMethodInfo mi)
 {
     if (!string.IsNullOrEmpty(mi.MenuName) &&
         !string.IsNullOrEmpty(mi.MenuText))
     {
         IntegrationHelpers.AddCommandMenu(mi.Name, mi.MenuName, mi.MenuText, mi.Description, mi.ShortCut, mi.HelpTopic);
     }
 }
Пример #24
0
        public static void RegisterMethodsWithAttributes(List <MethodInfo> methods, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods, null, null, methodAttributes, argumentAttributes);

            RegisterXlMethods(xlMethods);
        }
Пример #25
0
 static void RegisterShortCut(XlMethodInfo mi)
 {
     if (!string.IsNullOrEmpty(mi.ShortCut))
     {
         object xlCallResult;
         XlCallImpl.TryExcelImpl(XlCallImpl.xlcOnKey, out xlCallResult, mi.ShortCut, mi.Name);
         // CONSIDER: We ignore result and suppress errors - maybe log?
         addedShortCuts.Add(mi.ShortCut);
     }
 }
Пример #26
0
        public static void RegisterLambdaExpressionsWithAttributes(List <LambdaExpression> lambdaExpressions, List <object> methodAttributes, List <List <object> > argumentAttributes)
        {
            List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(null, null, lambdaExpressions, methodAttributes, argumentAttributes);

            RegisterXlMethods(xlMethods);
        }
Пример #27
0
        // This is the main conversion function called from XlLibrary.RegisterMethods
        public static List<XlMethodInfo> ConvertToXlMethodInfos(List<MethodInfo> methodInfos)
        {
            List<XlMethodInfo> xlMethodInfos = new List<XlMethodInfo>();

            // Set up assembly
            // Examine the methods, built the types and infos
            // Bake the assembly and export the function pointers

            AssemblyBuilder assemblyBuilder;
            ModuleBuilder moduleBuilder;
            assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                new AssemblyName("ExcelDna.DynamicDelegateAssembly"),
                AssemblyBuilderAccess.Run /*AndSave*/);
            moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDelegates");

            foreach (MethodInfo mi  in methodInfos)
            {
                try
                {
                    XlMethodInfo xlmi = new XlMethodInfo(mi, moduleBuilder);
                    // Add if no Exceptions
                    xlMethodInfos.Add(xlmi);
                }
                catch (DnaMarshalException e)
                {
                    // TODO: What to do here  (maybe logging)?
                    Debug.Print("ExcelDNA -> Inappropriate Method: " + mi.Name + " - " + e.Message);
                }
            }

            //			assemblyBuilder.Save(@"ExcelDna.DynamicDelegateAssembly.dll");
            return xlMethodInfos;
        }