本記事は、COM Advent Calendar 2014 – Qiitaの22日目の記事です。
OBJREFモニカーの回で、コンピュータ間のマーシャリングもできるらしいことを書きました。今回はそれを試してみることにしました。というわけで、結局DCOMに足を突っ込んでしまいました。
2台のWindowsマシンを用意します。接続を待ち受けるC++側と接続しに行くVBScript側です。以下の準備を行っておきます。
- 双方を同じLANに接続する。
- 双方で同じ名前・パスワードのユーザーを作り、そこでプログラムを実行する。ただし、デフォルトではパスワードが空だとうまくいきません。
- C++プログラムを実行する側では、Windowsファイアウォールなどを切っておくか、除外設定を行う。
- もしDCOMを無効にしていたら、有効に戻す。参考:Geekなぺーじ:DCOM(分散COM)を無効にする
C++側プログラムは以下です。main関数以外は前々回(OBJREFモニカー)とほぼ同じです。OBJREFモニカーの文字列を標準出力に表示して、Enterが入力されるまで待ち受ける、という内容です。
#define _ATL_NO_AUTOMATIC_NAMESPACE #include <iostream> #include <sstream> #include <stdlib.h> #include <windows.h> #include <comdef.h> #include <atlbase.h> #include <atlcom.h> #include <atlutil.h> #include <boost/scope_exit.hpp> class Module : public ATL::CAtlExeModuleT<Module> {}; Module module; class ATL_NO_VTABLE Hoge : public ATL::CComObjectRootEx<ATL::CComMultiThreadModel> , public ATL::CComCoClass<Hoge> , public IDispatch { public: BEGIN_COM_MAP(Hoge) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() IFACEMETHOD(GetTypeInfoCount)( __RPC__out UINT*) override { return E_NOTIMPL; } IFACEMETHOD(GetTypeInfo)( UINT, LCID, __RPC__deref_out_opt ITypeInfo**) override { return E_NOTIMPL; } IFACEMETHOD(GetIDsOfNames)( __RPC__in REFIID, /*__RPC__in_ecount_full(cNames)*/ LPOLESTR*, __RPC__in_range(0,16384) UINT, LCID, /*__RPC__out_ecount_full(cNames)*/ DISPID*) override { return E_NOTIMPL; } IFACEMETHOD(Invoke)( _In_ DISPID dispIdMember, _In_ REFIID riid, _In_ LCID, _In_ WORD flags, _In_ DISPPARAMS *pDispParams, _Out_opt_ VARIANT* pVarResult, _Out_opt_ EXCEPINFO*, _Out_opt_ UINT*) override { if (dispIdMember != DISPID_VALUE || flags != DISPATCH_METHOD) return DISP_E_MEMBERNOTFOUND; if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE; if (pDispParams->cArgs != 0) return DISP_E_BADPARAMCOUNT; if (pDispParams->cNamedArgs != 0) return DISP_E_NONAMEDARGS; VariantInit(pVarResult); std::cout << "Hoge::Invoke" << std::endl; return S_OK; } }; std::wstring GetObjrefMonikerDisplayString(IUnknown* obj) { IMonikerPtr moniker; ATLENSURE_SUCCEEDED(CreateObjrefMoniker(obj, &moniker)); IBindCtxPtr bc; ATLENSURE_SUCCEEDED(CreateBindCtx(0, &bc)); LPOLESTR monikerStr; ATLENSURE_SUCCEEDED(moniker->GetDisplayName(bc, nullptr, &monikerStr)); BOOST_SCOPE_EXIT_ALL(&monikerStr) { CoTaskMemFree(monikerStr); }; return monikerStr; } int main() { try { ATLENSURE_SUCCEEDED(CoInitializeEx( nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)); IDispatchPtr hoge; ATLENSURE_SUCCEEDED(Hoge::CreateInstance(&hoge)); std::wstring monikerStr = GetObjrefMonikerDisplayString(hoge); ATLENSURE_SUCCEEDED(CoLockObjectExternal(hoge, TRUE, FALSE)); std::wcout << monikerStr << std::endl; std::cin.get(); } catch (const ATL::CAtlException& e) { std::clog << std::hex << std::showbase; std::clog << e.m_hr << ' ' << ATL::AtlGetErrorDescription(e) << std::endl; } CoUninitialize(); } |
VBScript側は以下です。前々回と変わっていません。cscript objref.vbs /str:モニカーのように、コマンドライン引数でモニカーを指定します。
objref = WScript.Arguments.Named("str") WScript.Echo "--------" WScript.Echo "objref.vbs: " & objref WScript.Echo "--------" Set hoge = GetObject(objref) hoge() |
これで動かしてみました。まず、1台目のコンピュータでC++プログラムを起動しておきます。次に、画面に表示されたOBJREFモニカーを2台目のコンピュータで、objref.vbsのコマンドライン引数に書き写し、起動するという手順です。
すると、無事(?)C++側でHoge::Invokeが出力され、Hoge::Invoke関数が呼び出されたことが確認できました。CoLockObjectExternalしているため、VBScriptを実行するたび、何度でも動きます。
認証が掛かっているとは言え、これはちょっとやめてほしい場合もあると思います。というわけで、次回はCOMサーバープロセスにおいて他コンピュータからのアクセスを禁じる話の予定です。
スポンサード リンク |
この記事のカテゴリ
- COM ⇒ OBJREFモニカーによるコンピュータ間の通信を試す