ExcelのRTD用DCOMサーバの作り方
Excelのワークシート関数 RTD (RealTimeData)用サーバ作成メモ - しがないプログラマ の日記 の続きです。
とりあえず、DCOMとして別プロセスで動くRTDサーバが作れたのではまった場所のメモです。
前提として前回も書きましたが、.NETで DCOMを書くのは大変なのでVBのActiveX.EXEをWrapperとして .NETでの実装をVBから呼び出す形で動かします。VBと .NETとの間はCOM連携機能を使うことになります。
COMとして RTDサーバを作る場合には、この方法で .NETで可能です。しかし、.NETの言語サポートではDCOMはやってくれないため、C++/CLIなどで自前で ATLをガシガシ書く必要があります。しかし、今回はそれはやらない方向で*1。
ActiveX EXE Wrappers - CodeProject
ここを見ると、.NETでCOMに公開したクラスを VB6でWrapperを書けば DCOMとして動かすことができるらしいです。そんな情報が見つかったので今回はこのWarpperを作る方針にしました。
簡単にまとめると、以下のようになります。
- .NETでGUIを用意しExeとする。
- .NETでIRtdServerを実装したクラスを作り、COMに公開する。
- .NETで exeを作成後に、regasmコマンドで以下のコマンドを実行する。
regasm RTDServer.exe /tlb /codebase *2
- VB6でIRtdServerを実装した公開クラスを作り、ActiveX.EXEとする。
- IRtdServerの実装は上記の .NETの実装をただ呼ぶだけにする。
- ActiveX.EXEのEntryPointから、.NET側のGUIを表示する。
- ActiveX.EXEをレジストリに登録し、ExcelのRTD関数から呼び出す。
RTDServerWrapper.exe /RegServer
.NETでの IRtdServerの実装例
何もしない空実装ですが
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using Microsoft.Office.Interop.Excel; namespace RTDServer { public class MessageEventArgs: EventArgs { public string message; public MessageEventArgs(string msg) { message = msg; } } [ComVisible(true)] public class RTDFunctions : IRtdServer { public delegate void MessageEventHandler(object sender, MessageEventArgs e); public event MessageEventHandler message; private static RTDFunctions instance_; public static RTDFunctions GetInstance() { return instance_; } private IRTDUpdateEvent callback_; public RTDFunctions() { instance_ = this; } public bool Service { get { return callback_ != null; } } public int ServerStart(IRTDUpdateEvent CallbackObject) { SendMessage("Start Service."); callback_ = CallbackObject; return 1; } public void ServerTerminate() { SendMessage("Terminate Service."); callback_ = null; } public object ConnectData(int TopicID, [MarshalAs(UnmanagedType.SafeArray)] ref Array Strings, ref bool GetNewValues) { SendMessage(String.Format("ConnectData Detail: ID={0}", TopicID)); return ""; } public void DisconnectData(int TopicID) { SendMessage(String.Format("DisconnectData: ID={0}", TopicID)); } public int Heartbeat() { SendMessage(String.Format("Heartbeat")); return 1; } [return: MarshalAs(UnmanagedType.SafeArray)] public Array RefreshData(ref int TopicCount) { object[,] refreshed = new object[2, 0]; TopicCount = 0; SendMessage(String.Format("RefreshData: Count={0}", TopicCount)); return refreshed; } private void SendMessage(string msg) { if (message != null) { message(this, new MessageEventArgs(msg)); } } public void StartApplication() { System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); System.Windows.Forms.Application.Run(new 画面()); } } }
VB6でのIRtdServerの実装例
上記の .NETが公開している RTDServer.RTDFunctionsのtlbファイルを参照するように設定します。
Option Explicit Implements IRtdServer Function IRtdServer_ConnectData(ByVal TopicID As Long, ByRef Strings() As Variant, GetNewValues As Boolean) As Variant Dim ret As Variant ret = MyRtdServer.ConnectData(TopicID, Strings, GetNewValues) IRtdServer_ConnectData = ret End Function Sub IRtdServer_DisconnectData(ByVal TopicID As Long) MyRtdServer.DisconnectData (TopicID) End Sub Function IRtdServer_Heartbeat() As Long IRtdServer_Heartbeat = MyRtdServer.Heartbeat() End Function Function IRtdServer_RefreshData(TopicCount As Long) As Variant() IRtdServer_RefreshData = MyRtdServer.RefreshData(TopicCount) End Function Function IRtdServer_ServerStart(ByVal CallbackObject As Excel.IRTDUpdateEvent) As Long Dim ret As Long ret = MyRtdServer.ServerStart(CallbackObject) IRtdServer_ServerStart = ret End Function Sub IRtdServer_ServerTerminate() MyRtdServer.ServerTerminate End Sub
ソースを貼ったら長くなったのではまった点などは次エントリにします。