繁体   English   中英

如何使用RtdServer在C#中创建实时Excel自动化加载项?

[英]How do I create a real-time Excel automation add-in in C# using RtdServer?

我的任务是使用RtdServer在C#中编写实时Excel自动化加载项。 我非常依赖Stack Overflow中遇到的知识。 我决定表达我的感谢,写下如何记录所有我学到的东西。 Kenny Kerr的Excel RTD服务器:最小的C#实现文章帮助我入门。 我发现Mike RosenblumGovert的评论特别有帮助。

(作为下述方法的替代方案,您应该考虑使用Excel- DNA。Excel-DNA允许您构建免注册的RTD服务器.COM注册需要管理权限,这可能会导致安装问题。就是说,下面的代码工作正常。)

使用RtdServer在C#中创建实时Excel自动化加载项:

1)在Visual Studio中创建一个C#类库项目并输入以下内容:

using System;
using System.Threading;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace StackOverflow
{
    public class Countdown
    {
        public int CurrentValue { get; set; }
    }

    [Guid("EBD9B4A9-3E17-45F0-A1C9-E134043923D3")]
    [ProgId("StackOverflow.RtdServer.ProgId")]
    public class RtdServer : IRtdServer
    {
        private readonly Dictionary<int, Countdown> _topics = new Dictionary<int, Countdown>();
        private Timer _timer;

        public int ServerStart(IRTDUpdateEvent rtdUpdateEvent)
        {
            _timer = new Timer(delegate { rtdUpdateEvent.UpdateNotify(); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
            return 1;
        }

        public object ConnectData(int topicId, ref Array strings, ref bool getNewValues)
        {
            var start = Convert.ToInt32(strings.GetValue(0).ToString());
            getNewValues = true;

            _topics[topicId] = new Countdown { CurrentValue = start };

            return start;
        }

        public Array RefreshData(ref int topicCount)
        {
            var data = new object[2, _topics.Count];
            var index = 0;

            foreach (var entry in _topics)
            {
                --entry.Value.CurrentValue;
                data[0, index] = entry.Key;
                data[1, index] = entry.Value.CurrentValue;
                ++index;
            }

            topicCount = _topics.Count;

            return data;
        }

        public void DisconnectData(int topicId)
        {
            _topics.Remove(topicId);
        }

        public int Heartbeat() { return 1; }

        public void ServerTerminate() { _timer.Dispose(); }

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type t)
        {
            Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(@"CLSID\{" + t.GUID.ToString().ToUpper() + @"}\Programmable");
            var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"CLSID\{" + t.GUID.ToString().ToUpper() + @"}\InprocServer32", true);
            if (key != null)
                key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll", Microsoft.Win32.RegistryValueKind.String);
        }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type t)
        {
            Microsoft.Win32.Registry.ClassesRoot.DeleteSubKey(@"CLSID\{" + t.GUID.ToString().ToUpper() + @"}\Programmable");
        }
    }
}

2)右键单击项目,然后单击添加>新项...>安装程序类。 切换到代码视图并输入以下内容:

using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace StackOverflow
{
    [RunInstaller(true)]
    public partial class RtdServerInstaller : System.Configuration.Install.Installer
    {
        public RtdServerInstaller()
        {
            InitializeComponent();
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Commit(IDictionary savedState)
        {
            base.Commit(savedState);

            var registrationServices = new RegistrationServices();
            if (registrationServices.RegisterAssembly(GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase))
                Trace.TraceInformation("Types registered successfully");
            else
                Trace.TraceError("Unable to register types");
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);

            var registrationServices = new RegistrationServices();
            if (registrationServices.RegisterAssembly(GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase))
                Trace.TraceInformation("Types registered successfully");
            else
                Trace.TraceError("Unable to register types");
        }

        public override void Uninstall(IDictionary savedState)
        {
            var registrationServices = new RegistrationServices();
            if (registrationServices.UnregisterAssembly(GetType().Assembly))
                Trace.TraceInformation("Types unregistered successfully");
            else
                Trace.TraceError("Unable to unregister types");

            base.Uninstall(savedState);
        }
    }
}

3)右键单击项目属性并检查以下内容:应用程序>装配信息...>使装配COM-Visible和Build>注册COM Interop

3.1)右键单击项目Add Reference ...> .NET选项卡> Microsoft.Office.Interop.Excel

4)构建解决方案(F6)

5)运行Excel。 转到Excel选项>加载项>管理Excel加载项>自动化,然后选择“StackOverflow.RtdServer”

6)在单元格中输入“= RTD(”StackOverflow.RtdServer.ProgId“,200)”。

7)交叉你的手指,希望它有效!

从计时器线程调用UpdateNotify最终会导致奇怪的错误或从Excel断开连接。

必须仅从调用ServerStart()的同一线程调用UpdateNotify()方法。 它没有在RTDServer帮助中记录,但它是COM的限制。

修复很简单。 使用DispatcherSynchronizationContext捕获调用ServerStart的线程,并使用该线程将调用分派给UpdateNotify:

public class RtdServer : IRtdServer
{
    private IRTDUpdateEvent _rtdUpdateEvent;
    private SynchronizationContext synchronizationContext;

    public int ServerStart( IRTDUpdateEvent rtdUpdateEvent )
    {
        this._rtdUpdateEvent = rtdUpdateEvent;
        synchronizationContext = new DispatcherSynchronizationContext();
        _timer = new Timer(delegate { PostUpdateNotify(); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
        return 1;
    }


    // Notify Excel of updated results
    private void PostUpdateNotify()
    {
        // Must only call rtdUpdateEvent.UpdateNotify() from the thread that calls ServerStart.
        // Use synchronizationContext which captures the thread dispatcher.
        synchronizationContext.Post( delegate(object state) { _rtdUpdateEvent.UpdateNotify(); }, null);
    }

    // etc
} // end of class

按照前面两个RTD服务器的答案为我工作。 但是,在运行Excel x64的x64计算机上遇到问题。 就我而言,在我将项目的“目标平台”切换到x64之前,Excel总是显示#N / A.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM