// This is the function we register with Excel // All the IntPtrs are XLOPER12* public unsafe XlOper12 *RtdWrapper(XlOper12 *topic1, XlOper12 *topic2, XlOper12 *topic3, XlOper12 *topic4, XlOper12 *topic5, XlOper12 *topic6, XlOper12 *topic7, XlOper12 *topic8) { var callParamIndex = 2; _callParams[2] = topic1; _callParams[3] = topic2; _callParams[4] = topic3; _callParams[5] = topic4; _callParams[6] = topic5; _callParams[7] = topic6; _callParams[8] = topic7; _callParams[9] = topic8; callParamIndex = 9; int countParams = LastNonMissingIndex(_callParams, callParamIndex) + 1; try { int xlReturn = XlCallImpl.Excel12v(XlCallImpl.xlfRtd, countParams, _callParams, _resultXloper12); if (xlReturn == 0) // xlReturnSuccess) { _resultXloper12->xlType |= XlType12.XlBitXLFree; return(_resultXloper12); } } catch (Exception e) { Logger.Initialization.Error(e, "RTD call from wrapper failed with Exception"); } return(_errorValueXloper12); }
// 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 unsafe bool Initialize(IntPtr xlAddInExportInfoAddress, IntPtr hModuleXll, string pathXll) { XlAddIn.hModuleXll = hModuleXll; XlAddIn.pathXll = pathXll; // NOTE: Too early for logging - the TraceSource in ExcelDna.Integration has not been initialized yet. Debug.Print("In sandbox AppDomain with Id: {0}, running on thread: {1}", AppDomain.CurrentDomain.Id, Thread.CurrentThread.ManagedThreadId); Debug.Assert(xlAddInExportInfoAddress != IntPtr.Zero, "InitializationInfo address is null"); Debug.Print("InitializationInfo address: 0x{0:x8}", xlAddInExportInfoAddress); XlAddInExportInfo *pXlAddInExportInfo = (XlAddInExportInfo *)xlAddInExportInfoAddress; int exportInfoVersion = pXlAddInExportInfo->ExportInfoVersion; if (exportInfoVersion != 8) { Debug.Print("ExportInfoVersion '{0}' not supported", exportInfoVersion); return(false); } fn_short_void fnXlAutoOpen = (fn_short_void)XlAutoOpen; GCHandle.Alloc(fnXlAutoOpen); pXlAddInExportInfo->pXlAutoOpen = Marshal.GetFunctionPointerForDelegate(fnXlAutoOpen); fn_short_void fnXlAutoClose = (fn_short_void)XlAutoClose; GCHandle.Alloc(fnXlAutoClose); pXlAddInExportInfo->pXlAutoClose = Marshal.GetFunctionPointerForDelegate(fnXlAutoClose); fn_short_void fnXlAutoRemove = (fn_short_void)XlAutoRemove; GCHandle.Alloc(fnXlAutoRemove); pXlAddInExportInfo->pXlAutoRemove = Marshal.GetFunctionPointerForDelegate(fnXlAutoRemove); fn_void_intptr fnXlAutoFree12 = (fn_void_intptr)XlAutoFree12; GCHandle.Alloc(fnXlAutoFree12); pXlAddInExportInfo->pXlAutoFree12 = Marshal.GetFunctionPointerForDelegate(fnXlAutoFree12); fn_void_intptr fnSetExcel12EntryPt = (fn_void_intptr)XlCallImpl.SetExcel12EntryPt; GCHandle.Alloc(fnSetExcel12EntryPt); pXlAddInExportInfo->pSetExcel12EntryPt = Marshal.GetFunctionPointerForDelegate(fnSetExcel12EntryPt); fn_hresult_void fnDllRegisterServer = (fn_hresult_void)DllRegisterServer; GCHandle.Alloc(fnDllRegisterServer); pXlAddInExportInfo->pDllRegisterServer = Marshal.GetFunctionPointerForDelegate(fnDllRegisterServer); fn_hresult_void fnDllUnregisterServer = (fn_hresult_void)DllUnregisterServer; GCHandle.Alloc(fnDllUnregisterServer); pXlAddInExportInfo->pDllUnregisterServer = Marshal.GetFunctionPointerForDelegate(fnDllUnregisterServer); fn_get_class_object fnDllGetClassObject = (fn_get_class_object)DllGetClassObject; GCHandle.Alloc(fnDllGetClassObject); pXlAddInExportInfo->pDllGetClassObject = Marshal.GetFunctionPointerForDelegate(fnDllGetClassObject); fn_hresult_void fnDllCanUnloadNow = (fn_hresult_void)DllCanUnloadNow; GCHandle.Alloc(fnDllCanUnloadNow); pXlAddInExportInfo->pDllCanUnloadNow = Marshal.GetFunctionPointerForDelegate(fnDllCanUnloadNow); fn_void_double fnSyncMacro = (fn_void_double)SyncMacro; GCHandle.Alloc(fnSyncMacro); pXlAddInExportInfo->pSyncMacro = Marshal.GetFunctionPointerForDelegate(fnSyncMacro); fn_intptr_intptr fnRegistrationInfo = (fn_intptr_intptr)RegistrationInfo; GCHandle.Alloc(fnRegistrationInfo); pXlAddInExportInfo->pRegistrationInfo = Marshal.GetFunctionPointerForDelegate(fnRegistrationInfo); fn_void_void fnCalculationCanceled = (fn_void_void)CalculationCanceled; GCHandle.Alloc(fnCalculationCanceled); pXlAddInExportInfo->pCalculationCanceled = Marshal.GetFunctionPointerForDelegate(fnCalculationCanceled); fn_void_void fnCalculationEnded = (fn_void_void)CalculationEnded; GCHandle.Alloc(fnCalculationEnded); pXlAddInExportInfo->pCalculationEnded = Marshal.GetFunctionPointerForDelegate(fnCalculationEnded); // Thunk table for registered functions thunkTableLength = pXlAddInExportInfo->ThunkTableLength; thunkTable = pXlAddInExportInfo->ThunkTable; // This is the place where we call into Excel - this causes SecurityPermission exception // when run from VSTO. I don't know how to deal with this problem yet. // TODO: Learn more about the special security stuff in VSTO. // See ExecutionContext.SuppressFlow links below. try { XlAddIn.xlCallVersion = XlCallImpl.XLCallVer() / 256; } catch (DllNotFoundException) { // This is expected if we are running under HPC or Regsvr32. Debug.Print("XlCall library not found - probably running in HPC host or Regsvr32.exe"); // For the HPC support, I ignore error here and just assume we are under new Excel. // This will cause the common error here to get pushed to later ... XlAddIn.xlCallVersion = 12; // return false; } catch (Exception e) { Debug.Print("XlAddIn: XLCallVer Error: {0}", e); // CONSIDER: Is this right / needed - I'm not actually sure what happens under HPC host, // so I'll leave this case in here too.? XlAddIn.xlCallVersion = 12; // return false; } try { IntegrationLoader.LoadIntegration(); } catch (InvalidOperationException ioe) { Debug.Print("XlAddIn: Initialize Error - Invalid ExcelIntegration version detected: {0}", ioe); return(false); } catch (Exception e) { Debug.Print("XlAddIn: Initialize Error:", e); return(false); } // File.AppendAllText(Path.ChangeExtension(pathXll, ".log"), string.Format("{0:u} XlAddIn.Initialize OK\r\n", DateTime.Now)); return(true); }
public static unsafe bool Initialize(int xlAddInExportInfoAddress, int hModuleXll, string pathXll) { Debug.Assert(xlAddInExportInfoAddress != 0); Debug.Print("InitializationInfo Address: 0x{0:x8}", xlAddInExportInfoAddress); XlAddInExportInfo *pXlAddInExportInfo = (XlAddInExportInfo *)xlAddInExportInfoAddress; if (pXlAddInExportInfo->ExportInfoVersion != 1) { Debug.Print("ExportInfoVersion not supported."); return(false); } fn_short_void fnXlAutoOpen = (fn_short_void)XlAutoOpen; GCHandle.Alloc(fnXlAutoOpen); pXlAddInExportInfo->pXlAutoOpen = Marshal.GetFunctionPointerForDelegate(fnXlAutoOpen); fn_short_void fnXlAutoClose = (fn_short_void)XlAutoClose; GCHandle.Alloc(fnXlAutoClose); pXlAddInExportInfo->pXlAutoClose = Marshal.GetFunctionPointerForDelegate(fnXlAutoClose); fn_short_void fnXlAutoAdd = (fn_short_void)XlAutoAdd; GCHandle.Alloc(fnXlAutoAdd); pXlAddInExportInfo->pXlAutoAdd = Marshal.GetFunctionPointerForDelegate(fnXlAutoAdd); fn_short_void fnXlAutoRemove = (fn_short_void)XlAutoRemove; GCHandle.Alloc(fnXlAutoRemove); pXlAddInExportInfo->pXlAutoRemove = Marshal.GetFunctionPointerForDelegate(fnXlAutoRemove); fn_void_intptr fnXlAutoFree = (fn_void_intptr)XlAutoFree; GCHandle.Alloc(fnXlAutoFree); pXlAddInExportInfo->pXlAutoFree = Marshal.GetFunctionPointerForDelegate(fnXlAutoFree); fn_void_intptr fnXlAutoFree12 = (fn_void_intptr)XlAutoFree12; GCHandle.Alloc(fnXlAutoFree12); pXlAddInExportInfo->pXlAutoFree12 = Marshal.GetFunctionPointerForDelegate(fnXlAutoFree12); fn_intptr_intptr fnXlAddInManagerInfo = (fn_intptr_intptr)XlAddInManagerInfo; GCHandle.Alloc(fnXlAddInManagerInfo); pXlAddInExportInfo->pXlAddInManagerInfo = Marshal.GetFunctionPointerForDelegate(fnXlAddInManagerInfo); fn_intptr_intptr fnXlAddInManagerInfo12 = (fn_intptr_intptr)XlAddInManagerInfo12; GCHandle.Alloc(fnXlAddInManagerInfo12); pXlAddInExportInfo->pXlAddInManagerInfo12 = Marshal.GetFunctionPointerForDelegate(fnXlAddInManagerInfo12); thunkTableLength = pXlAddInExportInfo->ThunkTableLength; thunkTable = pXlAddInExportInfo->ThunkTable; XlAddIn.xlCallVersion = XlCallImpl.XLCallVer() / 256; XlAddIn.hModuleXll = (IntPtr)hModuleXll; XlAddIn.pathXll = pathXll; AssemblyManager.Initialize((IntPtr)hModuleXll, pathXll); bool result = false; try { LoadIntegration(); result = true; } catch (Exception e) { Debug.WriteLine("XlAddIn: Initialize Exception: " + e); } return(result); }
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);