我正在编写一个Excel RTD服务器实现,我被困在一个实现IDispatch
的coclass的样板上。我无法访问ATL,但我使用的是ActiveQt,虽然我对如何在原始C或C ++中执行此操作感兴趣。如何在COM服务器中正确实现IDispatch
方法?
文档和往常一样令人恐惧。我到目前为止所读到的内容:
IDispatch
method calls to some ITypeInfo
更好的做法。这是对的吗?ITypeInfo
给自己? LoadTypeLib()和家人(然后查看ITypeLib::GetTypeInfo()
)? LoadTypeLib()
方法似乎适合COM 客户端来获取某些库的类型信息,而不是尝试内省自身的COM服务器。我是对的吗?
答案 0 :(得分:6)
实施IDispatch既简单又难。 (假设你不能使用ATL)。
简单的方法是不支持TypeInfo(从GetTypeInfoCount
返回0,从E_NOTIMPL
返回GetTypeInfo
。没有人应该调用它。)。
然后您需要支持的是GetIDsOfNames
和Invoke
。它本质上只是一个很大的查找表。
对于GetIDsOfNames
,如果DISP_E_UNKNOWNNAME
,请返回cNames != 1
。你不会支持参数名称。然后,您只需在名称到ID的映射中查找rgszNames[0]
。
最后,实现Invoke。忽略除pDispParams和pVarResult之外的所有内容。使用VariantChangeType将参数强制转换为您期望的类型,并传递给您的实现。设置返回值并返回。完成。
困难的方法是使用ITypeInfo以及所有这些。我从来没有这样做过,也不愿意。 ATL让它变得简单,所以只需使用ATL。
如果采取艰难的方式,祝你好运。
答案 1 :(得分:4)
如果在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);
}
}
我使用类似的方法来创建a script-callable wrapper for MSHTML DOM objects以绕过脚本安全限制。
那么从哪里获取ITypeInfo?基本上你通过以下方式得到它:
ITypeInfo
实现知道调用哪个函数 - 它不能直接在类上调用C ++函数,因为C ++没有反射,因为它是语言中立的。因此,它只能将Invoke
调用委托给类型库中声明的另一个方法。E_NOTIMPL
,然后逐个实现它们)RegisterTypeLib
进行注册。如果它是作为资源嵌入的,您应该从DllRegisterServer
实现中调用它。LoadTypeLib
创建对象的第一个实例时加载类型库。这会为您提供ITypeLib
GetTypeInfoOfGuid
。答案 2 :(得分:3)
您可以使用Type Library。
如果你有一个,这是你不必做的一件事。如果您没有,则可以使用MIDL compiler创建一个。这是Platform SDK附带的免费工具。当然在这种情况下,这意味着您将不得不编写一个IDL文件(这可能是很多工作,但您只需要定义您想要的内容)。根据您要定位的COM对象的类型,SDK中可能已经提供了IDL文件。准备好IDL后,您可以编译它并get back a TLB file。
拥有该TLB文件后,您可以使用LoadTypeLib function加载它。获得ITypeLib引用后,您可以加载所需的ITypeInfo(可能不止一次),并基本上将IDispatch调用(GetIDsOfNames等)路由到ITypeInfo实现调用中,因为它们非常相似。