最新公告
  • 欢迎您光临波比源码,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • ATL实现一个组件多个dual接口,multidisp

    最近想自己写个按键精灵的插件,因而接触到这个问题: 怎样在1个组件里实现两个自动化接口

    主要针对的ATL,MFC貌似没这个问题,具体MFC是怎样实现的自己没有深究。

    按键精灵的插件会在1个组件里实现两个dispinterface,具体请看oleview工具截图:

    刚开始对这个问题不理解,以为不是问题,自己用ATL尝试了几次,才发现不是那末回事,因而google之。

    MSDN上是这么说的,看这里

    ATL不提供任作甚将多个两重接口支持。IDispatch的单个实现。 但是,有几个已知的方法来手动合并接口,如创建包括创建1个新的对象,履行 QueryInterface 函数或使用嵌套的对象1个基于typeinfo的实现的单独 IDispatch 接口来创建 IDispatch 接口的模板选件类。

    这些方法都有潜伏的命名空间冲突问题,和代码复杂性和可保护性。 建议不要创建多个双绑定接口。

    虽然ATL不支持,但是上面也说了,还是有方法的,因而再google之,终究找到1篇相干问题的文章,里面说的很细,还提供了几种不同的方案:

    网址:https://www.sellsbrothers.com/posts/details/12657

    自己比较喜欢第2和第3种方案,对照来讲,第3种方案比较容易理解和实现。

    固然我是用的第3种方案的简单实现,没有从typeinfo接口再继承,还是自己实现了1个类,代理其实接口的IDispatch调用,空话不说了,上代码:

    #ifndef _XMULTIDISPIMPL_H_
    #define _XMULTIDISPIMPL_H_
    #include <atlcom.h>

    #define INTERFACE_MASK 0xFFFF0000UL
    #define DISPID_MASK 0x0000FFFFUL

    template<class tihclass = CComTypeInfoHolder>
    struct _TIH_ENTRY {
    tihclass *ptih; // 类型库指针,实现IDispatch调用
    DWORD dispEncode; // 函数调用id编码,在GetIdsOfNames函数中对返回的dispid进行编码,尝试解决dispid重复的问题
    DWORD offset; // 接口虚函数表偏移,IDispatchImpl<…>
    };

    template <class T, class tihclass = CComTypeInfoHolder>
    class ATL_NO_VTABLE XMultiDispImpl: public IDispatch
    {
    public:
    typedef _TIH_ENTRY<tihclass> TIH_ENTRY;

    public:
    STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
    {
    //TODO: 斟酌是不是按多个类型库处理
    *pctinfo = 1;
    return S_OK;
    }
    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
    {
    //TODO: 斟酌是不是按多个类型库处理
    T* pT = static_cast<T*> (this);
    TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
    if (pEntry->ptih)
    {
    // 默许返回第1个接口的类型库
    return pEntry->ptih->GetTypeInfo(itinfo, lcid, pptinfo);
    }
    return E_FAIL;
    }
    STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames,
    UINT cNames, LCID lcid, DISPID* rgdispid)
    {
    // NOTE: 函数名字不能冲突,
    // 名字相同时按顺序查找接口映照表中的接口,
    // 返回第1个匹配的接口函数对应的dispid
    T* pT = static_cast<T*> (this);
    TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
    HRESULT hr = DISP_E_UNKNOWNNAME;
    while (pEntry->ptih != NULL)
    {
    hr = pEntry->ptih->GetIDsOfNames(riid, rgszNames,
    cNames, lcid, rgdispid);
    if (SUCCEEDED(hr))
    {
    for (UINT i = 0; i < cNames; i++)
    {
    rgdispid[i] |= pEntry->dispEncode;
    }
    return hr;
    }
    else if (hr != DISP_E_UNKNOWNNAME)
    {
    return hr;
    }
    pEntry++;
    }
    return DISP_E_UNKNOWNNAME;
    }

    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
    LCID lcid, WORD wFlags, DISPPARAMS* pdispparams,
    VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
    UINT* puArgErr)
    {
    T* pT = static_cast<T*> (this);
    TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
    HRESULT hr = DISP_E_MEMBERNOTFOUND;
    if (dispidMember & INTERFACE_MASK)
    {
    // 函数id是编码过的,查找对应的接口进行调用,1般是脚本1类的动态调用
    while (pEntry->ptih != NULL)
    {
    if (pEntry->dispEncode == (dispidMember & INTERFACE_MASK))
    {
    // 找到接口,调用并退出
    hr = pEntry->ptih->Invoke((IDispatch*)(((DWORD)pT)+pEntry->offset),
    (dispidMember & DISPID_MASK), riid, lcid,
    wFlags, pdispparams, pvarResult,
    pexcepinfo, puArgErr);
    return hr;
    }
    pEntry++;
    }
    }
    else
    {
    // 函数id未编码,逐一接口进行尝试,1般是VC生成的接口类进行的静态调用
    // NOTE: 不同的接口,如果存在dispid相同的函数,
    // 请保证其函数参数个数或参数类型或返回值类型不要相同,
    // 否则可能会调用到毛病的接口函数
    while (pEntry->ptih != NULL)
    {
    hr = pEntry->ptih->Invoke((IDispatch*)(((DWORD)pT)+pEntry->offset),
    dispidMember, riid, lcid,
    wFlags, pdispparams, pvarResult,
    pexcepinfo, puArgErr);
    if (SUCCEEDED(hr))
    {
    // 调用成功退出
    return hr;
    }
    pEntry++;
    }
    }
    return DISP_E_MEMBERNOTFOUND;
    }
    };

    // 映照表宏定义,需要在组件的头文件中援用
    #define BEGIN_MULTI_DISPATCH_MAP(CLS)
    typedef CLS theDerived;
    static theDerived::TIH_ENTRY* GetTypeInfoHolder() {
    const DWORD _dwCnt = __COUNTER__;
    static theDerived::TIH_ENTRY pDispEntries[] = {

    // 函数id编码,占用id的高16位bit
    #define MULTI_DISPATCH_ENCODE() (((DWORD)(__COUNTER__) – _dwCnt) << 16)

    #define MULTI_DISPATCH_ENTRY(theBase)
    { &theBase::_tih, MULTI_DISPATCH_ENCODE(), offsetofclass(theBase, theDerived) },

    #define END_MULTI_DISPATCH_MAP()
    { NULL, 0UL, 0UL } };
    return(pDispEntries); }

    #endif // sentry

    使用方法,在组件类的头文件中,让我们的组件继承我们的类:

    class ATL_NO_VTABLE CQMPlugin :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CQMPlugin, &CLSID_QMPlugin>,
    public ISupportErrorInfo,
    <span style="color:#3366ff;">public XMultiDispImpl<CQMPlugin>,</span>
    public IDispatchImpl<IQMPlugin, &IID_IQMPlugin, &LIBID_zdLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispatchImpl<IQMPluginStandard, &IID_IQMPluginStandard, &LIBID_zdLib, /*wMajor =*/ 1, /*wMinor =*/ 0>

    蓝色是要手动添加的代码

    在BEGIN_COM_MAP和END_COM_MAP中添加以下代码:

    BEGIN_COM_MAP(CQMPlugin)
    <span style="color:#3366ff;">COM_INTERFACE_ENTRY2(IDispatch, XMultiDispImpl<CQMPlugin>)</span>
    COM_INTERFACE_ENTRY(IQMPlugin)
    COM_INTERFACE_ENTRY(IQMPluginStandard)
    COM_INTERFACE_ENTRY(ISupportErrorInfo)
    END_COM_MAP()

    意思是说当外部程序查询IDispatch接口,返回我们实现的类的虚函数表

    下面就是要添加接口映照表了,目前感觉这里还是看着不是很爽,暂时没有解决办法:

    <span style="color:#3366ff;">typedef IDispatchImpl<IQMPlugin, &IID_IQMPlugin, &LIBID_zdLib, /*wMajor =*/ 1, /*wMinor =*/ 0> TQMPlugin;
    typedef IDispatchImpl<IQMPluginStandard, &IID_IQMPluginStandard, &LIBID_zdLib, /*wMajor =*/ 1, /*wMinor =*/ 0> TQMPluginStandard;
    BEGIN_MULTI_DISPATCH_MAP(CQMPlugin)
    MULTI_DISPATCH_ENTRY(TQMPlugin)
    MULTI_DISPATCH_ENTRY(TQMPluginStandard)
    END_MULTI_DISPATCH_MAP()</span>

    记住要先typedef 再用MULTI_DISPATCH_ENTRY,不然会编译失败,这也是让人不爽的地方。

    其他的可以按正常的ATLCOM接口开发步骤进行开发了。

    下面就是注意事项了:

    1. 如果要在1个组件里实现多个disp接口,对每一个接口的方法或属性,不要出现重名的情况,代码中有说明;

    2. 函数的dispid可以相同,但是如果dispid相同,请1定让两个函数的参数个数,参数类型或返加值类型不要全部相同,不然可能调用到毛病的接口函数;

    3. 理论上这个类实现的多接口是支持静态调用和动态调用的

    4. 对dispid相同的情况,代码是通过在dispid的高16bit设置标志还区分的,对VBS1类的动态脚本调用是没有问题的,在脚本里可以把组件当做只实现了1个接口

    5. 由于使用的dispid的高16bit,所以这个类最多支持65536个接口,同时每一个接口的方法和属性不能超过65536个,有需要的可以自行在代码里调剂。

    最后希望代码能帮助到大家,没有甚么比自己的代码被他人认可更让人。。。,找不到形容词了,欢迎大定留言哈。

    波比源码 – 精品源码模版分享 | www.bobi11.com
    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

    波比源码 » ATL实现一个组件多个dual接口,multidisp

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    波比源码
    一个高级程序员模板开发平台
    升级波友尊享更多特权立即升级