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

2022-01-14 00:00:00 qt com c c++

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

I am writing an Excel RTD server implementation and I'm stuck on the boilerplate for a coclass which implements IDispatch. I have no access to ATL, but I am using ActiveQt, although I'm interested in how to do this in raw C or C++ too. How to properly implement the IDispatch methods in a COM server?

文档一如既往地糟糕透顶.到目前为止我读过的内容:

The documentation is just panickingly awful, as always. What I have so far read:

  • 最好委派IDispatch 方法调用一些 ITypeInfo.这是正确的吗?
  • 如果是这样,如何获得一个 ITypeInfo 给自己?LoadTypeLib() 和家族(后跟正在查看 ITypeLib::GetTypeInfo())?
  • 如果不是,它是如何正确实施的?指向优质文档和独立示例的链接非常有用.
  • It is better practice to delegate the IDispatch method calls to some ITypeInfo. Is this correct?
  • If so, how to get an ITypeInfo to myself? LoadTypeLib() and family (followed by looking at ITypeLib::GetTypeInfo())?
  • If not, how is it implemented properly? Links to good-quality documentation and self-contained examples are of much use.

LoadTypeLib() 方法似乎适合 COM client 获取某些库的类型信息,而不适合尝试自省的 COM 服务器.我说的对吗?

The LoadTypeLib() approach seems appropriate for a COM client to reach type information for some library, not for a COM server trying to introspect itself. Am I correct?

推荐答案

如果接口在IDL中正确定义并编译成类型库,通过类型库的ITypeInfo实现IDispatch 非常可行,因为它主要是委托.有趣的部分是 ITypeInfo::Invoke 它依赖于正确的 C++ v-table 布局:

If the interface is properly defined in the IDL and compiled into a type library, implementing IDispatch via the type library's ITypeInfo is quite feasible as it's mostly delegating. The interesting part is ITypeInfo::Invoke which relies upon correct C++ v-table layout:

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 对象的可脚本调用包装器,以绕过脚本安全限制.

I've used a similar approach to create a script-callable wrapper for MSHTML DOM objects to bypass scripting security restrictions.

那么你从哪里得到 ITypeInfo 呢?基本上你可以通过:

So where do you get the ITypeInfo from? Essentially you get it by:

  1. 编写一个 IDL 文件,将您的接口声明为 双 接口.它必须是双接口,因为 ITypeInfo 实现就是这样知道要调用哪个函数 - 它不能直接在你的类上调用 C++ 函数,因为 C++ 没有反射,而且它是语言中立的.因此它只能将 Invoke 调用委托给类型库中声明的另一个方法.
  2. 在构建过程中将 IDL 编译为头文件和类型库
  3. 从 IDL 生成的头文件定义接口,您的实现类必须从该接口继承.一旦你实现了所有的方法,你就可以开始了.(对于开发,首先让它们都返回 E_NOTIMPL 然后一个一个地实现它们)
  4. 将类型库安装到目标目录或作为 EXE/DLL 中的资源.它需要通过调用 RegisterTypeLib 进行注册.如果它作为资源嵌入,您应该从 DllRegisterServer 实现中调用它.
  5. 在创建对象的第一个实例时加载类型库,使用 LoadTypeLib.这会给你一个 ITypeLib
  6. 使用 GetTypeInfoOfGuid 获取您需要的 ITypeInfo.
  1. Write an IDL file which declares your interface as a dual interface. It has to be a dual interface, as that is how the ITypeInfo implementation knows which function to invoke - it cannot just invoke the C++ functions directly on your class because C++ has no reflection and because it is language neutral. Therefore it can only delegate the Invoke call to another method declared in the type library.
  2. Compile the IDL to a header file and type library as part of the build process
  3. The header file produced from the IDL defines the interface, which your implementing class must inherit from. Once you have implemented all the methods you are good to go. (For development start by making them all return E_NOTIMPL then implement them one by one)
  4. Install the Type Library, either to the destination directory, or as a resource in the EXE/DLL. It will need to be registered by calling RegisterTypeLib. If it is embedded as a resource, you should call this from your DllRegisterServer implementation.
  5. Load the type library when the first instance of your object is created, using LoadTypeLib. This gives you an ITypeLib
  6. Get the ITypeInfo you need using GetTypeInfoOfGuid.

相关文章