纯技术讨论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);
这个这个宾馆的房间有多豪华就是你宾馆的星级,当然你可以通过重载这个函数来定制你的房间设施。
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控件。
LPCTSTR lpszWindowName, DWORD dwStyle, const POINT * ppt,
const SIZE * psize, UINT nID, CFile * pPersist = NULL, BOOL bStorage = FALSE,
我们有时候看见这样的创建方式: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的,所以我的派生一下:
{
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();
}
class
CMyRichEditCtrl :
public
CRichEditCtrl
{
friend class CTestCtrlInRichEditDlg;
DECLARE_DYNAMIC(CMyRichEditCtrl)
public:
CWnd m_wndCtrl;
BOOL CreateControlSite(COleControlContainer* pContainer,
COleControlSite** ppSite, UINT /**//* nID */, REFCLSID /**//* clsid */);
} ;
实现之:
{
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;
}
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 = { 20, 20 } ;
SIZE sz = { 300, 300 } ;
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拉。上面反映了一个控件入住一个窗体的过程。
COleControlSite * pControlSite = NULL;
m_wndRichCtrl.CreateControlSite(m_wndRichCtrl.GetControlContainer(), & pControlSite, 102 , CLSID_NULL /**/ /*CLSID_WebBrowser*/ );
POINT pt = { 20, 20 } ;
SIZE sz = { 300, 300 } ;
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();
效果: