Example #1
0
        // 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);
        }
Example #2
0
        // 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();
        }
Example #3
0
        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);
        }
Example #4
0
        static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

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

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

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

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Logger.Registration.Info("Register - XllPath={0}, ProcName={1}, FunctionType={2}, Name={3} - Result={4}",
                                         registerParameters[0], registerParameters[1], registerParameters[2], registerName,
                                         xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    Logger.Registration.Error("xlfRegister call failed for function or command: '{0}'", mi.Name);
                }
                // Now clear out the xll path and store the parameters to support RegistrationInfo access.
                registerParameters[0] = null;
                registrationInfo.Add(registerParameters);
            }
            catch (Exception e)
            {
                Logger.Registration.Error(e, "Registration failed for function or command: '{0}'", mi.Name);
            }
        }
Example #5
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);
     }
 }
Example #6
0
 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);
     }
 }
Example #7
0
        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);
        }
Example #8
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

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

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

            // Basically suppress problems here !?
            try
            {
                object xlCallResult;
                XlCallImpl.TryExcelImpl(XlCallImpl.xlfRegister, out xlCallResult, registerParameters);
                Debug.Print("Register - XllPath={0}, ProcName={1}, FunctionType={2}, MethodName={3} - Result={4}",
                            registerParameters[0], registerParameters[1], registerParameters[2], registerParameters[3],
                            xlCallResult);
                if (xlCallResult is double)
                {
                    mi.RegisterId = (double)xlCallResult;
                    registeredMethods.Add(mi);
                    if (mi.IsCommand)
                    {
                        RegisterMenu(mi);
                        RegisterShortCut(mi);
                    }
                }
                else
                {
                    // TODO: What to do here? LogDisplay??
                    Debug.Print("Registration Error! - Register call failed for method {0}", mi.Name);
                }
            }
            catch (Exception e)
            {
                // TODO: What to do here? LogDisplay??
                Debug.WriteLine("Registration Error! - " + e.Message);
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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();
        }
Example #11
0
        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);
        }
Example #12
0
        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);
        }
Example #13
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

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

            string functionType     = 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);
            }
        }
Example #14
0
        private static void RegisterXlMethod(XlMethodInfo mi)
        {
            int index = registeredMethods.Count;

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

            string functionType;

            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);
            }
        }
Example #15
0
 public XlCall.XlReturn TryExcelImpl(int xlFunction, out object result, params object[] parameters)
 => XlCallImpl.TryExcelImpl(xlFunction, out result, parameters);