用一个没有任何意义的例子来看看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 = { 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拉。上面反映了一个控件入住一个窗体的过程。
效果:
posted on 2007-07-07 00:59
万连文 阅读(3185)
评论(2) 编辑 收藏 引用 所属分类:
MFC