CUIAutomationCOM クラス オブジェクトのメソッドを、アクティブ/Windows スクリプト アプリケーションを介して読み込んで実行するスクリプトに公開したいと考えています (スクリプト エンジンを実装していません。具体的には「JScript」エンジンを使用しています)。IDispatchスクリプト ホストは通常、 を実装するオブジェクトを自動的に公開できますが、CUIAutomationクラスは を実装しませんIDispatch。QueryInterfaceオブジェクトへのIDispatchポインターを呼び出すには、 を返しE_NOINTERFACEます。
以下で詳しく説明する私の質問全体は、基本的にこれに要約されます。実装されていないオブジェクトのディスパッチを実装することは可能IDispatchですか? 可能であれば、オブジェクトのコクラスの型情報を持つことは、必要な (そしておそらく十分な) 要件であるに違いありません。可能であれば、以下で説明するように、そうしようとして何が問題になっていますか? 私の代替手段は何ですか?
ITypeInfo前述のように、私の解決策は、コクラスの型情報 ( ) を取得する必要がある場合CUIAutomation、理論的には、コクラスのオブジェクトを実装しなくても、 likeおよびIDispatchのメソッドを介してランタイム ディスパッチを実行できるはずであるという私の仮説を中心にしています。実際には、 を実装し、オブジェクト (または適切な型情報と組み合わせることができるもの) をラップし、メンバーのディスパッチをラップされたオブジェクトに委任する独自のクラスを設計します。ITypeInfoGetIDsOfNamesInvokeIDispatchCUIAutomationIUnknown
それを実装するモジュールへのパスを見つけ、次の手順を使用することで、少なくともCUIAutomationコクラスの型情報を読み込むことに成功しました。これはすべて Windows レジストリにあります。LoadTypeLib
(注:呼び出しが成功したかどうかをチェックするアサーションがあります(S_OKまたはERROR_SUCCESSなどと比較することにより-成功のコードに依存します)が、簡潔にするために、スニペットでのエラーチェックを省略します-呼び出しが戻りをチェックしない場合説明されているように、値の周りには常にアサーションがあります)
/// Return zero if and only if successful
int LoadTypeInfo(LPOLESTR szCLSID, ITypeInfo * * ppTypeInfo) {
HKEY hRegKeyCLSIDs;
RegOpenKeyEx(HKEY_CLASSES_ROOT, "CLSID", 0, KEY_READ, &hRegKeyCLSIDs); /// Only need to do this once through application lifetime, but here for context
HKEY hRegKeyCLSID;
RegOpenKeyEx(hRegKeyCLSIDs, szCLSID , 0, KEY_READ, &hRegKeyCLSID);
BYTE data[MAX_PATH];
DWORD cbData = sizeof(data);
RegGetValueW(hRegKeyCLSID, L"InprocServer32", NULL, RRF_RT_REG_SZ, NULL, data, &cbData);
ITypeLib * pTypeLib;
LoadTypeLib((LPOLESTR)data, &pTypeLib);
return (pTypeLib->GetTypeInfoOfGuid(CLSID, ppTypeInfo) == S_OK);
}
委任DispatchProxyクラスは次のように設計されています。
class DispatchProxy: public IDispatch {
private:
IUnknown * pUnknown;
ITypeInfo * pTypeInfo;
public:
DispatchProxy(IUnknown * pUnknown, ITypeInfo * pTypeInfo): pUnknown(pUnknown), pTypeInfo(pTypeInfo) {
/// `pUnknown` is the object that doesn't implement `IDispatch` and `pTypeInfo` is the type information for objects like what `pUnknown` points to.
}
/// Omitting `AddRef` and `Release` -- these are rather standard.
HRESULT STDMETHODCALLTYPE DispatchProxy::QueryInterface(REFIID riid, void * * ppvObject) {
if(ppvObject == nullptr) {
return E_POINTER;
}
else
if(riid == IID_IUnknown || riid == IID_IDispatch) {
*ppvObject = this;
((IUnknown *)*ppvObject)->AddRef();
return S_OK;
}
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
/// NOT returning any type information -- explanation below, if you're surprised
HRESULT STDMETHODCALLTYPE DispatchProxy::GetTypeInfoCount(UINT * pctinfo) {
*pctinfo = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE DispatchProxy::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo ** ppTInfo) {
if(iTInfo != 0) return DISP_E_BADINDEX;
_ASSERTE(*ppTInfo == NULL);
return E_NOTIMPL; /// Even though type information for the object being delegated to, is available, obviously, I am unsure whether it technically is valid for `DispatchProxy`, which may have a completely different, incompatible, layout. Granted, `E_NOTIMPL` isn't part of the contract for this method, but like I said -- I am unsure about this one.
}
HRESULT STDMETHODCALLTYPE DispatchProxy::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) {
return pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId); /// Returns S_OK, all good. Also tried `DispGetIDsOfNames(pTypeInfo, rgszNames, cNames, rgDispId)` with same result
}
HRESULT STDMETHODCALLTYPE DispatchProxy::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) {
return pTypeInfo->Invoke(pUnknown, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); /// Fails with `E_NOTIMPL`. Also tried `DispInvoke(pUnknown, pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr)` with same result
}
};
これに関連して、スクリプトがオブジェクトCUIAutomation(スクリプト) でメソッドを呼び出す前に、クラスのオブジェクトのようなオブジェクトへの参照を取得する方法が必要です。VBScript の関数や当時の Internet ExplorerのようcreateObjectに、「グローバル」実装オブジェクトでメソッドを公開することにより、スクリプトが指定された CLSID の COM オブジェクトを作成できるようにします。指定された CLSID で識別される COM クラスのオブジェクトを作成するために使用します。IDispatchCreateObjectnew ActiveXObject(progID)CoCreateInstance
HRESULT Global::CreateObject(VARIANT * pvCLSID, VARIANT * pvResult) {
_ASSERTE(V_VT(pvCLSID) == VT_BSTR);
CLSID CLSID;
CLSIDFromString(V_BSTR(pvCLSID), &CLSID);
IUnknown * pUnknown;
CoCreateInstance(CLSID, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, &pUnknown);
IDispatch * pDispatch;
HRESULT hResult = pUnknown->QueryInterface(&pDispatch);
if(hResult != S_OK) {
_ASSERTE(hResult == E_NOINTERFACE);
ITypeInfo * pTypeInfo;
if(LoadTypeInfo(V_BST(pvCLSID), &pTypeInfo)) { /// No type information was available -- not much choice but to return the created object as `IUnknown`
V_VT(pvResult) = VT_UNKNOWN;
V_UNKNOWN(pvResult) = pUnknown;
return S_OK;
} else {
pDispatch = new DispatchProxy(pUnknown, pTypeInfo);
}
}
if(pvResult) {
V_VT(pvResult) = VT_DISPATCH;
V_DISPATCH(pvResult) = pDispatch;
}
return S_OK;
}
スクリプトはCUIAutomationオブジェクトを作成し、次のように新しいDispatchProxyラッピングへの参照を取得できます。
uiautomation = createObject("{ff48dba4-60ef-4201-aa87-54103eef594e}");
GetRootElementその後、オブジェクトのメソッド (ここでは ) を呼び出すことができるはずです。
uiautomation.GetRootElement(/* parameters */);
残念ながら、pTypeInfo->Invokeすべての中心にある呼び出しが返されますE_NOTIMPL。それが当面の課題です。
実装されていないものとその理由は? メンバー ID ( dispIdMember) は前に書いたものと一致しpTypeInfo->GetIDsOfNames、後者は を返すS_OKので、少なくともそれによると、メンバー ID は有効です。パラメータの形式もそれとは何の関係もないと思います-pTypeInfo->Invokeもしそうなら、呼び出しから別のエラーコードが予想されます。
write as type information count を作成し、結果として書き込みをGetTypeInfoCount行っても、後続の呼び出しの結果には影響しません。それでも失敗します。1pTypeInfoGetTypeInfoITypeInfo::Invoke
また、コクラス自体の情報ではなく、元のコクラスオブジェクトで取得した実際のIUIAutomation インターフェイスの型情報 (pTypeInfoDefaultInterface以下のスニペット) を使用してみました。 ITypeInfoITypeInfo::Invoke
HREFTYPE hRefType;
pTypeInfo->GetRefTypeOfImplType(0, &hRefType);
ITypeInfo * pTypeInfoDefaultInterface;
pTypeInfo->GetRefTypeInfo(hRefType, &pTypeInfoDefaultInterface);
インターフェイスまたはコクラスの型情報が使用されているかどうかに関係なく、効果は同じです -- をITypeInfo::Invoke返しますE_NOTIMPL。
私は何を間違っていますか?COM やディスパッチに関する重要な情報を見逃していませんか? また、どのような型情報が役立ちますか? 私は IDL ファイルを作成しません。これDispatchProxyは COM サーバーの一部ではなく、アプリケーションの厳密な内部クラスです。Visual C++ で確認できる仮想関数テーブルを確認し、型情報についても調査を行いGetFuncDescました。入力内容はしっかりしているように見えます。名前とパラメーターの型と数がすべて揃っています。私が呼び出そうとしているすべての予想されるメソッド。ポインターは有効で、使用可能です。
少なくともGetRootElement、オブジェクトへのポインターへのポインターを期待し、そのようなタイプのパラメーターを渡すことさえできないスクリプトからそのようなメソッドをディスパッチすることが原因である可能性があることを認めます。しかし、ドキュメントによると、ITypeInfo::InvokeおそらくE_INVALIDARGorDISP_E_EXCEPTIONを返す必要があります。
私も をCreateStdDispatchいじってみましたが、2 つのことが気になりました。CreateStdDispatch次に、どこからどのディスパッチが行われ、どのポインターがどの引数として使用されるのか正確にはわかりません。ここでの慣用的な代替案でない限り、それは私の実際の質問ではないと思いますが、それが私の場合に役立つ場合は、それが正確に何をし、どのようにプラグインするかについての説明を得るためです.