11

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

与往常一样,该文档非常糟糕。到目前为止我读到的:

  • 将方法调用委托IDispatchITypeInfo给 some是更好的做法。这个对吗?
  • 如果是这样,如何得到一个ITypeInfo自己?LoadTypeLib()和家族(接着看ITypeLib::GetTypeInfo())?
  • 如果没有,它是如何正确实施的?指向高质量文档和独立示例的链接非常有用。

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

4

3 回答 3

9

实现 IDispatch 可能容易或困难。(假设您不能使用 ATL)。

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

那么你所要支持的就是GetIDsOfNamesand Invoke。它本质上只是一个大查找表。

对于GetIDsOfNamesDISP_E_UNKNOWNNAME如果 则返回cNames != 1。你不会支持参数名称。然后你只需要rgszNames[0]在你的名字到ID的映射中查找。

最后,实现调用。忽略除 pDispParams 和 pVarResult 之外的所有内容。使用 VariantChangeType 将参数强制为您期望的类型,并传递给您的实现。设置返回值并返回。完毕。

困难的方法是使用 ITypeInfo 和所有这些。我从来没有做过,也不会。ATL 让它变得简单,所以只需使用 ATL。

如果选择困难的方式,祝你好运。

于 2013-10-08T16:46:37.960 回答
5

如果接口在 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 呢?本质上,您可以通过以下方式获得它:

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

您可以做的是使用类型库

如果你有一个,那是你不必做的一件事。如果您没有,那么您可以使用MIDL 编译器创建一个。这是 Platform SDK 附带的免费工具。当然,在这种情况下,这意味着您将不得不编写一个 IDL 文件(这可能是很多工作,但您只需要定义您想要的内容)。根据您所针对的 COM 对象的类型,IDL 文件可能已经在 SDK 中可用。一旦准备好 IDL,就可以编译它并取回 TLB 文件

一旦你有了那个 TLB 文件,你就可以使用LoadTypeLib 函数来加载它。一旦有了ITypeLib引用,就可以加载所需的ITypeInfo(可能不止一次),并且基本上将 IDispatch 调用(GetIDsOfNames 等)路由到 ITypeInfo 实现调用中,因为它们非常相似。

于 2013-10-08T17:19:13.543 回答