Chinaunix首页 | 论坛 | 博客
  • 博客访问: 194899
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2017-08-23 16:48
文章分类

全部博文(90)

文章存档

2015年(1)

2011年(21)

2010年(59)

2009年(9)

我的朋友

分类: C/C++

2010-07-14 09:54:38

这一步很需要一些COM基础,因为没有这个基础的话,可能会有为什么要做这个,有必要吗,之类的疑问的。这回我们要看看双接口,为MFC设计的控件添加双接口(双接口是什么就不解释了)。
这里参考了msdn中的例子acdual,并且应用了例子里面的一些宏,这个例子可以直接搜索msdn找到,还有TN065: Dual-Interface Support for OLE Automation Servers参考。
老是用一个例子,也有些腻了,咱新来一例子,TDual控件
好,开始:
1.新建MFC控件工程,这里添加了一个属性Hello(在控件上显示Hello属性对应的字符串),一个方法SayHello,弹出一消息框,显示方法参数中的字符串。
2.改造接口,
添加第二个接口
    [ uuid(C3180013-EB23-4e8f-924C-38F5A201D3D8),
      helpstring("Double interface"),
      oleautomation,
      dual ]
    interface ITDual : IDispatch
    {
        [propput, id(1)] HRESULT Hello([in] BSTR newText);
        [propget, id(1)] HRESULT Hello([out, retval] BSTR* ret);
        [id(2)] HRESULT SayHello(BSTR strHello);
    }
双接口的接口描述中必须有oleautomation和dual属性,而且接口必须派生自IDispatch。
这里的接口定义很象ATL中的IDL定义,如果用ATL设计过控件的话,应该不陌生的,具体的规则就不罗嗦了,如果不清楚的话,请自行查找资料了,大家可以和MFC中原来的接口定义对照一下:
        properties:
            // NOTE - ClassWizard will maintain property information here.
            //    Use extreme caution when editing this section.
            //{{AFX_ODL_PROP(CTDualCtrl)
            [id(1)] BSTR Hello;
            //}}AFX_ODL_PROP
        methods:
            // NOTE - ClassWizard will maintain method information here.
            //    Use extreme caution when editing this section.
            //{{AFX_ODL_METHOD(CTDualCtrl)
            [id(2)] void SayHello(BSTR strHello);
            //}}AFX_ODL_METHOD
定义好了新的接口后,需要在coclass描述中添加新接口的一个引用,如下:
    [ uuid(A1E75855-8561-4416-BE49-97B4D9A228E2),
      helpstring("TDual Control"), control ]
    coclass TDual
    {
//        [default] dispinterface _DTDual;
//        [default, source] dispinterface _DTDualEvents;
        [default] interface ITDual;
        [default, source] dispinterface _DTDualEvents;
        dispinterface _DTDual;
    };
这里将缺省的接口设置为了ITDual而不是原来的dispinterface _DTDual。
3.改造好了接口后,就要实现这个新的接口了。MFC使用嵌套类来实现一个接口,得到嵌套类的方法是使用BEGIN_INTERFACE_PART/END_INTERFACE_PART宏对,接着具体实现嵌套类的相应接口函数(包括IUnknown和IDispatch的接口函数)就可以了。
这里用了acdul例子中定义的BEGIN_DUAL_INTERFACE_PART和END_DUAL_INTERFACE_PART宏对和DELEGATE_DUAL_INTERFACE宏来简化实现IUnknown和IDispatch中的接口函数。这些宏的定义将在最后列出。
在TDualCtl.h中,#include "mfcdual.h"
添加新接口PART,如下:
// Event maps
    //{{AFX_EVENT(CTDualCtrl)
    //}}AFX_EVENT
    DECLARE_EVENT_MAP()
//此处为添加部分
public:
/////////////////////////////////////////////////////////////////////////////
// Double Interface Part
    DECLARE_INTERFACE_MAP()
    BEGIN_DUAL_INTERFACE_PART(TDual, ITDual)
        STDMETHOD(put_Hello)(THIS_ BSTR newText);
        STDMETHOD(get_Hello)(THIS_ BSTR FAR* ret);
        STDMETHOD(SayHello)(THIS_ BSTR strHello);
    END_DUAL_INTERFACE_PART(TDual)

在TDualCtl.cpp中
/////////////////////////////////////////////////////////////////////////////
// Control type information
static const DWORD BASED_CODE _dwTDualOleMisc =
    OLEMISC_ACTIVATEWHENVISIBLE |
    OLEMISC_SETCLIENTSITEFIRST |
    OLEMISC_INSIDEOUT |
    OLEMISC_CANTLINKINSIDE |
    OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CTDualCtrl, IDS_TDUAL, _dwTDualOleMisc)
//添加AddRef,QueryInterface等通用函数的实现
DELEGATE_DUAL_INTERFACE(CTDualCtrl, TDual)
......
//这两个函数是原来Dispatch接口分发时要用到的Hello属性和SayHello方法函数
void CTDualCtrl::OnHelloChanged()
{
    // TODO: Add notification handler code
    InvalidateControl();
    SetModifiedFlag();
}
void CTDualCtrl::SayHello(LPCTSTR strHello)
{
    // TODO: Add your dispatch handler code here
    AfxMessageBox(strHello);
}
//这三个函数是新添加的接口实现的函数,一般实现还是调用原接口的函数,这里加上一个描述,表示是从vtable中调用的。
STDMETHODIMP CTDualCtrl::XTDual::put_Hello(BSTR newText)
{
    METHOD_PROLOGUE(CTDualCtrl, TDual)
    // MFC automatically converts from Unicode BSTR to
    // Ansi CString, if necessary...
    CString str = newText;
    pThis->m_hello = "vtable:" + str;
    pThis->OnHelloChanged();
    return NOERROR;
}
STDMETHODIMP CTDualCtrl::XTDual::get_Hello(BSTR* ret)
{
    METHOD_PROLOGUE(CTDualCtrl, TDual)
    // MFC automatically converts from Ansi CString to
    // Unicode BSTR, if necessary...
    pThis->m_hello.SetSysString(ret);
    return NOERROR;
}
STDMETHODIMP CTDualCtrl::XTDual::SayHello(BSTR strHello)
{
    METHOD_PROLOGUE(CTDualCtrl, TDual)
    // MFC automatically converts from Ansi CString to
    // Unicode BSTR, if necessary...
    CString str = strHello;
    str = "I'm vtable SayHello\n" + str;
    pThis->SayHello(str);
    return NOERROR;
}
4.连接新的接口到MFC的QueryInterface接口表机制,使能够通过QueryInterface获得ITDual接口。
在.h中添加
public:
/////////////////////////////////////////////////////////////////////////////
// Double Interface Part
    DECLARE_INTERFACE_MAP()
    BEGIN_DUAL_INTERFACE_PART(TDual, ITDual)
        STDMETHOD(put_Hello)(THIS_ BSTR newText);
        STDMETHOD(get_Hello)(THIS_ BSTR FAR* ret);
        STDMETHOD(SayHello)(THIS_ BSTR strHello);
    END_DUAL_INTERFACE_PART(TDual)

在.cpp中添加
/////////////////////////////////////////////////////////////////////////////
// Event map
BEGIN_EVENT_MAP(CTDualCtrl, COleControl)
    //{{AFX_EVENT_MAP(CTDualCtrl)
    // NOTE - ClassWizard will add and remove event map entries
    //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_EVENT_MAP
END_EVENT_MAP()
//将新的接口连接到QueryInterface机制
BEGIN_INTERFACE_MAP(CTDualCtrl, COleControl)
    INTERFACE_PART(CTDualCtrl, IID_IDispatch, Dispatch)
    INTERFACE_PART(CTDualCtrl, DIID__DTDual, Dispatch)
    INTERFACE_PART(CTDualCtrl, IID_ITDual, TDual)
END_INTERFACE_MAP()
5.这时编译的话,会提示错误,因为没有ITDual接口声明和一些IID的定义。点击TDual.odl右键,弹出设置菜单,选择Setting菜单项,在Output header file name中键入你想要输出的接口.h文件名,这里用的是ITDual.h,然后将ITDual.h包括在stdafx.h中,以便可以在各处用到。
接下来新建一个initIIDs.cpp文件,加上下面代码
#include
#include
#include "ITDual.h"
这个initIIDs.cpp没什么用处,只是用来实现要用到的几个IID的,不过要在Project-Setting中把initIIDs.cpp的Precompiled Headers设置为Not using precompiled headers。
编译一下TDual.odl,OK了
6.添加异常处理和自动化错误接口,这部分本例未提供,有兴趣的可以参考acdual例子。
7.新建一个普通的MFC对话框exe工程testdual,在工程中引入TDual.ocx,这里用的方法是在stdafx.h中加入#import "TDual.ocx" no_namespace。
为CTestdualDlg添加成员变量CWnd m_wnd,响应WM_CREATE消息,添加如下代码
    CRect rect(10, 10, 100, 100);
    BOOL b = m_wnd.CreateControl(__uuidof( TDual ), NULL, WS_CHILD | WS_VISIBLE, rect, this, 1);
    if(!b){
        AfxMessageBox("Can't CreateControl");
        return 0;
    }
    LPUNKNOWN pukn = m_wnd.GetControlUnknown();
    if(!pukn){
        AfxMessageBox("Can't get IUnknown interface");
        return 0;
    }
    ITDual* pdual = NULL;
    pukn->QueryInterface(__uuidof(ITDual), (void**)&pdual);
    if(!pdual){
        AfxMessageBox("Can't get ITDual interface");
        return 0;
    }
    CString str = "Thanks";
    pdual->SayHello(str.AllocSysString());
    pdual->put_Hello(str.AllocSysString());
    pdual->Release();
编译运行,具体结果就不说了,一看便知。
因为本文主要讲述如何做,至于为什么要如此做就不说了,有兴趣的朋友可以自己研究,这里提供几个网址,仅供参考

http://blog.csdn.net/RedStar81/archive/2003/04/03/19761.aspx

下面是acdual例子中的一些宏定义代码
#define BEGIN_DUAL_INTERFACE_PART(localClass, baseClass) \
    BEGIN_INTERFACE_PART(localClass, baseClass) \
       STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo); \
       STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); \
       STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid); \
       STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr); \
/////////////////////////////////////////////////////////////////////
// END_DUAL_INTERFACE_PART is just like END_INTERFACE_PART. It
// is only added for symmetry...
#define END_DUAL_INTERFACE_PART(localClass) \
    END_INTERFACE_PART(localClass) \
 
/////////////////////////////////////////////////////////////////////
// DELEGATE_DUAL_INTERFACE expands to define the standard IDispatch
// methods for a dual interface, delegating back to the default
// MFC implementation
#define DELEGATE_DUAL_INTERFACE(objectClass, dualClass) \
    STDMETHODIMP_(ULONG) objectClass::X##dualClass::AddRef() \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        return pThis->ExternalAddRef(); \
    } \
    STDMETHODIMP_(ULONG) objectClass::X##dualClass::Release() \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        return pThis->ExternalRelease(); \
    } \
    STDMETHODIMP objectClass::X##dualClass::QueryInterface( \
        REFIID iid, LPVOID* ppvObj) \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        return pThis->ExternalQueryInterface(&iid, ppvObj); \
    } \
    STDMETHODIMP objectClass::X##dualClass::GetTypeInfoCount( \
        UINT FAR* pctinfo) \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE); \
        ASSERT(lpDispatch != NULL); \
        return lpDispatch->GetTypeInfoCount(pctinfo); \
    } \
    STDMETHODIMP objectClass::X##dualClass::GetTypeInfo( \
        UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE); \
        ASSERT(lpDispatch != NULL); \
        return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo); \
    } \
    STDMETHODIMP objectClass::X##dualClass::GetIDsOfNames( \
        REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, \
        LCID lcid, DISPID FAR* rgdispid) \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE); \
        ASSERT(lpDispatch != NULL); \
        return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames, \
                                         lcid, rgdispid); \
    } \
    STDMETHODIMP objectClass::X##dualClass::Invoke( \
        DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, \
        DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, \
        EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) \
    { \
        METHOD_PROLOGUE(objectClass, dualClass) \
        LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE); \
        ASSERT(lpDispatch != NULL); \
        return lpDispatch->Invoke(dispidMember, riid, lcid, \
                                  wFlags, pdispparams, pvarResult, \
                                  pexcepinfo, puArgErr); \
    } \
 
/////////////////////////////////////////////////////////////////////
// TRY_DUAL and CATCH_ALL_DUAL are used to provide exception handling
// for your dual interface methods. CATCH_ALL_DUAL takes care of
// returning the appropriate error code.
#define TRY_DUAL(iidSource) \
    HRESULT _hr = S_OK; \
    REFIID  _riidSource = iidSource; \
    TRY \
#define CATCH_ALL_DUAL \
    CATCH(COleException, e) \
    { \
        _hr = e->m_sc; \
    } \
    AND_CATCH_ALL(e) \
    { \
        AFX_MANAGE_STATE(pThis->m_pModuleState); \
        _hr = DualHandleException(_riidSource, e); \
    } \
    END_CATCH_ALL \
    return _hr; \
/////////////////////////////////////////////////////////////////////
// DualHandleException is a helper function used to set the system's
// error object, so that container applications that call through
// VTBLs can retrieve rich error information
HRESULT DualHandleException(REFIID riidSource, const CException* pAnyException);
/////////////////////////////////////////////////////////////////////
// DECLARE_DUAL_ERRORINFO expands to declare the ISupportErrorInfo
// support class. It works together with DUAL_ERRORINFO_PART and
// IMPLEMENT_DUAL_ERRORINFO defined below.
#define DECLARE_DUAL_ERRORINFO() \
    BEGIN_INTERFACE_PART(SupportErrorInfo, ISupportErrorInfo) \
        STDMETHOD(InterfaceSupportsErrorInfo)(THIS_ REFIID riid); \
    END_INTERFACE_PART(SupportErrorInfo) \
/////////////////////////////////////////////////////////////////////
// DUAL_ERRORINFO_PART adds the appropriate entry to the interface map
// for ISupportErrorInfo, if you used DECLARE_DUAL_ERRORINFO.
#define DUAL_ERRORINFO_PART(objectClass) \
    INTERFACE_PART(objectClass, IID_ISupportErrorInfo, SupportErrorInfo) \
/////////////////////////////////////////////////////////////////////
// IMPLEMENT_DUAL_ERRORINFO expands to an implementation of
// ISupportErrorInfo which matches the declaration in
// DECLARE_DUAL_ERRORINFO.
#define IMPLEMENT_DUAL_ERRORINFO(objectClass, riidSource) \
    STDMETHODIMP_(ULONG) objectClass::XSupportErrorInfo::AddRef() \
    { \
        METHOD_PROLOGUE(objectClass, SupportErrorInfo) \
        return pThis->ExternalAddRef(); \
    } \
    STDMETHODIMP_(ULONG) objectClass::XSupportErrorInfo::Release() \
    { \
        METHOD_PROLOGUE(objectClass, SupportErrorInfo) \
        return pThis->ExternalRelease(); \
    } \
    STDMETHODIMP objectClass::XSupportErrorInfo::QueryInterface( \
        REFIID iid, LPVOID* ppvObj) \
    { \
        METHOD_PROLOGUE(objectClass, SupportErrorInfo) \
        return pThis->ExternalQueryInterface(&iid, ppvObj); \
    } \
    STDMETHODIMP objectClass::XSupportErrorInfo::InterfaceSupportsErrorInfo( \
        REFIID iid) \
    { \
        METHOD_PROLOGUE(objectClass, SupportErrorInfo) \
        return (iid == riidSource) ? S_OK : S_FALSE; \
    }
 
 
阅读(1608) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~