これを実現することはそれほど容易ではありません。どの言語でも静的に宣言できるわけではないからです。実行時においてコードが正しいかを確認し、メソッド呼び出しの手続きを行うことになります。話だけでは分かりにくいので、次に示すJScriptのコードを例に考えてみます。実際にコードを実行する場合は、メモ帳などにコードを張り付けて拡張子.jsで保存し、それを開いてください。Host)が起動されることになります。引数のProIDから関連するCLSIDを特定し、そのCLSIDで識別されるオブジェクトを作成します。上記の例で言えばこのオブジェクトはWordに相当しますから、Wordが起動されることになります。ここである1つの問題が生じます。それは、オブジェクトが本当にそのメソッドやプロパティを実装しているのかという点です。オブジェクトがIDispatchを実装していれば、IDispatch::GetIDsOfNamesを呼び出すことによってメソッドの有無を確認することができます。IDispatch::Invokeを呼び出せば、メソッドやプロパティを実際に使用することができます。それをWSHが使用してGetIDsOfNamesやInvokeを呼び出しているからなのです。このように、IDispatchを通じてオブジェクトを操作できる仕組みのことをオートメーションと呼びます。C++の視点からすれば、メソッドとプロパティはどちらも純粋仮想関数のように見えますが、その位置づけは互いに異なります。クライアントはこの属性に対して設定と取得を行うことができます。これらのどちらかがVisible指定時に呼び出されることになります。メソッドは何らかの命令を実行する関数です。IDispatchを使用してメソッドやプロパティを呼び出すには、基本的に2つの手順を踏むことになります。1つは、次に示すGetIDsOfNamesを呼び出してメソッドまたはプロパティのDISPIDを取得することです。DISPIDとは、メソッドやプロパティを識別する番号のことです。);riidは、将来のために予約されているためNULLを指定します。rgszNamesは、DISPIDを取得したいメソッドやプロパティの名前を指定します。cNamesは、rgszNamesの要素数を指定します。lcidは、ローケルIDを指定します。rgDispIdは、DISPIDを受け取る変数のアドレスを指定します。DISPIDを取得したら、Invokeを呼び出すことでメソッドやプロパティを呼び出すことができます。);dispIdMemberは、呼び出したいメソッドまたはプロパティを表すDISPIDを指定します。riidは、将来のために予約されているためNULLを指定します。lcidは、ローケルIDを指定します。wFlagsは、呼び出しの種類を表す定数を指定します。プロパティを取得する場合はDISPATCH_PROPERTYGETを指定します。pDispParamsは、DISPPARAMS構造体のアドレスを指定します。pVarResultは、戻り値を受け取る変数のアドレスを指定します。wFlagsがDISPATCH_PROPERTYPUTの場合は考慮されません。pExcepInfoは、発生した例外を受け取るEXCEPINFO構造体のアドレスを指定します。puArgErrは、型が合わなかった引数のインデックスを受け取る変数のアドレスを指定します。引数の型が型が合わなかった場合は、戻り値がDISP_E_TYPEMISMATCHになります。DISPPARAMS構造体は、次のように定義されています。cNamedArgs;};rgvargは、VARIANT構造体の配列を指定します。この構造体は、メソッドまたはプロパティの引数を格納している必要があります。rgdispidNamedArgsは、DISPIDの配列を指定します。プロパティを設定する場合は適切な配列を指定します。cArgsは、rgvargの要素数を指定します。cNamedArgsは、rgdispidNamedArgsの要素数を指定します。どのような型の引数でも統一的に扱いたいからです。LONG型を要求するメソッドやプロパティしか呼び出せなくなり問題といえます。どのような型のデータもこの構造体に格納することができます。VARTYPE型のvtメンバを調べることになります。このメンバには、VARTYPEと呼ばれる定数のいずれかが格納されます。VariantInitがこれを設定する。次のようにVARIANT構造体を初期化します。bstr;BSTR型を指定したい場合は、vtメンバにVT_BSTRを指定します。これによりbstrValメンバが考慮されるようになるため、このメンバにBSTR型の変数を指定することになります。SysAllocStringは、LPWSTR型をBSTR型に変換する関数です。文字列の先頭4バイトには文字のサイズが格納されるという特徴があります。事実上LPWSTR型と同じように扱うことができます。Basicで使用される文字列と互換性があります。IDispatchを使用するためにはこれを実装するサーバーを見つけなければなりません。今回はWordを例にIDispatchの使い方を確認することにします。スクリプト言語だけでなくC/C++アプリケーションにとっても非常に有用なものです。SDKに含まれていないからです。IDispatchを使用して動的にメソッドを呼び出す以外に方法がないのです。各種定義がなければメソッド名も分からないようにも思えますが、これについては次節で説明します。今回のプログラムは、前述の通りWordを起動して表示します。hr;}まず、WordのProgIDをCLSIDに変換し、これをCoCreateInstanceに指定します。アウトプロセスサーバーとして起動するべくCLSCTX_LOCAL_SERVERを指定します。pApplicationの型はIDispatchでなければなりません。これを表示状態にする必要があります。これをIDispatch::Invokeで実行する必要があります。第3引数に呼び出しの種類を指定します。今回はプロパティを設定するため、DISPATCH_PROPERTYPUTを指定しています。第4引数は引数の配列で、第5引数は引数の数です。vtにVT_I4を指定してlValに数値を指定します。0でない値を指定すればウインドウが表示されることになります。最後の引数は、メソッドまたはプロパティの戻り値を受け取る変数のアドレスを指定します。プロパティを設定する場合は戻り値がありませんからNULLを指定しています。Invokeの実装は次のようになっています。hr;}まず、GetIDsOfNamesを呼び出してメソッドまたはプロパティのDISPIDを取得します。次に、渡された引数の数と引数をDISPPARAMS構造体のcArgsとrgvargに指定します。rgdispidNamedArgsにはDISPIDの配列を指定します。通常は、DISPID_PROPERTYPUTを格納した変数のアドレスを指定するだけで構いません。cNamedArgsに0を指定し、rgdispidNamedArgsにNULLを指定します。DISPIDの取得とDISPPARAMS構造体の初期化が終われば、IDispatch::Invokeを呼び出してメソッドまたはプロパティを実行することができます。例外情報とエラー情報は不要ということで、第7引数と第8引数はNULLを指定しています。IDispatchを実装するサーバーは、自身の機能を必要とする言語がスクリプトに限らないことを意識するべきです。できることならpApplication->put_Visible(1)のような分かりやすい呼び出しをしたいからです。した要望に応えるためには、サーバーはIDispatchの他に通常のカスタムインターフェースを実装することになるでしょう。その取得したインターフェースのメソッドをvtblから直接呼び出すことができます。vtblからの呼び出しはアーリーバインディングと呼ばれています。デュアルインターフェースを持っていると呼ばれたりします。何もこれから実行するオブジェクトであるとは限りません。そのWordが登録したオブジェクトを操作したいこともあるでしょう。このように、既に登録されたオブジェクトを取得する例を次に示します。TEXT("オブジェクトが登録されていません。実行されている場合は、そのオブジェクトが登録しているオブジェクトのポインタを第3引数に返します。QueryInterfaceでIDispatchを取得し、動的にプロパティやメソッドを呼び出すことができます。上記ではQuitメソッドを呼び出していますから、現在起動しているWordを終了させることになります。