如果接口在 IDL 中正确定义并编译到类型库中,IDispatch
则通过类型库实现ITypeInfo
是非常可行的,因为它主要是委托。有趣的部分是ITypeInfo::Invoke
它依赖于正确的 C++ v-table 布局:
public class CComClass: public IDualInterface
{
// ...
// implementing IDualInterface
ITypeInfo* m_pTypeInfo // can be obtained via ITypeLib::GetTypeInfoOfGuid for the GUID of IDualInterface
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT* pctinfo)
{
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{
if (0 != itinfo)
return E_INVALIDARG;
(*pptinfo = m_pTypeInfo)->AddRef();
return S_OK;
}
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
{
return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
}
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
return m_pTypeInfo->Invoke(static_cast<IDualInterface*>(this), dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
}
}
我使用了类似的方法为 MSHTML DOM 对象创建了一个可脚本调用的包装器,以绕过脚本安全限制。
那么你从哪里得到 ITypeInfo 呢?本质上,您可以通过以下方式获得它:
- 编写一个 IDL 文件,将您的接口声明为双接口。它必须是一个双重接口,因为这就是
ITypeInfo
实现知道调用哪个函数的方式——它不能直接在你的类上调用 C++ 函数,因为 C++ 没有反射并且因为它是语言中立的。因此它只能将Invoke
调用委托给类型库中声明的另一个方法。
- 作为构建过程的一部分,将 IDL 编译为头文件和类型库
- 从 IDL 生成的头文件定义了实现类必须继承的接口。一旦你实现了所有的方法,你就可以开始了。(对于开发,首先让它们全部返回
E_NOTIMPL
,然后一一实施)
- 将类型库安装到目标目录或作为 EXE/DLL 中的资源。它需要通过调用来注册
RegisterTypeLib
。如果它作为资源嵌入,您应该从您的DllRegisterServer
实现中调用它。
- 在创建对象的第一个实例时加载类型库,使用
LoadTypeLib
. 这给你一个ITypeLib
- 使用 .获取您需要的 ITypeInfo
GetTypeInfoOfGuid
。