首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现COM IDispatch而不使用ATL

实现COM IDispatch而不使用ATL
EN

Stack Overflow用户
提问于 2013-10-08 16:39:02
回答 3查看 6.6K关注 0票数 11

我正在编写一个Excel服务器实现,我被困在实现IDispatch的coclass的样板上。我没有访问ATL的权限,但我使用的是ActiveQt,尽管我也对如何在原始C或C++中这样做感兴趣。如何在COM服务器中正确实现IDispatch方法?

和往常一样,这些文档太可怕了。我到目前为止读到的是:

  • ITypeInfo进行更好的实践。这是正确的吗?
  • 如果是这样的话,如何给我自己一个ITypeInfoLoadTypeLib()和家人(然后是ITypeLib::GetTypeInfo())?
  • 如果没有,它是如何正确实施的?链接到高质量的文档和独立的例子是非常有用的。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-10-08 22:13:24

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

代码语言:javascript
复制
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对象的脚本可调用包装器,以绕过脚本安全限制。

那么你从哪里得到的ITypeInfo呢?从本质上说,你是通过:

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

Stack Overflow用户

发布于 2013-10-08 16:46:37

实现IDispatch可能是容易的,也可能是困难的。(假设您不能使用ATL)。

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

那么,您所需要支持的就是GetIDsOfNamesInvoke。从本质上说,这只是一个大的查找表。

对于GetIDsOfNames,返回DISP_E_UNKNOWNNAME如果cNames != 1。你不会支持参数名的。然后,您只需在名称到ids的映射中查找rgszNames[0]即可。

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

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

如果选择困难的道路,祝你好运。

票数 9
EN

Stack Overflow用户

发布于 2013-10-08 17:19:13

您可以做的是使用类型库

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

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19253471

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档