應該使用哪個框架?用ATL和MFC來創建ActiveX控件3

下面的代碼顯示了MFCMsgTraffic控件是怎樣將它的顏色和時間間隔屬性保存起來的:

void CMFCMsgTrafficCtrl::DoPropExchange(CPropExchange* pPX)

{

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

PX_Color(pPX, "GraphLineColor", m_graphLineColor);

PX_Long(pPX, "GraphInterval", m_interval);

}

MFC包括了若干PX_函數在控件和存儲媒體間轉移數據,它們是:

PX_Short

PX_UShort

PX_Long

PX_ULong

PX_Color

PX_Bool

PX_String

PX_Currency

PX_Float

PX_Double

PX_Blob

PX_Font

PX_Picture

PX_IUnknown

PX_VBXFontConvert

PX_DataPath

在ATL中管理控件屬性持續涉及到兩個步驟。第一步是添加你希望客戶能夠使用的持續接口的ATL實現。ATL包括了類IPersistStorageImpl, IPersistStreamInitImpl, 和 IPersistPropertyBagImpl, 它們實現了三個主COM持續機制。

第二步是在控件的屬性映射中插入屬性。當一個客戶請求保存或者加載基於ATL的控件時,ATL檢查控件的屬性映射表,將控件的屬性輸出到存儲媒介,或者從存儲媒介輸入。屬性映射表是屬性名字、DISPIDs的一個表,有時還包括一個屬性頁面GUID。ATL遍歷詞表查找該持續哪個屬性,並將其持續到合適的媒體。圖5顯示了繼承了持續接口實現和一個屬性映射的ATLMsgTraffic控件。

屬性頁

ActiveX控件經常在開發者將控件放到各類容器時提供屬性頁幫助開發者。將消息流控件放入一個對話框的開發者可能想要配置控件的各個方面,象控件的取樣間隔,或者繪圖線條的顏色。例如,當控件放在一個對話框中,你想通過右擊鼠標得到控件的屬性時,Visual Studio顯示了一個突出的對話框。這裏將說明其工作過程。

Visual Studio請求控件在一個對話框框架裏顯示屬性頁(Visual Studio IspecifyPropertyPages接口請求控件提供一個屬性頁的清單),屬性頁顯示在Visual Studio中,但是通過控件提供的一個COM接口,保持與控件的連接。每當你完成了屬性編輯並從Visual Studio中關閉了對話框,它就會要求屬性頁更新控件中的屬性。

當你生成一個MFC的控件時,wizard給你一個對話框模板和一個從ColePropertyPage中派生的代表此控件的缺省屬性頁的類。Visual Studio使得實現一個控件的屬性和此屬性頁中的屬性的連接變得容易了。當你使用ControlWizard的Automation tab添加屬性到你的基於MFC的控件中的時候,你給了屬性一個外部名字。這個名字是外部客戶方(包括屬性頁)用來識別該屬性的。

你按照開發其它任何對話框的方法來開發屬性頁——將控件添加到對話框模板,將對話框成員變量和控件聯繫起來。ControlWizard添加DDX/DDV代碼在對話框控件和成員變量之間交換數據。然而,當你將成員變量和對話框控件相關聯時,ControlWizard給你提供了這樣一個機會,你可以將外部屬性名字用於對話框的成員變量。此外部名字是你給控件添加屬性時鍵入的字符串。

當屬性頁需要將變化應用於控件時(例如當按下Apply按鈕時),屬性頁使用控件的Idispatch接口以及外部名字來修改控件的屬性。在MFC中,你可以通過ClassWizard來添加一個新屬性,添加一個新的對話框模板到工程中,讓ClassWizard創建一個類——要確保是從ColePropertyPage中派生出來的類。然後,爲了使新的屬性頁可以被外界訪問到,將它的GUID添加到控件的屬性頁映射中(在控件的.CPP文件中查找BEGIN_ PROPPAGEIDS 和 END_PROPPAGEIDS兩個宏)。不象MFC的ActiveX ControlWizard,ATL COM App Wizard並不向DLL中添加缺省的屬性頁。這意味着你要自己完成此工作。幸運的是,又一個wizard可以向屬性頁中添加基於ATL的DLL。只要選擇Insert ATL Object,然後找到屬性頁對象。Wizard將一個對話框模板和一個C++類與所有必要的COM goo一起添加到一個屬性頁中。讓它們完成什麼工作是你的事情。

不幸的是,ATL屬性頁的wizard驅動特性不如基於MFC的屬性頁,你得手工完成應用和顯示操作。這就意味着提供Apply 和 Show的函數實現到你的屬性頁類中。Apply函數只是提取對話框中控件的狀態,遍歷屬性頁擁有的指向控件的接口指針列表,使用接口指針來修改控件屬性。Show函數通常提取控件的狀態,然後以次來組織對話框的控件。下面的代碼顯示了基於ATL的屬性頁是怎樣處理Apply函數的:

STDMETHOD(Apply)(void)

{

long nInterval = GetDlgItemInt(IDC_EDITINTERVAL);

ATLTRACE(_T("CMainPropPage::Apply/n"));

for (UINT i = 0; i < m_nObjects; i++)

{

IATLMsgTrafficCtl* pATLMsgTrafficCtl;

m_ppUnk[i]->QueryInterface(IID_IATLMsgTrafficCtl,

(void**)&pATLMsgTrafficCtl);

if(pATLMsgTrafficCtl) {

pATLMsgTrafficCtl->put_Interval(nInterval);

pATLMsgTrafficCtl->Release();

}

}

m_bDirty = FALSE;

return S_OK;

}

爲基於ATL的控件提供一個屬性頁的第二步是確保屬性頁的CLSID出現在控件的屬性映射中,圖5中列出的屬性持續代碼提供了它的一個例子。消息映射表明了控件的圖線顏色,被標準的顏色屬性頁管理。控件的取樣間隔由控件的主屬性頁來管理。

Window 消息

MFC和ATL在它們處理window消息方面有很多共同之處,都使用消息映射,都有wizards來生成代碼處理window消息。在MFC中,消息映射可以添加到任何一個CcmdTarget派生的類中,然後你就可以用ClassWizard來建立你的控件的事件處理器了。圖6顯示了基於MFC的控件怎樣處理WM_ TIMER消息。另外,MFC提供了處理命令和控件通告的宏。象MFC一樣,ATL通過消息映射來處理window消息,只要你的類是從CwindowImpl派生的,而且包含ATL的消息映射宏,你就可以使用類視來建立事件處理器。圖7顯示了ATL消息流控制是怎樣處理WM_TIMER消息的。

ATL使用MESSAGE_HANDLER宏將標準的window消息映射到一個C++類。此宏簡單的產生一個將window消息和類的成員函數關聯的表。除了常規消息,消息映射還可以處理其它類型的事件。圖8顯示了能參與消息映射的各種宏。

連接和事件

最後要進行的比較是MFC和ATL各是怎樣處理連接點和事件集的。爲了管理連接點和事件集,需要一個COM類來實現IconnectionPointContainer,然後創建一種提供指向IconnectionPoint的指針給客戶的方法。MFC的主控件類,ColeControl,已經有了內置的IconnectionPointContainer,MFC通過連接映射提供了連接點。MFC已經爲IPropertyNotifySink定義了連接點和控件的缺省事件集。

爲了完善一個基於MFC的控件的缺省事件集,你只要簡單的使用ClassWizard's ActiveX Event tab。在你使用ClassWizard添加事件的時候,Visual Studio更新你的控件的.ODL文件,爲潛在的包容器描述外出事件。另外,Visual Studio添加一個函數到你的類中,你可以調用它反過來向包容器激發事件。圖9演示了基於MFC的控件的事件觸發機制。基於MFC的控件的事件觸發函數只是由包容器在它和控件建立連接點時提供的一個Idispatch指針的一些簡單的包裹器。

在基於ATL的控件中建立事件則有所不同。在基於ATL的控件中,你從定義控件的.IDL文件中的事件開始。接着你建立了類型庫的編譯工程文件。圖10顯示了在IDL中描述的基於ATL的控件的事件集。

一但類型庫編譯通過,你就可以通過在類視中選擇控件的類,在類上右擊,然後選擇Implement Connection Point,讓類視來爲你創建一個回調代理了。Visual Studio彈出一個對話框,列出控件類型庫中所有可訪問的事件接口。你選擇那些你希望回調代理做的,Visual Studio就爲你寫一個代理。圖11顯示了基於ATL的消息流控制的回調代理。Visual Studio產生的回調代理代表了一個C++友好的函數集,被客戶實現的接口所調用。

MFC的IconnectionPointContainer實現是硬分佈到ColeControl中,並且每個連接點是由一個連接映射處理的,而ATL的實現是用多重繼承處理的。你的控件類繼承IconnectionPointContainerImpl和類視生成的代理。如果你開始一個工程的時候,選擇了"Supports connection points",ObjectWizard就爲你添加IconnectionPointContainerImpl。如果你忘了標記檢查框,你可以寫進去。此代碼顯示了連接點機制是怎樣加入一個控件中的。類ATL_NO_VTABLE CATLMsgTrafficCtl :

{

&#8226;&#8226;&#8226;

public IConnectionPointContainerImpl<CATLMsgTrafficCtl>,

public CProxy_DATLMsgTrafficEvents<CATLMsgTrafficCtl>

&#8226;&#8226;&#8226;

{

LRESULT OnTimer(UINT msg, WPARAM wParam,

LPARAM lParam,

BOOL& bHandled) {

//&#8226;&#8226;&#8226;

if(nMessagesToShow > m_threshold)

{

Fire_ExceededThreshold(nMessagesToShow, m_threshold);

}

//&#8226;&#8226;&#8226;

}

};

作爲一個應用框架ATL和MFC的比較

最近,許多開發者開始對使用ATL作爲框架來開發應用和控件感興趣了。當然,MFC已經使用了很長時間了,是一個能夠開發可雙擊的基於Windows的應用的非常成熟的框架。例如,MFC包括了這樣的特性作爲一個總體文檔/視體系結構:Object Linking 和 Embedding支持, 以及工具條和狀態欄。

然而,所有這些功能都是有代價的。一些更加普遍的抱怨是MFC的比較深的足跡(無論是在DLL中,或是在靜態連接的版本中),以及自身的某種相互依存性。例如,買入MFC的一種特性意味着買入MFC的對象連接和嵌入,這意味着買入MFC的文檔/視結構。另一方面,ATL是一個原始的框架,沒有任何應用框架goodies。

正象你已經看到的,兩個框架都提供創建控件的可行途徑。然而,兩者都各有千秋,也各有弊病。用MFC編寫控件通常更加容易——尤其是如果你不是在開發COM集中的應用並且你需要windowing和drawing支持。ATL的體系架構更加靠近COM的核心,你還會經常發現自己在編寫很多的SDK類型的代碼——就是說,你在回過頭來用window和設備上下文句柄。ATL爲更廣範圍的控件類型提供了很大的支持,象複合控件,基於HTML的控件,沒有design-time接口的輕量級的控件等等。MFC僅提供完全成熟的控件。

對ATL進行分支的實現是非常直接的。例如,增加一個接口通常是一件添加接口到繼承表,在COM映射中添加一個入口然後實現接口函數的工作。分支MFC的實現通常是一種折磨。例如,添加一個接口到基於MFC的控件意味着處理所有那些接口映射宏。

最後,ATL提供了大量的調試支持,包括接口引用計數以及QueryInterface調試支持,這在MFC中是沒有的。

這兩種體系架構的區別是非常明顯的。通常,MFC使得你很快完成你的工程並更快的運行起來,但是犧牲了靈活性。ATL沒有那樣快,那樣容易使用,但是它是COM友好的。而且,好像隨着ATL的成熟,它將會越來越容易使用。  
 更多分享請關注:軟信網-編程-http://www.iis365.net.cn

發佈了13 篇原創文章 · 獲贊 5 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章