MFC的容器站点控件模型

来源:互联网 发布:阿里云人工客服电话 编辑:程序博客网 时间:2024/06/03 17:11

http://blog.csdn.net/linlingzhao/article/details/7519201

背景知识
VC
向导里面有一个MFC ActiveX项,我们可以使用它来创建ActiveX控件,ActiveX技术是OLE技术的延伸,微软早期推出OLE技术不是非常成功,于是修改了名字以抹去人们对OLE的阴影。

ActiveX技术在现在应用非常广泛,它以COM思想为基础,以MFC技术实现,使得开发人员可以快速创建组件功能模块应用于Windows平台任何语言。
我们在使用ActiveX控件的时候(本文限于VC讨论)很简单,直接拖放控件或者通过CWnd::CreateControl创建。如果仅限于此,可以说能够很好的使用ActiveX控件,然而无法对控件实现定制。为什么要定制?我不想解释太多,因为有些东西意会而不能言传。用Spy看一下TT浏览器、遨游浏览器就可以知道它的内核就是IEWebBrowser,它们的实现其实就是对WebBrowser的定制。如何才能进行定制?当然必须了解控件创建机制,了解应用与控件之间的模型,了解MFC背后为我们做了什么。

我以前一向很少使用ActiveX控件,原因很简单,我不了解内部技术,不是不会用,而是不能随心所欲的用。我一方面希望软件组件化,一方面又对ActiveX控件如此畏惧,自己内心其实都很矛盾。随着知识的积累,慢慢终于有能力去了解这些东西,我希望自己可以讲明白,也希望有人渴望了解,尽管是老技术。

牵扯到的类汇总
牵扯到ActiveX控件创建的有这些MFC类:COccManagerCOleControlContainerCOleControlSiteCOleControl。下面分别解释一下这些类:
COccManager
control container manager控件容器管理器,任何应用程序若要支持AxtiveX控件必须创建该对象,创建位置一般在×××App: InitInstance()函数里面AfxEnableControlContainer()。为容纳控件窗体创建COleControlContainer
COleControlContainer
:控件容器类,容纳ActiveX控件的窗体都创建该对象,用来创建COleControlSite对象以登录控件信息。
COleControlSite
COleControlContainer为每个ActiveX控件创建一个COleControlSite对象以登录控件信息。
COleControl
:所有ActiveX控件从其派生。

对象创建流程分析
下面介绍这些对象的创建流程,通过分析可以清楚一个ActiveX控件创建细节:
控件容器管理对象位于应用程序级,如果应用支持ActiveX控件,那么会在应用初始化的时候创建一个管理器,MFC缺省实现:

AfxEnableControlContainer();

我们看下它的原型(AFXDISP.H )

void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager=NULL);

它的实现如下(OCCMGR.CPP )

void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager)
{
    
if (pOccManager == NULL)
        afxOccManager = _afxOccManager.GetData();
    
else
        afxOccManager = pOccManager;
}

解释一下:如果传入NULLMFC自动创建默认管理器,否则接管用户定义的管理器,这个地方是有意思的,我们可以在这个根部替换管理器从而替换容器对象的创建,进而定制站点。默认管理器依靠一个宏定义(OCCMGR.CPP )::

PROCESS_LOCAL(COccManager, _afxOccManager)

在此不深入,有兴趣可以看看。

到这里有了控件管理器,这个应用就算支持ActiveX控件。下面来看看当ActiveX控件创建的时候发生了什么。
假设一个ActiveX控件创建采用如下形式(归根到底也应该如此)(OCCCONT.CPP )

BOOL CWnd::CreateControl(REFCLSID clsid, LPCTSTR lpszWindowName, DWORD dwStyle,
    
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);
}

看上面实现,由于已经有afxOccManager 了,继续向下看有InitControlContainer()函数,控件父窗口初始化容器(OCCCONT.CPP )

BOOL CWnd::InitControlContainer()
{
    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);
}

这里看出加入没有容器对象,afxOccManager 则负责创建容器(每个窗体存在唯一容器对象),并且修改所有祖先窗口支持OLE风格。

接着父窗口的容器对象调用CreateControl函数,看看它的实现过程(OCCSITE.CPP )

BOOL COleControlContainer::CreateControl(CWnd* pWndCtrl, REFCLSID clsid,
    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代码较长,此处不赘述,有兴趣可以自己看看,是ControlSiteActiveX控件定位的过程。

为了便于大家理解,我根据自己理解绘制一个创建过程:

应用示例

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

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