// NOTE: We are not currently removing the functions from the Jmp array // That would be needed to do a proper per-method deregistration, // together with a garbage-collectable story for the wrapper methods and delegates, // instead of the currently runtime-compiled and loaded assemblies. internal static void UnregisterMethods() { object xlCallResult; // Remove menus and ShortCuts IntegrationHelpers.RemoveCommandMenus(); UnregisterShortCuts(); // Now take out the methods foreach (XlMethodInfo mi in registeredMethods) { if (mi.IsCommand) { // Clear the name and unregister XlCallImpl.TryExcelImpl(XlCallImpl.xlfSetName, out xlCallResult, mi.Name); XlCallImpl.TryExcelImpl(XlCallImpl.xlfUnregister, out xlCallResult, mi.RegisterId); } else { // And Unregister the real function XlCallImpl.TryExcelImpl(XlCallImpl.xlfUnregister, out xlCallResult, mi.RegisterId); // I follow the advice from X-Cell website to get function out of Wizard (with fix from kh) XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, XlAddIn.PathXll, "xlAutoRemove", "I", mi.Name, IntegrationMarshalHelpers.GetExcelMissingValue(), 2); if (xlCallResult is double) { double fakeRegisterId = (double)xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlfSetName, out xlCallResult, mi.Name); XlCallImpl.TryExcelImpl(XlCallImpl.xlfUnregister, out xlCallResult, fakeRegisterId); } } } registeredMethods.Clear(); registrationInfo.Clear(); }
internal static short XlAutoOpen() { Debug.Print("AppDomain Id: " + AppDomain.CurrentDomain.Id + " (Default: " + AppDomain.CurrentDomain.IsDefaultAppDomain() + ")"); short result = 0; try { Debug.WriteLine("In XlAddIn.XlAutoOpen"); DeInitializeIntegration(); object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignore*/, true, "Registering library " + pathXll); InitializeIntegration(); // InitializeIntegration has loaded the DnaLibrary IntegrationHelpers.DnaLibraryAutoOpen(); result = 1; // All is OK } catch (Exception e) { // TODO: What to do here? Debug.WriteLine(e.Message); result = 0; } finally { // Clear the status bar message object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignore*/, false); } return(result); }
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 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); } }
private static void UnregisterShortCuts() { foreach (string shortCut in addedShortCuts) { // xlcOnKey with no macro name: // "If macro_text is omitted, key_text reverts to its normal meaning in Microsoft Excel, // and any special key assignments made with previous ON.KEY functions are cleared." object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcOnKey, out xlCallResult, shortCut); } }
internal static short XlAutoOpen() { Debug.Print("AppDomain Id: " + AppDomain.CurrentDomain.Id + " (Default: " + AppDomain.CurrentDomain.IsDefaultAppDomain() + ")"); short result = 0; try { Debug.WriteLine("In XlAddIn.XlAutoOpen"); if (_opened) { DeInitializeIntegration(); } object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignore*/, true, "Registering library " + pathXll); InitializeIntegration(); // v. 30 - moved the setting of _opened before calling AutoOpen, // so that checking in DeInitializeIntegration does not prevent AutoOpen - unloading via xlAutoRemove from working. _opened = true; // InitializeIntegration has loaded the DnaLibrary IntegrationHelpers.DnaLibraryAutoOpen(); result = 1; // All is OK } catch (Exception e) { // TODO: What to do here - maybe prefer Trace...? // START HERE: Better error display (with Exception info?) Debug.WriteLine("ExcelDna.Loader.XlAddin.XlAutoOpen. Exception during Integration load: " + e.ToString()); string alertMessage = string.Format("A problem occurred while an add-in was being initialized (InitializeIntegration failed - {1}).\r\nThe add-in is built with ExcelDna and is being loaded from {0}", pathXll, e.Message); object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcAlert, out xlCallResult /*Ignored*/, alertMessage, 3 /* Only OK Button, Warning Icon*/); result = 0; } finally { // Clear the status bar message object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignored*/, false); Debug.Print("Clear status bar message result: " + xlCallResult); } return(result); }
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); } }
internal static short XlAutoOpen() { Debug.Print("XlAddIn.XlAutoOpen - AppDomain Id: " + AppDomain.CurrentDomain.Id + " (Default: " + AppDomain.CurrentDomain.IsDefaultAppDomain() + ")"); short result = 0; try { if (_opened) { DeInitializeIntegration(); } object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignore*/, true, "Registering library " + pathXll); InitializeIntegration(); Logger.Initialization.Verbose("In XlAddIn.XlAutoOpen"); // v. 30 - moved the setting of _opened before calling AutoOpen, // so that checking in DeInitializeIntegration does not prevent AutoOpen - unloading via xlAutoRemove from working. _opened = true; // InitializeIntegration has loaded the DnaLibrary ExcelIntegration.DnaLibraryAutoOpen(); result = 1; // All is OK } catch (Exception e) { // Can't use logging here string alertMessage = string.Format("A problem occurred while an add-in was being initialized (InitializeIntegration failed - {1}).\r\nThe add-in is built with ExcelDna and is being loaded from {0}", pathXll, e.Message); object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcAlert, out xlCallResult /*Ignored*/, alertMessage, 3 /* Only OK Button, Warning Icon*/); result = 0; } finally { // Clear the status bar message object xlCallResult; XlCallImpl.TryExcelImpl(XlCallImpl.xlcMessage, out xlCallResult /*Ignored*/, false); // Debug.Print("Clear status bar message result: " + xlCallResult); } return(result); }
static void UnregisterMethods() { object xlCallResult; // Remove menus and ShortCuts IntegrationHelpers.RemoveCommandMenus(); UnregisterShortCuts(); // Now take out the methods foreach (XlMethodInfo mi in registeredMethods) { if (!mi.IsCommand) { // I follow the advice from X-Cell website // to get function out of Wizard XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, pathXll, "xlAutoRemove", "J", mi.Name, IntegrationMarshalHelpers.GetExcelMissingValue(), 0); } XlCallImpl.TryExcelImpl(XlCallImpl.xlfSetName, out xlCallResult, mi.Name); XlCallImpl.TryExcelImpl(XlCallImpl.xlfUnregister, out xlCallResult, mi.RegisterId); } registeredMethods.Clear(); }
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); } }
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); } }
public XlCall.XlReturn TryExcelImpl(int xlFunction, out object result, params object[] parameters) => XlCallImpl.TryExcelImpl(xlFunction, out result, parameters);