技术开发 频道

.Net2.0工作线程和MFC8.0托管支持

    在使用MFC的.Net容器类和用户控件来计算亲和数时,需要在.Net控件的Load事件之前给控件传递一个计算范围参数。在MFC中,.Net控件是以ActiveX控件的形式被创建的,但是在传统的窗口初始化函数CWinFormsView::OnInitialUpdate被执行之前,控件的Load事件已经被调用了,这使得参数的传递更加困难。.Net控件是在窗口的Create方法中被创建的:

BOOL CWinFormsView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) ...{ m_nFlags |= WF_ISWINFORMSVIEWWND; BOOL ok=__super::Create(lpszClassName, lpszWindowName, dwStyle, rect,pParentWnd, nID, pContext); ASSERT(ok); ok=ok && m_control.CreateManagedControl(m_pManagedViewType,WS_VISIBLE, rect, this,nID); ASSERT(ok); .. return ok; }

    但是在CWinFormsControl::CreateManagedControl返回之前,Load事件就已经被触发了。在Load事件中设置一个断点,在执行时查看调用堆栈发现,这个事件是COleControlSite::CreateControlCommon中的下面语句调用的

// control is visible: just activate it hr = DoVerb(OLEIVERB_INPLACEACTIVATE);

    也就是说,为了在这之前调用自定义代码,需要自定义一个COleControlSite类。在MFC6.0中,COleControlSite是一个未文档化的类,但是在微软知识库文章Q236312和Q329802中描述了自定义全局ActiveX控件站点的方法。从MFC7.0开始,COleControlSite被文档化了,而且可以通过重载CWnd::CreateControlSite来使用自定义控件站点类。这个方法在MFC7.0中被用来扩展CHTMLView和CDHTMLDialog,而且在MFC8.0中被用来将默认的COleControlSite替换成CWinFormsControlSite以提供托管控件支持。同样的方法可以用来将CWinFormsControlSite替换成其派生类来自定义控件创建过程。

virtual BOOL CreateControlSite(COleControlContainer* pContainer, COleControlSite** ppSite, UINT nID, REFCLSID clsid) { COleControlSite* pSite=NULL; if (InlineIsEqualGUID(clsid , CLSID_WinFormsControl)) { pSite=new CCAmicablesControlSite(this, pContainer); if(pSite) { *ppSite=pSite;return TRUE; } } return FALSE; }

    自定义控件创建过程中需要重载的函数是CWinFormsControlSite::CreateOrLoad。CWinFormsControlSite的成员函数并不多,只有这个函数是在控件的创建之前被调用,并且在控件的Load事件被触发之前结束执行。重载的函数只是简单的调用视图的一个成员函数,而由视图来负责进行具体的处理。 

class CCAmicablesControlSite:public CWinFormsControlSite { public: CCAmicablesControlSite(CAmicableNumbersView* pView, COleControlContainer* pCtrlCont) : CWinFormsControlSite(pCtrlCont) ,m_pView(pView) ...{} virtual HRESULT CreateOrLoad(const CControlCreationInfo& creationInfo)...{ HRESULT hr =CWinFormsControlSite::CreateOrLoad(creationInfo); if (SUCCEEDED(hr)) ...{ m_pView->OnControlCreated(); } return hr; } protected: CAmicableNumbersView* m_pView; }; void CAmicableNumbersView::OnControlCreated() { m_ViewControl = safe_cast<Amicable::AmicableNumberView ^>(this->GetControl()); m_ViewControl->Range = m_range; }

    和传统的视图不同,使用CWinFormsView时,不能在重载的PreCreateWindow函数中返回FALSE来终止视图的创建。这是因为CWinFormsView::Create没有对视图创建失败的情况进行处理,而是继续创建.Net控件而造成的。如果确实要终止视图的创建,那么可以重载CWinFormsView::Create,在这之前终止视图的创建。

BOOL CAmicableNumbersView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { // TODO: Add your specialized code here and/or call the base class //prompt for search range FormRange^ fr= gcnew FormRange(); fr->Range=m_range; if (fr->ShowDialog() == DialogResult::OK) { Amicable::CAmicableNumberRange range=fr->Range; m_range=range; } else return FALSE; return CWinFormsView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); }

    尽管可以使用MFC来给应用程序加上托管扩展,但是托管代码和非托管代码互操作造成的性能影响还是很明显。没有绝对必要的话,还是建议像上面调用FormRange表单那样直接调用托管的表单而不是使用MFC的托管支持。

0
相关文章