如何使用 RtdServer 在 C# 中创建实时 Excel 自动化加载项?
Posted
技术标签:
【中文标题】如何使用 RtdServer 在 C# 中创建实时 Excel 自动化加载项?【英文标题】:How do I create a real-time Excel automation add-in in C# using RtdServer? 【发布时间】:2011-03-22 20:44:19 【问题描述】:我的任务是使用 RtdServer 在 C# 中编写一个实时 Excel 自动化插件。我非常依赖在 Stack Overflow 中学到的知识。为了表达我的谢意,我决定写一个如何将我所学的知识联系在一起的文档。 Kenny Kerr 的Excel RTD Servers: Minimal C# Implementation 文章帮助我入门。我发现 Mike Rosenblum 和 Govert 的 cmets 特别有用。
【问题讨论】:
辛苦了,我记得我第一次尝试让其中一个工作的人没有任何例子。我相信这会对某人有所帮助:) 我会做两件事。 1) 您可以创建一个 xla,它将对 RTD 的调用封装在一个函数中,该函数将为您提供更清晰、更清晰的语法和 Excel 端的错误处理。其次,如果您执行任何 VSTO 操作,以及使用 .net 编码的 IRTDServer 进行较小扩展的操作,请使用 Excel 中的选项对话框来模拟 Excel 被阻止。你的代码必须处理它。 非常正确。来自 Excel (RefreshData) 的拉取请求可能会在您调用 UpdateNotify 后立即发出,但有很多事情可能会无限期地延迟它(对话框、输入公式等)。你不能永远排队更新。 我也同意,一般来说你不想强迫人类直接调用 RTD 函数。在 VBA 中创建包装函数很容易。我不确定如何在 C# 中创建包装函数,并发布了与 here 相关的问题 @Frank - 我不确定如何在 c# 中编写包装器,但如果您要在 c++ 中编写 UDF 包装器 - 您可以使用 xlfRtd(excel 2007+ c api 的一部分)来将调用包装到您的 rtd 服务器。 【参考方案1】:(作为下面描述的方法的替代方法,您应该考虑使用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 ***
public class Countdown
public int CurrentValue get; set;
[Guid("EBD9B4A9-3E17-45F0-A1C9-E134043923D3")]
[ProgId("***.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 ***
[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 可见并构建 > 注册 COM 互操作
3.1) 右键单击项目添加引用... > .NET 选项卡 > Microsoft.Office.Interop.Excel
4) 构建解决方案 (F6)
5) 运行 Excel。转到 Excel 选项 > 加载项 > 管理 Excel 加载项 > 自动化并选择“***.RtdServer”
6) 在单元格中输入“=RTD("***.RtdServer.ProgId",,200)"。
7) 交叉手指,希望它有效!
【讨论】:
Excel如何找到带有ProgId的文件所在的位置?我正在尝试将我的 Excel 插件移动到另一台机器上,但我无法在 Excel 上设置它 这个例子中Trace
写到什么路径?我找不到它。
Sysytem.Diagnostics.Trace 与 System.Console 类似,但更灵活。它允许您执行诸如添加跟踪级别和添加多个侦听器之类的操作。
RtdServer的GUID属性有什么意义?我需要指定它吗?我从哪里得到它?
COM 注册需要 GUID。您可以使用工具 > 在 Visual Studio 中创建 GUID 生成 GUID。【参考方案2】:
从计时器线程调用 UpdateNotify 最终会导致奇怪的错误或与 Excel 断开连接。
UpdateNotify() 方法只能从调用 ServerStart() 的同一线程中调用。 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
【讨论】:
非常好(另一个选择是有一个线程安全的集合来读取/写入)。但是,从纯 C# 类库中,定位 DispatcherSynchronizationContext 需要哪些额外的引用?我找不到它(我怀疑这是 WPF 特有的东西)。【参考方案3】:按照 RTD 服务器的前两个答案对我有用。但是,我在运行 Excel x64 的 x64 机器上遇到了问题。就我而言,在我将项目的“目标平台”切换到 x64 之前,Excel 始终显示#N/A。
【讨论】:
以上是关于如何使用 RtdServer 在 C# 中创建实时 Excel 自动化加载项?的主要内容,如果未能解决你的问题,请参考以下文章