コード例 #1
0
        public bool TryGetValue(out object value)
        {
            // We need to be careful when this is called from an array formula.
            // In the 'completed' case we actually still have to call xlfRtd, then only skip if for the next (single-cell calller) call.
            // That gives us a proper Disconnect...
            ExcelReference caller        = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
            bool           isCallerArray = caller != null &&
                                           (caller.RowFirst != caller.RowLast ||
                                            caller.ColumnFirst != caller.ColumnLast);

            if (_currentObserver == null || isCallerArray || !IsCompleted())
            {
                // NOTE: At this point the SynchronizationManager must be registered!
                if (!SynchronizationManager.IsInstalled)
                {
                    Debug.Print("SynchronizationManager not registered!");
                    throw new InvalidOperationException("SynchronizationManager must be registered for async and observable support. Call ExcelAsyncUtil.Initialize() in an IExcelAddIn.AutoOpen() handler.");
                }

                // Ensure that Excel-DNA knows about the RTD Server, since it would not have been registered when loading
                ExcelObserverRtdServer.EnsureRtdServerRegistered();

                // Refresh RTD call
                // NOTE: First time this will result in a call to ConnectData, which will call Subscribe and set the _currentObserver
                //       For the first array-group call, this returns null (due to xlUncalced error),
                //       but Excel will call us again... (I hope).
                if (!RtdRegistration.TryRTD(out value, _observerRtdServerProgId, null, _id))
                {
                    // This is the special case...
                    // We return false - to the state creation function that indicates the state should not be saved.
                    value = ExcelError.ExcelErrorNA;
                    return(false);
                }
            }
            else if (_currentObserver != null && IsCompleted())
            {
                // Special call for the Excel 2010 bug helper to indicate we are not refreshing (due to completion)
                if (ExcelRtd2010BugHelper.ExcelVersionHasRtdBug)
                {
                    ExcelRtd2010BugHelper.RecordRtdComplete(_observerRtdServerProgId, _id);
                }
            }

            // No assumptions about previous state here - could have re-entered this class

            // We use #N/A as the 'busy' indicator, as RTD does normally.
            // Add-in creator can remap the 'busy' result in the UDF or another wrapper.
            if (_currentObserver == null)
            {
                value = ExcelError.ExcelErrorNA;
                return(true);
            }

            // Subsequent calls get value from Observer
            value = _currentObserver.Value;
            return(true);
        }
コード例 #2
0
        // Forwarded from XlCall
        // Loads the RTD server with temporary ProgId.
        // CAUTION: Might fail when called from array formula (the first call in every array-group fails).
        //          When it fails, the xlfRtd call returns xlReturnUncalced.
        //          In that case, this function returns null, and does not keep a reference to the created server object.
        //          The next call should then succeed (though a new server object will be created).
        public static bool TryRTD(out object result, string progId, string server, params string[] topics)
        {
            Debug.Print("### RtdRegistration.RTD " + progId);
            // Check if this is any of our business.
            Type rtdServerType;

            if (!string.IsNullOrEmpty(server) || !registeredRtdServerTypes.TryGetValue(progId, out rtdServerType))
            {
                // Just pass on to Excel.
                return(TryCallRTD(out result, progId, null, topics));
            }

            // TODO: Check that ExcelRtdServer with stable ProgId case also works right here -
            //       might need to add to loadedRtdServers somehow

            // Check if already loaded.
            string loadedProgId;

            if (loadedRtdServers.TryGetValue(progId, out loadedProgId))
            {
                if (ExcelRtd2010BugHelper.ExcelVersionHasRtdBug && rtdServerType.IsSubclassOf(typeof(ExcelRtdServer)))
                {
                    ExcelRtd2010BugHelper.RecordRtdCall(progId, topics);
                }
                // Call Excel using the synthetic RtdSrv_xxx (or actual from attribute) ProgId
                return(TryCallRTD(out result, loadedProgId, null, topics));
            }

            // Not loaded already - need to get the Rtd server loaded
            // TODO: Need to reconsider registration here.....
            //       Sometimes need stable ProgIds.
            object rtdServer;

            if (ExcelRtd2010BugHelper.ExcelVersionHasRtdBug && rtdServerType.IsSubclassOf(typeof(ExcelRtdServer)))
            {
                Debug.Print("### Creating Wrapper " + progId);
                rtdServer = new ExcelRtd2010BugHelper(progId, rtdServerType);
            }
            else
            {
                using (XlCall.Suspend())
                {
                    rtdServer = Activator.CreateInstance(rtdServerType);
                }
                ExcelRtdServer excelRtdServer = rtdServer as ExcelRtdServer;
                if (excelRtdServer != null)
                {
                    // Set ProgId so that it can be 'unregistered' (removed from loadedRtdServers) when the RTD server terminates.
                    excelRtdServer.RegisteredProgId = progId;
                }
                else
                {
                    // Make a wrapper if we are not an ExcelRtdServer
                    // (ExcelRtdServer implements exception-handling and XLCall supension itself)
                    rtdServer = new RtdServerWrapper(rtdServer, progId);
                }
            }

            // We pick a new Guid as ClassId for this add-in...
            CLSID clsId = Guid.NewGuid();

            // ... (bad idea - this will cause Excel to try to load this RTD server while it is not registered.)
            // Guid typeGuid = GuidUtilit.CreateGuid(..., DnaLibrary.XllPath + ":" + rtdServerType.FullName);
            // or something based on ExcelDnaUtil.XllGuid
            // string progIdRegistered = "RtdSrv_" + typeGuid.ToString("N");

            // by making a fresh progId, we are sure Excel will try to load when we are ready.
            // Change from RtdSrv.xxx to RtdSrv_xxx to avoid McAfee bug that blocks registry writes with a "." anywhere
            string progIdRegistered = "RtdSrv_" + clsId.ToString("N");

            Debug.Print("RTD - Using ProgId: {0} for type: {1}", progIdRegistered, rtdServerType.FullName);

            try
            {
                using (new SingletonClassFactoryRegistration(rtdServer, clsId))
                    using (new ProgIdRegistration(progIdRegistered, clsId))
                        using (new ClsIdRegistration(clsId, progIdRegistered))
                        {
                            Debug.Print("### About to call TryCallRTD " + progId);
                            if (TryCallRTD(out result, progIdRegistered, null, topics))
                            {
                                // Mark as loaded - ServerTerminate in the wrapper will remove.
                                loadedRtdServers[progId] = progIdRegistered;
                                Debug.Print("### Added to loadedRtdServers " + progId);
                                return(true);
                            }
                            return(false);
                        }
            }
            catch (UnauthorizedAccessException secex)
            {
                Logger.RtdServer.Error("The RTD server of type {0} required by add-in {1} could not be registered.\r\nThis may be due to restricted permissions on the user's HKCU\\Software\\Classes key.\r\nError message: {2}", rtdServerType.FullName, DnaLibrary.CurrentLibrary.Name, secex.Message);
                result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
                // Return true to have the #VALUE stick, just as it was before the array-call refactoring
                return(true);
            }
            catch (Exception ex)
            {
                Logger.RtdServer.Error("The RTD server of type {0} required by add-in {1} could not be registered.\r\nThis is an unexpected error.\r\nError message: {2}", rtdServerType.FullName, DnaLibrary.CurrentLibrary.Name, ex.Message);
                Debug.Print("RtdRegistration.RTD exception: " + ex.ToString());
                result = ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
                // Return true to have the #VALUE stick, just as it was before the array-call refactoring
                return(true);
            }
        }
コード例 #3
0
        // Forwarded from XlCall
        // Loads the RTD server with temporary ProgId.
        public static object RTD(string progId, string server, params string[] topics)
        {
            Debug.Print("### RtdRegistration.RTD " + progId);
            // Check if this is any of our business.
            Type rtdServerType;
            if (!string.IsNullOrEmpty(server) || !registeredRtdServerTypes.TryGetValue(progId, out rtdServerType))
            {
                // Just pass on to Excel.
                return CallRTD(progId, null, topics);
            }

            // TODO: Check that ExcelRtdServer with stable ProgId case also works right here -
            //       might need to add to loadedRtdServers somehow

            // Check if already loaded.
            string loadedProgId;
            if (loadedRtdServers.TryGetValue(progId, out loadedProgId))
            {
                if (ExcelRtd2010BugHelper.ExcelVersionHasRtdBug && rtdServerType.IsSubclassOf(typeof(ExcelRtdServer)))
                {
                    ExcelRtd2010BugHelper.RecordRtdCall(progId, topics);
                }
                // Call Excel using the synthetic RtdSrv.xxx (or actual from attribute) ProgId
                return CallRTD(loadedProgId, null, topics);
            }

            // Not loaded already - need to get the Rtd server loaded
            // TODO: Need to reconsider registration here.....
            //       Sometimes need stable ProgIds.
            object rtdServer;
            if (ExcelRtd2010BugHelper.ExcelVersionHasRtdBug && rtdServerType.IsSubclassOf(typeof(ExcelRtdServer)))
            {
                Debug.Print("### Creating Wrapper " + progId);
                rtdServer = new ExcelRtd2010BugHelper(progId, rtdServerType);
            }
            else
            {
                using (XlCall.Suspend())
                {
                    rtdServer = Activator.CreateInstance(rtdServerType);
                }
                ExcelRtdServer excelRtdServer = rtdServer as ExcelRtdServer;
                if (excelRtdServer != null)
                {
                    // Set ProgId so that it can be 'unregistered' (removed from loadedRtdServers) when the RTD server terminates.
                    excelRtdServer.RegisteredProgId = progId;
                }
                else
                {
                    // Make a wrapper if we are not an ExcelRtdServer
                    // (ExcelRtdServer implements exception-handling and XLCall supension itself)
                    rtdServer = new RtdServerWrapper(rtdServer, progId);
                }
            }

            // We pick a new Guid as ClassId for this add-in...
            CLSID clsId = Guid.NewGuid();

            // ... (bad idea - this will cause Excel to try to load this RTD server while it is not registered.)
            // Guid typeGuid = GuidUtilit.CreateGuid(..., DnaLibrary.XllPath + ":" + rtdServerType.FullName);
            // or something based on ExcelDnaUtil.XllGuid
            // string progIdRegistered = "RtdSrv." + typeGuid.ToString("N");

            // by making a fresh progId, we are sure Excel will try to load when we are ready.
            string progIdRegistered = "RtdSrv." + clsId.ToString("N");
            Debug.Print("RTD - Using ProgId: {0} for type: {1}", progIdRegistered, rtdServerType.FullName);

            try
            {
                using (new SingletonClassFactoryRegistration(rtdServer, clsId))
                using (new ProgIdRegistration(progIdRegistered, clsId))
                using (new ClsIdRegistration(clsId, progIdRegistered))
                {
                    object result;
                    Debug.Print("### About to call TryCallRTD " + progId);
                    if (TryCallRTD(out result, progIdRegistered, null, topics))
                    {
                        // Mark as loaded - ServerTerminate in the wrapper will remove.
                        loadedRtdServers[progId] = progIdRegistered;
                        Debug.Print("### Added to loadedRtdServers " + progId);
                    }
                    return result;
                }
            }
            catch (UnauthorizedAccessException secex)
            {
                Logging.LogDisplay.WriteLine("The RTD server of type {0} required by add-in {1} could not be registered.\r\nThis may be due to restricted permissions on the user's HKCU\\Software\\Classes key.\r\nError message: {2}", rtdServerType.FullName, DnaLibrary.CurrentLibrary.Name, secex.Message );
                return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
            }
            catch (Exception ex)
            {
                Logging.LogDisplay.WriteLine("The RTD server of type {0} required by add-in {1} could not be registered.\r\nThis is an unexpected error.\r\nError message: {2}", rtdServerType.FullName, DnaLibrary.CurrentLibrary.Name, ex.Message);
                Debug.Print("RtdRegistration.RTD exception: " + ex.ToString());
                return ExcelErrorUtil.ToComError(ExcelError.ExcelErrorValue);
            }
        }