前書き
引数のメモリ割り当て義務はどちらにあるか
戻り値のあるメソッド
参照渡し引数
可変個引数
ここでは、VBから利用可能なVC++(ATL)のCOMサーバーを作るための実装方法を事例別に紹介します。
内容は初歩的ではあるけれども、多少なりとも役に立つのではないかと思う。
in, outのそれぞれ、もしくはそれらを組み合わせた属性を持つ引数は、基本的にクライアントが受け持つことになる。従って以下のような定義があった場合、
MIDL
interface IClass : IDispatch{ [ id(1) ]HRESULT Method( [in]long Targ, [out, retval]long* RetVal ); }
out属性の引数であるRetValには既に引数の領域が割り当てられているため、データ領域の確保は必要ない。
COMサーバー(C++)
STDMETHODIMP CClass::Method(long Targ, long* RetVal){ *RetVal = Targ + 1; return S_OK; }
COMクライアント(VB/VBA/VBS)
Dim obj Set obj = CreateObject("Component.Class") Dim num As Long num = 1 obj.Method num MsgBox num ' 2と表示される
戻り値は、out, retval属性を適用した引数をメソッドの引数に用意することによって定義する。
引数はポインタでなければならない。
MIDL
interface IClass : IDispatch{ [ id(1) ]HRESULT Method( [in]long Targ, [out, retval]long* RetVal ); }
COMサーバー(C++)
STDMETHODIMP CClass::Method(long Targ, long* RetVal){ *RetVal = Targ + 1; return S_OK; }
COMクライアント(VB/VBA/VBS)
Dim obj Set obj = CreateObject("Component.Class") MsgBox obj.Method(99) ' 100と表示される
参照渡しという言葉が、COMの用語にあるかどうかは別として、同等の機能をCOMサーバに織り込むには、in, out属性によるインターフェース定義と、ポインタ引数による実装を必要とする。
MIDL
interface IClass : IDispatch{ [ id(1) ]HRESULT Method( [in, out]long* Targ); }
COMサーバー(C++)
STDMETHODIMP CClass::Method(long* Targ){ *Targ = *Targ + 1; return S_OK; }
COMクライアント(VB/VBA/VBS)
Dim obj Set obj = CreateObject("Component.Class") Dim inc inc = 99 obj.Method inc MsgBox inc ' 100と表示される
可変個引数を表現する場合には、vararg 属性と、SAFEARRAY(VARIANT)データ型を併用する。
ただし、SAFEARRAY(...)というデータ型は、(少なくともVS6までの)VisualStudioのメソッド追加ダイアログでは指定できないので、ソースコードを直にいじる必要がある。
※1
MIDL
interface IClass : IDispatch{ [ id(1), vararg ]HRESULT Method( [in]SAFEARRAY(VARIANT) Args, [out, retval]BSTR* RetVal ); }
COMサーバー(C++)
STDMETHODIMP CClass::Method(SAFEARRAY* Array){ 〜Array を使った処理〜 return S_OK; }
COMクライアント(VB/VBA/VBS)
Dim obj Set obj = CreateObject("Component.Class") MsgBox obj.Method(100, 200, 300) MsgBox obj.Method("A","B","C","D")
補足
※1:
とはいっても、全てを手で書くのは面倒くさいし、よほどCOM/ATLを知っていなければ出来ない作業なので、まずは、ダミーとして「SAFEARRAY
arg」というような引数を指定しておいて、置換で必要な部分だけ直すとかいう方法をとったほうが、いいかもしれない。