Fra*_*ank 25 c# vsto rtd excel-addins excel-udf
我的任务是使用RtdServer在C#中编写实时Excel自动化加载项.我非常依赖Stack Overflow中遇到的知识.我决定表达我的感谢,写下如何记录所有我学到的东西.Kenny Kerr的Excel RTD服务器:最小的C#实现文章帮助我入门.我发现Mike Rosenblum和Govert的评论特别有帮助.
Fra*_*ank 33
(作为下述方法的替代方案,您应该考虑使用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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
}
| 归档时间: |
|
| 查看次数: |
16040 次 |
| 最近记录: |