在没有ATL的情况下实现COM IDispatch

时间:2013-10-08 16:39:03

标签: c++ c qt com

我正在编写一个Excel RTD服务器实现,我被困在一个实现IDispatch的coclass的样板上。我无法访问ATL,但我使用的是ActiveQt,虽然我对如何在原始C或C ++中执行此操作感兴趣。如何在COM服务器中正确实现IDispatch方法?

文档和往常一样令人恐惧。我到目前为止所读到的内容:

LoadTypeLib()方法似乎适合COM 客户端来获取某些库的类型信息,而不是尝试内省自身的COM服务器。我是对的吗?

3 个答案:

答案 0 :(得分:6)

实施IDispatch既简单又难。 (假设你不能使用ATL)。

简单的方法是不支持TypeInfo(从GetTypeInfoCount返回0,从E_NOTIMPL返回GetTypeInfo。没有人应该调用它。)。

然后您需要支持的是GetIDsOfNamesInvoke。它本质上只是一个很大的查找表。

对于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?基本上你通过以下方式得到它:

  1. 编写一个IDL文件,将您的界面声明为界面。它必须是双接口,因为ITypeInfo实现知道调用哪个函数 - 它不能直接在类上调用C ++函数,因为C ++没有反射,因为它是语言中立的。因此,它只能将Invoke调用委托给类型库中声明的另一个方法。
  2. 将IDL编译为头文件并在构建过程中键入库
  3. 从IDL生成的头文件定义了实现类必须继承的接口。一旦你实施了所有方法,你就可以了。 (对于开发,首先让它们全部返回E_NOTIMPL,然后逐个实现它们)
  4. 将类型库安装到目标目录,或者作为EXE / DLL中的资源。需要通过致电RegisterTypeLib进行注册。如果它是作为资源嵌入的,您应该从DllRegisterServer实现中调用它。
  5. 使用LoadTypeLib创建对象的第一个实例时加载类型库。这会为您提供ITypeLib
  6. 使用GetTypeInfoOfGuid
  7. 获取所需的ITypeInfo

答案 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实现调用中,因为它们非常相似。