JScript配列をCOM(C++)で参照する
ちょっとした経緯で,JScriptから呼び出されるCOMをC++で実装することになりました。
そこでJScriptからCOMに対して,パラメータとして配列を渡す必要があったのですが,
JScriptで扱う配列は,COMで通常扱うSafeArray配列ではないということをことを知りました。
Webでいろいろ調べてみると,
"length"と配列インデックス("0","1","2",...)をメソッドとして持つIDispatchインターフェースとして渡されているようです。
このインターフェース(JScript配列)をCOM内部で取り扱う形式に変換する処理を作成してみましたので,ここで紹介します。
なお,今回扱っているのは旧版のJScriptで,.Net 言語になった新しいもの(JScript.Net)には当てはまりませんので,
その点了承下さい。
最初にIDispatchインターフェースを介して"name"で指定したプロパティ値を取得するメソッドを用意します。
CComVariant JsArray::GetIDispatchProperty(IDispatch* iDisp,
const char* name) {
HRESULT hr;
DISPID dispid = 0;
OLECHAR FAR* szMember = CA2W(name);
hr = iDisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_SYSTEM_DEFAULT, &dispid);
if(FAILED(hr)) {
throw exception("IDispatch::GetIDsOfNames");
}
DISPPARAMS params = { NULL, NULL, 0, 0 };
VARIANT var;
EXCEPINFO excepinfo;
UINT nArgErr;
hr = iDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, ¶ms, &var,
&excepinfo, &nArgErr);
if(FAILED(hr)) {
throw exception("IDispatch::Invoke");
}
return CComVariant(var);
}
このメソッドを使って,JScript配列の要素数を取得します。
要素数は,プロパティ"length"から取り出せる整数値(VT_I4)です。
int JsArray::GetJScriptArrayLength(IDispatch* iDispatch) {
CComVariant length = GetIDispatchProperty(iDispatch, "length");
HRESULT hr = length.ChangeType(VT_I4);
if(FAILED(hr)) {
throw exception("CComVariant::ChangeType(VT_I4)");
}
return length.intVal;
}
要素数を参照して,配列の内容を取得するメソッドを作成します。
配列値はVariantで格納されているので,CComVariant配列(Vector)として取得しています。
vector JsArray::GetJScriptArrayValues(IDispatch* iDispatch) {
int length = GetJScriptArrayLength(iDispatch);
std::vector result(length);
for(int n = 0; n < length; ++n) {
char str[10];
::sprintf(str, "%d", n);
result[n] = GetIDispatchProperty(iDispatch, str);
}
return result;
}
後は取得したVariant配列の各要素を,所定の型の値として参照すればOKです。