我正在编写一个Excel RTD服务器实现,我被困在一个实现的coclass的样板上 IDispatch
。我无法访问ATL,但我使用的是ActiveQt,虽然我对如何在原始C或C ++中执行此操作感兴趣。如何正确实施 IDispatch
COM服务器中的方法?
一如既往,文档非常糟糕。到目前为止我所读的内容如下:
该 LoadTypeLib()
方法似乎适合COM 客户 获取某些库的类型信息,而不是尝试内省自己的COM服务器。我对么?
如果接口在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
。
实现IDispatch可能很容易或很难。 (假设你不能使用ATL)。
简单的方法是不支持TypeInfo(从0开始返回0) GetTypeInfoCount
和 E_NOTIMPL
从 GetTypeInfo
。没人应该叫它。)
那你需要支持的就是 GetIDsOfNames
和 Invoke
。它本质上只是一个很大的查找表。
对于 GetIDsOfNames
,回来 DISP_E_UNKNOWNNAME
如果 cNames != 1
。你不会支持参数名称。然后你只需要查找 rgszNames[0]
在你的名称到ID的映射。
最后,实现Invoke。忽略除pDispParams和pVarResult之外的所有内容。使用VariantChangeType将参数强制转换为您期望的类型,并传递给您的实现。设置返回值并返回。完成。
困难的方法是使用ITypeInfo以及所有这些。我从来没有这样做过,也不愿意。 ATL让它变得简单,所以只需使用ATL即可。
如果采取艰难的方式,祝你好运。
你能做的就是用a 类型库。
如果你有一个,这是你不必做的一件事。如果你没有,那么你可以使用 MIDL编译器。这是Platform SDK附带的免费工具。当然在这种情况下,这意味着你必须编写一个IDL文件(这可能是很多工作,但你只需要定义你想要的东西)。根据您要定位的COM对象的类型,SDK中可能已经提供了IDL文件。准备好IDL之后,就可以编译它了 获取TLB文件。
获得该TLB文件后,可以使用 LoadTypeLib函数。一旦你有了 的ITypeLib 参考,你可以加载 的ITypeInfo 您需要(可能不止一次),并且基本上将IDispatch调用(GetIDsOfNames等)路由到ITypeInfo实现调用中,因为它们非常相似。