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); } }
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)); }
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; }
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); } }
// 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); }
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; }
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)); } }
// 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); }
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); } }
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); }
// 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; }
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); } }
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); } }
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); } }
public static void RegisterMethods(List <MethodInfo> methods) { List <XlMethodInfo> xlMethods = XlMethodInfo.ConvertToXlMethodInfos(methods); xlMethods.ForEach(RegisterXlMethod); }
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); }
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); } }
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); }
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); }
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); } }
// 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()); }
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; }
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); } }
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); }
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); }
// 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; }