打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ExcelのRTDサーバーを、VC++で書いてみた。 - しがないプログラマ の日記

ExcelのRTDサーバーを、VC++で書いてみた。

12/30追記:interface IControllerのidl定義が抜けていたのを追加。

ref: ExcelのRTD用DCOMサーバの作り方 - しがないプログラマ の日記

昔にVBでっち上げたCOMサーバーを、VC++で書き換えてみようと思い立ち挑戦してみました。この辺りの情報は、英語ではある程度見つかるんですが日本語情報に乏しいので色々とはまりました。。。

以下は、とりあえずRTDサーバーとしては簡単に渡された値を返すだけのものの作り方です。

ExcelからはRTDワークシート関数から「=RTD("RtdServerSample.Controller","","test value")」と呼び出せます。

プロジェクトの作成

「RtdServerSample」という名前で、VisualStudioのATLプロジェクトを作成します*1オプションで作成するファイルの種類をDLLから普通のExeに変更します。デフォルトプロジェクト名がCOMとしての名前になるため、注意が必要です。

COMクラスの作成

「Controller」という名前で、ATLシンプルオブジェクトを作成します。スレッドの種類はアパートメントにします。

idlファイル編集

RtdServerSample.idlが生成されているので、以下の記述をimport文の後に追加する。

※下記のuuidは Excel2003向けです。他のバージョン用にする場合には Excelのタイプライブラリから「IRTDUpdateEvent 」と「IRtdServer」の定義コピーしてくる必要があります。

[uuid(A43788C1-D91B-11D3-8F39-00C04F3651B8),dual,oleautomation]interface IRTDUpdateEvent : IDispatch {[id(0x0000000a)] HRESULT UpdateNotify();[id(0x0000000b), propget] HRESULT HeartbeatInterval([out, retval] long* value);[id(0x0000000b), propput] HRESULT HeartbeatInterval([in] long value);[id(0x0000000c)] HRESULT Disconnect();};[uuid(EC0E6191-DB51-11D3-8F3E-00C04F3651B8),dual,oleautomation]interface IRtdServer : IDispatch {[id(0x0000000a)]HRESULT ServerStart([in] IRTDUpdateEvent* callback,[out, retval] long* result);[id(0x0000000b)]HRESULT ConnectData([in] long topicId,[in] SAFEARRAY(VARIANT)* strings,[in, out] VARIANT_BOOL* newValues,[out, retval] VARIANT* values);[id(0x0000000c)]HRESULT RefreshData([in, out] long* topicCount,[out, retval] SAFEARRAY(VARIANT)* data);[id(0x0000000d)]HRESULT DisconnectData([in] long topicId);[id(0x0000000e)]HRESULT Heartbeat([out, retval] long* result);[id(0x0000000f)]HRESULT ServerTerminate();};

interface IController の定義に IRtdServerのメソッドを追加する。

	interface IController : IDispatch{[id(0x0000000a), helpcontext(0x0007a125)]HRESULT ServerStart([in] IRTDUpdateEvent* CallbackObject,[out, retval] long* pfRes);[id(0x0000000b), helpcontext(0x0007a126)]HRESULT ConnectData([in] long TopicID,[in] SAFEARRAY(VARIANT)* Strings,[in, out] VARIANT_BOOL* GetNewValues,[out, retval] VARIANT* pvarOut);[id(0x0000000c), helpcontext(0x0007a127)]HRESULT RefreshData([in, out] long* TopicCount,[out, retval] SAFEARRAY(VARIANT)* parrayOut);[id(0x0000000d), helpcontext(0x0007a128)]HRESULT DisconnectData([in] long TopicID);[id(0x0000000e), helpcontext(0x0007a129)]HRESULT Heartbeat([out, retval] long* pfRes);[id(0x0000000f), helpcontext(0x0007a12a)]HRESULT ServerTerminate();};

さらに、idlファイル内に coclass Controller の定義があるので以下の1文を追加する。

interface IRtdServer;

ここまでの作業が終わったら、一旦ビルドを行い問題ないことを確認する。

Controllerクラスの実装

Controller.hを開き以下の作業を行う。

継承クラスに以下を追加する。

public IDispatchImpl<IRtdServer, &__uuidof(IRtdServer)>

BEGIN_COM_MAP~END_COM_MAPの間に以下を追加する*2

//COM_INTERFACE_ENTRY(IDispatch)COM_INTERFACE_ENTRY2(IDispatch, IRtdServer)COM_INTERFACE_ENTRY(IRtdServer)

継承したメソッドの定義を追加するため、クラス定義の末尾に以下の記述を追加する。

private:IRTDUpdateEvent* m_pCallback;// IRtdServer Methodspublic:STDMETHOD(ServerStart)(IRTDUpdateEvent * CallbackObject, long * pfRes);STDMETHOD(ConnectData)(long TopicID, SAFEARRAY * * Strings, VARIANT_BOOL * GetNewValues, VARIANT * pvarOut);STDMETHOD(RefreshData)(long * TopicCount, SAFEARRAY * * parrayOut);STDMETHOD(DisconnectData)(long TopicID);STDMETHOD(Heartbeat)(long * pfRes);STDMETHOD(ServerTerminate)();

最後に、Controller.cppを開き以下の実装を追加する。

#include <atlsafe.h>// CControllerSTDMETHODIMP CController::ServerStart(IRTDUpdateEvent* CallbackObject, long* pfRes){m_pCallback = CallbackObject;*pfRes = 1;return S_OK;}STDMETHODIMP CController::ServerTerminate(){m_pCallback = NULL;return S_OK;}STDMETHODIMP CController::ConnectData(long TopicID, SAFEARRAY** Strings, VARIANT_BOOL* GetNewValues, VARIANT* pvarOut){CComBSTR result;CComSafeArray<VARIANT> input(*Strings);for (long index = input.GetLowerBound(), last = input.GetUpperBound(); index <= last; ++index) {const CComVariant& value = input.GetAt(index);value.CopyTo(&result);}CComVariant variant(result);HRESULT hr = variant.Detach(pvarOut);if ( FAILED(hr) ) {return E_FAIL;}return S_OK;}STDMETHODIMP CController::DisconnectData(long TopicID){return S_OK;}STDMETHODIMP CController::RefreshData(long* TopicCount, SAFEARRAY** parrayOut){CComSafeArray<VARIANT> result;*TopicCount = result.GetCount();*parrayOut = result.Detach();return S_OK;}STDMETHODIMP CController::Heartbeat(long* pfRes){*pfRes = 1;return S_OK;}

以上で実装は完了です。ビルドを行えば RTDサーバーとして動きます。

後は、Controllerの実装を変えていけば、色々な値を返すサーバーを作ることができるかと思います。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
EXCEL不同的单元格格式太多解决方案
在COM中使用数组参数-SafeArray
J-Integra中的java数据类型与C++数据类型对比 - zhaojun2001的专...
SAFEARRAY使用实例
vbs正则
ATL/COM
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服