MFC的容器站点控件模型
背景知识 VC向导里面有一个MFC ActiveX项,我们可以使用它来创建ActiveX控件,ActiveX技术是OLE技术的延伸,微软早期推出OLE技术不是非常成功,于是修改了名字以抹去人们对OLE的阴影。ActiveX技术在现在应用非常广泛,它以COM思想为基础,以MFC技术实现,使得开发人员可以快速创建组件功能模块应用于Windows平台任何语言。我们在使用ActiveX控件的时候
背景知识
VC向导里面有一个MFC ActiveX项,我们可以使用它来创建ActiveX控件,ActiveX技术是OLE技术的延伸,微软早期推出OLE技术不是非常成功,于是修改了名字以抹去人们对OLE的阴影。
ActiveX技术在现在应用非常广泛,它以COM思想为基础,以MFC技术实现,使得开发人员可以快速创建组件功能模块应用于Windows平台任何语言。
我们在使用ActiveX控件的时候(本文限于VC讨论)很简单,直接拖放控件或者通过CWnd::CreateControl创建。如果仅限于此,可以说能够很好的使用ActiveX控件,然而无法对控件实现定制。为什么要定制?我不想解释太多,因为有些东西意会而不能言传。用Spy看一下TT浏览器、遨游浏览器就可以知道它的内核就是IE的WebBrowser,它们的实现其实就是对WebBrowser的定制。如何才能进行定制?当然必须了解控件创建机制,了解应用与控件之间的模型,了解MFC背后为我们做了什么。
我以前一向很少使用ActiveX控件,原因很简单,我不了解内部技术,不是不会用,而是不能随心所欲的用。我一方面致力于软件组件化,一方面又对ActiveX控件如此畏惧,自己内心其实都很矛盾。随着知识的积累,慢慢终于有能力去了解这些东西,我希望自己可以讲明白,也希望有人渴望了解,尽管是老技术。
牵扯到的类汇总
牵扯到ActiveX控件创建的有这些MFC类:COccManager、COleControlContainer、COleControlSite、COleControl。下面分别解释一下这些类:
COccManager:control container manager控件容器管理器,任何应用程序若要支持AxtiveX控件必须创建该对象,创建位置一般在×××App: InitInstance()函数里面AfxEnableControlContainer()。为容纳控件窗体创建COleControlContainer。
COleControlContainer:控件容器类,容纳ActiveX控件的窗体都创建该对象,用来创建COleControlSite对象以登录控件信息。
COleControlSite:COleControlContainer为每个ActiveX控件创建一个COleControlSite对象以登录控件信息。
COleControl:所有ActiveX控件从其派生。
对象创建流程分析
下面介绍这些对象的创建流程,通过分析可以清楚一个ActiveX控件创建细节:
控件容器管理对象位于应用程序级,如果应用支持ActiveX控件,那么会在应用初始化的时候创建一个管理器,MFC缺省实现:
{
if (pOccManager == NULL)
afxOccManager = _afxOccManager.GetData();
else
afxOccManager = pOccManager;
}
到这里有了控件管理器,这个应用就算支持ActiveX控件。下面来看看当ActiveX控件创建的时候发生了什么。
假设一个ActiveX控件创建采用如下形式(归根到底也应该如此)(OCCCONT.CPP ):
const POINT * ppt, const SIZE * psize, CWnd * pParentWnd, UINT nID,
CFile * pPersist, BOOL bStorage, BSTR bstrLicKey)
{
ASSERT(pParentWnd != NULL);
#ifdef _DEBUG
if (afxOccManager == NULL)
{
TRACE0("Warning: AfxEnableControlContainer has not been called yet./n");
TRACE0(">>> You should call it in your app's InitInstance function./n");
}
#endif
if ((pParentWnd == NULL) || !pParentWnd->InitControlContainer())
return FALSE;
return pParentWnd->m_pCtrlCont->CreateControl(this, clsid, lpszWindowName,
dwStyle, ppt, psize, nID, pPersist, bStorage, bstrLicKey);
}
{
TRY
{
if (m_pCtrlCont == NULL)
m_pCtrlCont = afxOccManager->CreateContainer(this);
}
END_TRY
// Mark all ancestor windows as containing OLE controls.
if (m_pCtrlCont != NULL)
{
CWnd* pWnd = this;
while ((pWnd != NULL) && !(pWnd->m_nFlags & WF_OLECTLCONTAINER))
{
pWnd->m_nFlags |= WF_OLECTLCONTAINER;
pWnd = pWnd->GetParent();
if (! (GetWindowLong(pWnd->GetSafeHwnd(), GWL_STYLE) & WS_CHILD))
break;
}
}
return (m_pCtrlCont != NULL);
}
接着父窗口的容器对象调用 CreateControl函数,看看它的实现过程(OCCSITE.CPP ):
LPCTSTR lpszWindowName, DWORD dwStyle, const POINT * ppt, const SIZE * psize,
UINT nID, CFile * pPersist, BOOL bStorage, BSTR bstrLicKey,
COleControlSite ** ppNewSite)
{
COleControlSite* pSite = NULL;
TRY
{
pSite = afxOccManager->CreateSite(this);
}
END_TRY
if (pSite == NULL)
return FALSE;
BOOL bCreated = SUCCEEDED( pSite->CreateControl(pWndCtrl, clsid,
lpszWindowName, dwStyle, ppt, psize, nID, pPersist, bStorage,
bstrLicKey ) );
if (bCreated)
{
ASSERT(pSite->m_hWnd != NULL);
m_siteMap.SetAt(pSite->m_hWnd, pSite);
if (ppNewSite != NULL)
*ppNewSite = pSite;
}
else
{
delete pSite;
}
return bCreated;
}
afxOccManager负责为COleControlContainer创建COleControlSite对象,有COleControlSite对象创建具体ActiveX控件,由于COleControlSite:: CreateControl代码较长,此处不赘述,有兴趣可以自己看看,是ControlSite将ActiveX控件定位的过程。
为了便于大家理解,我根据自己理解绘制一个创建过程:
应用示例
上面介绍了创建流程,只谈这些你可能不明白到底有什么好处。这里示例还是我以前的一个例子:
使MFC变漂亮二:MFC与HTML交互示例
WebBrowser控件通过IDocHostUIHandler接口处理UI以及一些交互事件,我们可以定制自己的COleControlSite实现自定义行为,要创建自定义的COleControlSite对象就必须实现自定义的COccManager。因此例子中派生了两个类,分别实现定制COccManager、COleControlSite,当然你也可以创建自定义的COleControlContainer对象以在创建COleControlSite对象时为其提供某种服务。
不知道讲清楚没有,反正我是又糊涂了,^_^。难得糊涂!如果你想对Office有深入了解、希望应用集成VBA开发,这些知识都是必不可少。OLE技术还是ActiveX技术,我分不清,需要了解的太多,慢慢来。
更多推荐
所有评论(0)