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

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;
}
解释一下:如果传入NULL,MFC自动创建默认管理器,否则接管用户定义的管理器,这个地方是有意思的,我们可以在这个根部替换管理器从而替换容器对象的创建,进而定制站点。默认管理器依靠一个宏定义(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代码较长,此处不赘述,有兴趣可以自己看看,是ControlSite将ActiveX控件定位的过程。

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

应用示例

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

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

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐