纯技术讨论com控件创建过程

纯技术讨论com控件创建过程

用一个没有任何意义的例子来看看com控件的创建过程。又是狠狠忙了一阵子。前几天,一个网上的朋友找到我,他想在richedit里面插入一个ax控件,这个倒是很简单,网上也很多。接下来,他想要在richedit里面插入一个窗口,当然无论ax控件是否设置m_bWindowOnly都无法达到需求。我给他的答案是不行,其实我不知道该如何解释这个问题。不管需求如何怪异,也不管是否解决了他的问题,这里我单纯来强行插入一个窗口到richedit,不关心插入进去是否可以正常工作。声明,这是一个没有意义的事情,只是想说明com控件创建过程。

一个支持com控件的窗口必须创建CWnd的COleControlContainer* m_pCtrlCont;  // for containing OLE controls,通过BOOL InitControlContainer();函数,它又会调用虚函数virtual BOOL CreateControlContainer(COleControlContainer** ppContainer);你可以重载来创建你自己的Container。而这个冬冬就好比一个宾馆,每个控件好比一个人,有人生自由,可以入住任何一个宾馆,但是一旦寄宿在一家宾馆,这个宾馆必须为之提供一个房间,这是ControlSite的概念,创建调用:
virtual BOOL CreateControlSite(COleControlContainer *  pContainer, 
      COleControlSite
**  ppSite, UINT nID, REFCLSID clsid);
这个这个宾馆的房间有多豪华就是你宾馆的星级,当然你可以通过重载这个函数来定制你的房间设施。
再通过:
virtual HRESULT CreateControl(CWnd *  pWndCtrl, REFCLSID clsid,
        LPCTSTR lpszWindowName, DWORD dwStyle, 
const  POINT *  ppt,
      
const  SIZE *  psize, UINT nID, CFile *  pPersist = NULL, BOOL bStorage = FALSE,
创建com控件。

我们有时候看见这样的创建方式:CWnd::CreateControl()来创建com控件。这其中经历了父窗口初始化ControlContainer,准备一个ControlSite,把com控件创建并安排到此房间并做一些初始化操作,然后把com控件窗口句柄(如果存在)依附到CWnd,这里CWnd充当的是一个与控件通信的代理。可以看出是否创建有窗口还是无窗口的,依赖CreateControl内部操作。再控件里面,我们看到控件实现了这样两个接口:
COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
 COM_INTERFACE_ENTRY(IOleInPlaceObject)
他们接口大同小异,一个是有窗口方式一个是无窗口方式,为什么这么做是历史原因,因为窗口耗费大量资源。在不深究下去,有兴趣可以找点老古典来瞅瞅。

下面开始我的创建过程:
我想实现在RichEdit里面创建一个房间(ControlSite)来容纳一个web控件,由于COleControlSite不让我new出来,所以我的继承一个CMyOleControlSite:
class  CMyOleControlSite :  public  COleControlSite
{
public:
    CMyOleControlSite(COleControlContainer
* pCtrlCont) : COleControlSite(pCtrlCont) {}

protected:
    
// Implementation
    LPUNKNOWN GetInterfaceHook(const void *iid);
    
// IUnknown methods
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
}
;

LPUNKNOWN CMyOleControlSite::GetInterfaceHook(
const   void   * iid)
{
    
if (IsEqualIID((REFIID) (*(IID*)iid), IID_IDocHostUIHandler))
        
return (IDocHostUIHandler *this;
    
return NULL;
}


//  IUnknown methods
STDMETHODIMP CMyOleControlSite::QueryInterface(REFIID riid,  void   ** ppvObject)
{
    
return COleControlSite::ExternalQueryInterface(&riid, ppvObject);
}

STDMETHODIMP_(ULONG) CMyOleControlSite::AddRef()
{
    
return ExternalAddRef();
}


STDMETHODIMP_(ULONG) CMyOleControlSite::Release()
{
    
return ExternalRelease();
}
我要创建自己的ControlSite,而CreateControlSite是protect的,所以我的派生一下:
class  CMyRichEditCtrl :  public  CRichEditCtrl
{
    friend 
class CTestCtrlInRichEditDlg;
    DECLARE_DYNAMIC(CMyRichEditCtrl)
public:
    CWnd m_wndCtrl;

    BOOL CreateControlSite(COleControlContainer
* pContainer, 
        COleControlSite
** ppSite, UINT /**//* nID */, REFCLSID /**//* clsid */);
}
;
实现之:
IMPLEMENT_DYNAMIC(CMyRichEditCtrl, CRichEditCtrl)

BOOL CMyRichEditCtrl::CreateControlSite(COleControlContainer
*  pContainer, 
                                   COleControlSite
**  ppSite, UINT  /**/ /* nID */ , REFCLSID  /**/ /* clsid */ )
{
    
if(ppSite == NULL)
    
{
        ASSERT(FALSE);
        
return FALSE;
    }


    CMyOleControlSite 
*pOleSite = 
        
new CMyOleControlSite(pContainer);

    
*ppSite = pOleSite;
    (
*ppSite)->m_pCtrlCont = pContainer;
    
return TRUE;
}

在对话框的OnInitDialog函数里面:
m_wndRichCtrl.InitControlContainer();

    COleControlSite
*  pControlSite  =  NULL;
    m_wndRichCtrl.CreateControlSite(m_wndRichCtrl.GetControlContainer(), 
& pControlSite,  102 , CLSID_NULL /**/ /*CLSID_WebBrowser*/ );
    POINT pt 
=   2020 } ;
    SIZE sz 
=   300300 } ;
    pControlSite
-> CreateControl( & m_wndRichCtrl.m_wndCtrl, CLSID_WebBrowser,
        _T(
" ^_^ " ), WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP,  & pt,  & sz,  102 );
    COleControlSiteOrWnd
*  pSiteOrWnd  =   new  COleControlSiteOrWnd(pControlSite);
    m_wndRichCtrl.GetControlContainer()
-> m_listSitesOrWnds.AddTail(pSiteOrWnd);
    IWebBrowser2
*  pIWebBrowser  =  NULL;
    pControlSite
-> m_pInPlaceObject -> QueryInterface(IID_IWebBrowser2, ( void ** ) & pIWebBrowser);
    pIWebBrowser
-> Navigate2( & CComVariant( " www.cppblog.com/wlwlxj " ), NULL, NULL, NULL, NULL);
    pIWebBrowser
-> Release();
    IOleControl
*  pIOleCtrl  =  NULL;
    pControlSite
-> m_pInPlaceObject -> QueryInterface(IID_IOleControl, ( void ** ) & pIOleCtrl);
    pIOleCtrl
-> Release();
这样就ok拉。上面反映了一个控件入住一个窗体的过程。

效果:
纯技术讨论com控件创建过程_第1张图片


纯技术讨论com控件创建过程_第2张图片

你可能感兴趣的:(纯技术讨论com控件创建过程)