MFC 控件容器

来源:互联网 发布:学java有前景吗 编辑:程序博客网 时间:2024/06/01 10:31

背景知识
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缺省实现:

MFC 控件容器 - phpking - 努力+天赋=成功 AfxEnableControlContainer();
我们看下它的原型(AFXDISP.H ):
MFC 控件容器 - phpking - 努力+天赋=成功void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager=NULL);
它的实现如下(OCCMGR.CPP ):
MFC 控件容器 - phpking - 努力+天赋=成功void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager)
MFC 控件容器 - phpking - 努力+天赋=成功
{
MFC 控件容器 - phpking - 努力+天赋=成功    
if (pOccManager == NULL)
MFC 控件容器 - phpking - 努力+天赋=成功        afxOccManager 
= _afxOccManager.GetData();
MFC 控件容器 - phpking - 努力+天赋=成功    
else
MFC 控件容器 - phpking - 努力+天赋=成功        afxOccManager 
= pOccManager;
MFC 控件容器 - phpking - 努力+天赋=成功}
解释一下:如果传入NULL,MFC自动创建默认管理器,否则接管用户定义的管理器,这个地方是有意思的,我们可以在这个根部替换管理器从而替换容器对象的创建,进而定制站点。默认管理器依靠一个宏定义(OCCMGR.CPP )::
MFC 控件容器 - phpking - 努力+天赋=成功PROCESS_LOCAL(COccManager, _afxOccManager)
在此不深入,有兴趣可以看看。

到这里有了控件管理器,这个应用就算支持ActiveX控件。下面来看看当ActiveX控件创建的时候发生了什么。
假设一个ActiveX控件创建采用如下形式(归根到底也应该如此)(OCCCONT.CPP ):
MFC 控件容器 - phpking - 努力+天赋=成功BOOL CWnd::CreateControl(REFCLSID clsid, LPCTSTR lpszWindowName, DWORD dwStyle,
MFC 控件容器 - phpking - 努力+天赋=成功    
const POINT* ppt, const SIZE* psize, CWnd* pParentWnd, UINT nID,
MFC 控件容器 - phpking - 努力+天赋=成功   CFile
* pPersist, BOOL bStorage, BSTR bstrLicKey)
MFC 控件容器 - phpking - 努力+天赋=成功
{
MFC 控件容器 - phpking - 努力+天赋=成功    ASSERT(pParentWnd 
!= NULL);
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功#ifdef _DEBUG
MFC 控件容器 - phpking - 努力+天赋=成功    
if (afxOccManager == NULL)
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        TRACE0(
"Warning: AfxEnableControlContainer has not been called yet.\n");
MFC 控件容器 - phpking - 努力+天赋=成功        TRACE0(
">>> You should call it in your app's InitInstance function.\n");
MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功
#endif
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
if ((pParentWnd == NULL) || !pParentWnd->InitControlContainer())
MFC 控件容器 - phpking - 努力+天赋=成功        
return FALSE;
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
return pParentWnd->m_pCtrlCont->CreateControl(this, clsid, lpszWindowName,
MFC 控件容器 - phpking - 努力+天赋=成功        dwStyle, ppt, psize, nID, pPersist, bStorage, bstrLicKey);
MFC 控件容器 - phpking - 努力+天赋=成功}
看上面实现,由于已经有afxOccManager 了,继续向下看有InitControlContainer()函数,控件父窗口初始化容器(OCCCONT.CPP ):
MFC 控件容器 - phpking - 努力+天赋=成功BOOL CWnd::InitControlContainer()
MFC 控件容器 - phpking - 努力+天赋=成功
{
MFC 控件容器 - phpking - 努力+天赋=成功    TRY
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        
if (m_pCtrlCont == NULL)
MFC 控件容器 - phpking - 努力+天赋=成功            m_pCtrlCont 
= afxOccManager->CreateContainer(this);
MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功    END_TRY
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
// Mark all ancestor windows as containing OLE controls.
MFC 控件容器 - phpking - 努力+天赋=成功
    if (m_pCtrlCont != NULL)
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        CWnd
* pWnd = this;
MFC 控件容器 - phpking - 努力+天赋=成功        
while ((pWnd != NULL) && !(pWnd->m_nFlags & WF_OLECTLCONTAINER))
MFC 控件容器 - phpking - 努力+天赋=成功        
{
MFC 控件容器 - phpking - 努力+天赋=成功            pWnd
->m_nFlags |= WF_OLECTLCONTAINER;
MFC 控件容器 - phpking - 努力+天赋=成功            pWnd 
= pWnd->GetParent();
MFC 控件容器 - phpking - 努力+天赋=成功            
if (! (GetWindowLong(pWnd->GetSafeHwnd(), GWL_STYLE) & WS_CHILD))
MFC 控件容器 - phpking - 努力+天赋=成功                
break;
MFC 控件容器 - phpking - 努力+天赋=成功        }

MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
return (m_pCtrlCont != NULL);
MFC 控件容器 - phpking - 努力+天赋=成功}
这里看出加入没有容器对象,afxOccManager 则负责创建容器(每个窗体存在唯一容器对象),并且修改所有祖先窗口支持OLE风格。

接着父窗口的容器对象调用CreateControl函数,看看它的实现过程(OCCSITE.CPP ):
MFC 控件容器 - phpking - 努力+天赋=成功BOOL COleControlContainer::CreateControl(CWnd* pWndCtrl, REFCLSID clsid,
MFC 控件容器 - phpking - 努力+天赋=成功    LPCTSTR lpszWindowName, DWORD dwStyle, 
const POINT* ppt, const SIZE* psize,
MFC 控件容器 - phpking - 努力+天赋=成功   UINT nID, CFile
* pPersist, BOOL bStorage, BSTR bstrLicKey,
MFC 控件容器 - phpking - 努力+天赋=成功   COleControlSite
** ppNewSite)
MFC 控件容器 - phpking - 努力+天赋=成功
{
MFC 控件容器 - phpking - 努力+天赋=成功    COleControlSite
* pSite = NULL;
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    TRY
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        pSite 
= afxOccManager->CreateSite(this);
MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功    END_TRY
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
if (pSite == NULL)
MFC 控件容器 - phpking - 努力+天赋=成功        
return FALSE;
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    BOOL bCreated 
= SUCCEEDED( pSite->CreateControl(pWndCtrl, clsid,
MFC 控件容器 - phpking - 努力+天赋=成功        lpszWindowName, dwStyle, ppt, psize, nID, pPersist, bStorage,
MFC 控件容器 - phpking - 努力+天赋=成功      bstrLicKey ) );
MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
if (bCreated)
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        ASSERT(pSite
->m_hWnd != NULL);
MFC 控件容器 - phpking - 努力+天赋=成功        m_siteMap.SetAt(pSite
->m_hWnd, pSite);
MFC 控件容器 - phpking - 努力+天赋=成功        
if (ppNewSite != NULL)
MFC 控件容器 - phpking - 努力+天赋=成功            
*ppNewSite = pSite;
MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功    
else
MFC 控件容器 - phpking - 努力+天赋=成功    
{
MFC 控件容器 - phpking - 努力+天赋=成功        delete pSite;
MFC 控件容器 - phpking - 努力+天赋=成功    }

MFC 控件容器 - phpking - 努力+天赋=成功
MFC 控件容器 - phpking - 努力+天赋=成功    
return bCreated;
MFC 控件容器 - phpking - 努力+天赋=成功}

afxOccManager负责为COleControlContainer创建COleControlSite对象,有COleControlSite对象创建具体ActiveX控件,由于COleControlSite::CreateControl代码较长,此处不赘述,有兴趣可以自己看看,是ControlSite将ActiveX控件定位的过程。

为了便于大家理解,我根据自己理解绘制一个创建过程:
MFC 控件容器 - phpking - 努力+天赋=成功

应用示例

上面介绍了创建流程,只谈这些你可能不明白到底有什么好处。这里示例还是我以前的一个例子:
使MFC变漂亮二:MFC与HTML交互示例
WebBrowser控件通过IDocHostUIHandler接口处理UI以及一些交互事件,我们可以定制自己的COleControlSite实现自定义行为,要创建自定义的COleControlSite对象就必须实现自定义的COccManager。因此例子中派生了两个类,分别实现定制COccManager、COleControlSite,当然你也可以创建自定义的COleControlContainer对象以在创建COleControlSite对象时为其提供某种服务。

不知道讲清楚没有,反正我是又糊涂了,^_^。难得糊涂!如果你想对Office有深入了解、希望应用集成VBA开发,这些知识都是必不可少。OLE技术还是ActiveX技术,我分不清,需要了解的太多,慢慢来。

0 0
原创粉丝点击