翻譯:賴儀靈

出處: http://blog.csdn.net/laiyiling

聲明:版權(quán)歸原作者擁有,請勿隨意轉(zhuǎn)載此翻譯文檔,保留一切權(quán)利。

?

1.1??? 什么是 ATL

?

ATL 的全稱并不能完整的說明 ATL 是什么,提供了什么功能。 Active 一詞實際上是過去 Microsoft 市場時期的遺留物,當(dāng)時的“ ActiveX ”表示 COM 。而現(xiàn)在,“ ActiveX ”表示控件。 ATL 對建立控件提供了非常強大的支持,但是它實際的功能遠不止這些。

?

ATL 的主要功能:

?????? 1 、 對復(fù)雜的數(shù)據(jù)類型提供包裝類,比如智能指針、 VARIANT(S) 、 BSTR(S) 、 HWND(S)

?????? 2 、提供一些基本 COM 接口的實現(xiàn)類,比如 IUnknown, IClassFactory, IDispatch, IPersistXxx, IConnectionPointContainer, IEnumXxx

?????? 3 、提供 COM 服務(wù)器的管理類,比如暴露類對象、實現(xiàn)自我注冊以及管理服務(wù)器生命期。

?????? 4 、提供建立 COM 控件和控件容器的支持類,以及建立舊的空白窗口程序。

?????? 5 、龐大的類庫支持建立 WEB 應(yīng)用程序和 XML Web Services 。

?????? 6 、快速建立應(yīng)用程序的向?qū)Чδ堋?/span>

?

ATL 的實現(xiàn)啟發(fā)于當(dāng)今 C++ 領(lǐng)域的類庫標(biāo)準——標(biāo)準模板庫( STL )。 ATL 由一些短小、高效、靈活的類組成。權(quán)利與職責(zé)同在,與 STL 一樣,只有具有一定經(jīng)驗的 C++ 程序員才能很好的使用 ATL 。

?

當(dāng)然,我們的主要目的編寫 COM 應(yīng)用程序。使用、實現(xiàn) COM 對象、 COM 服務(wù)器的經(jīng)驗也是要求的。對于沒有任何 COM 知識、希望建立 COM 對象的人, ATL 并不適合(這種情況其他的任何工具如 Visual Basic 、 MFC 都是不適合的)。使用 ATL 要求非常熟悉 C++ COM 如何實現(xiàn),以及一些 ATL 本身的實現(xiàn)細節(jié)。

?

事實上, ATL 也實現(xiàn)了一些向?qū)椭覀兩沙跏嫉拇a。在本章的剩余部分,展示了在 Visual Studio 2005 中如何使用 ATL 向?qū)АR黄饋眢w驗吧!

?

1.2??? 創(chuàng)建 COM 服務(wù)器

?

1.2.1 創(chuàng)建 ATL 工程

?????? Visual Studio 中任何開發(fā)的第一步就是建立解決方案,初始化工程。選擇 File => New Project 菜單,顯示圖 1 1 所示的新建工程對話框,然后選擇 Visual C++ 工程類型。

?

?o_1-1.JPG

1 1 創(chuàng)建新的 Visual Studio 工程

?

選擇 Visual C++ 工程文件夾顯示所有可用的 C++ 工程模板類型。工程名稱(圖 1-1 中是 PiSvr )是生成的 DLL 或者 EXE 服務(wù)器名稱。

?

ATL 工程模板的工作就是為你的 COM 服務(wù)器建立一個工程。 COM 服務(wù)器既可以是 DLL 也可以是 EXE , EXE 又可以進一步分為標(biāo)準應(yīng)用程序惡化 NT 服務(wù)。 ATL 工程模板支持所有的三種服務(wù)器類型。默認情況下,初始選擇 DLL 服務(wù)器類型,如圖 1-2 所示。

?

?o_1-2.JPG

1-2 ?? ATL 工程設(shè)置

?

1.2.2 ATL 工程向?qū)нx項

?????? 1-2 的工程向?qū)е辛谐龅牟糠诌x擇我們需要做進一步的解釋。第一個是選擇工程建立使用 ATL 屬性。正如在前言中所述,在 ATL 8 中首選使用非屬性工程,所以本書也集中討論非屬性工程。如果你需要使用屬性,請參考附錄 D 的“ ATL 屬性”,介紹了屬性工程的知識。

?

在附加選擇中,第一個選項允許綁定自定義的代理 / 存根代碼到 DLL 服務(wù)器中。默認不選擇。選中后, Visual Studio 會為代理 / 存根生成一個單獨的工程,名稱為 <ProjectName>PS.vcproj 。同時添加到服務(wù)器的解決方案中。此工程編譯生成一個代理 / 存根 DLL ,把這個 DLL 分發(fā)到所有需要列集和散列自定義接口的客戶、服務(wù)器機器上。注意,在解決方案的默認編譯配置中( Debug, Release ),都沒有選中代理 / 存根 DLL 的同時編譯,在圖 1-3 所示的解決方案屬性頁中,選中 PiSvrPS 工程最后 Build 下的選擇框,然后在你編譯解決方案的時候代理 / 存根 DLL 工程也會同時編譯發(fā)布。

?

?o_1-3.JPG

1-3 新建 ATL COM 服務(wù)器的應(yīng)用程序設(shè)置

?

如果希望把代理 / 存根 DLL 捆綁到服務(wù)器 DLL 一起(要求服務(wù)器安裝在客戶計算機),可以把 Allow Merging of Proxy/Stub Code 選項選中(非屬性工程)。這樣做以后解決方案就只有一個單獨的工程(主服務(wù)器工程),同時為了合并代理 / 存根 DLL 的代碼,部分條件編譯語句會添加到服務(wù)器工程中。預(yù)編譯符號 _MERGE_PROXYSTUB會控制是否把 代理 / 存根的代碼編譯到服務(wù)器,這個定義符號默認會添加到所有的配置文件中。

?

如果沒有足夠的理由(已經(jīng)超出了本書的討論范圍),應(yīng)該盡量避免把代理 / 存根代碼插入到服務(wù)器中,使用雙重接口或者 OLE 自動化兼容的自定義接口更合適。

?

ATL 工程向?qū)У牡诙€選項可以給工程添加微軟基礎(chǔ)類庫( MFC )支持。坦白說,應(yīng)該避免選擇這個選項。下面列舉了一些缺陷,說明開發(fā)人員為什么應(yīng)該關(guān)閉這個選項:

?????? 1 、離開 CString CMap CList 等等),我就無法工作。 MFC 工具類庫只是在 C++ 標(biāo)準委員會建立標(biāo)準程序庫之前的臨時產(chǎn)品。在標(biāo)準程序庫建立之后,應(yīng)該停止使用 MFC 版本,因為標(biāo)準程序庫提供的類 string, map, list 等,與等價的 MFC 版本相比更靈活健壯。而且,現(xiàn)在的 CString 類是 MFC ATL 的共享類,因此在 ATL 最新版中不需要額外包含其他的 MFC 部分,也不需要鏈接 MFC 庫,可直接使用。其他的 MFC 工具類也稱為了共享類,如 CPoint CRect 。對于這些和 MFC 類似的集合類庫,在 ATL 最新版實現(xiàn)了相應(yīng)的集合類( CAtlMap, CAtlList 等等)。

?????? 2 、沒有向?qū)Ь筒荒芄ぷ?。本章的?nèi)容全部是關(guān)于 Visual Studio 提供給程序員的向?qū)Чδ堋?/span> ATL 的向?qū)Чδ芎?/span> MFC 的一樣強大。

?????? 3 、我已經(jīng)掌握 MFC ,不想學(xué)其他的。幸運的是,有這樣思想的人現(xiàn)在都不會讀本書。

?

ATL 工程向?qū)У牡谌齻€選項是支持 COM+ ,使得工程鏈接 COM+ 組件服務(wù)庫 comsvs.dll ,包含相應(yīng)的頭文件 comsvcs.h 。所有應(yīng)用程序就可以訪問 COM+ 的各種接口。選中 COM+ 支持后,可以選中子項以支持組件注冊,在工程中生成額外的 coclass 類實現(xiàn) IComponentRegister 接口。

?

1.2.3 ATL 工程向?qū)У慕Y(jié)果

?

不管有沒有選中上面介紹的三個工程項, ATL 向?qū)傻?/span> COM 服務(wù)器都實現(xiàn)了如下的三個功能:自注冊、服務(wù)器生命期控制、暴露類對象。作為額外提供的方便,向?qū)г诿看尉幾g成功后發(fā)送一個事件以注冊 COM 服務(wù)器。根據(jù) DLL/EXE 服務(wù)器的類型,響應(yīng)事件執(zhí)行 regsvr32.exe <project>.dll 或者 <project>.exe /regserver

?

關(guān)于 COM 服務(wù)器支持此三項功能的更詳細信息,以及如何擴展進行高級的同步控制和聲明期需要,請參考第五章 COM 服務(wù)器。

?

1.3??? 插入 COM

?

1.3.1 添加 ATL 簡單對象

?

建立了 ATL COM 服務(wù)器后,可能需要插入一個新的 COM 類。選擇菜單 Project => Add Class Item 實現(xiàn)。插入 ATL 類時,需要線選中插入類的類型,如果 1-4 所示。

?o_1-4.JPG
1-4 添加 ATL COM

?

如果時間充足,你也許想花點時候稍微的瀏覽一下可用的類類型。每個類型都會生成一系列特殊的代碼,它們使用 ATL 基礎(chǔ)類提供大部分的功能,然后生成自定義接口的框架。 COM 類實現(xiàn)不同的 COM 接口時,你可以選擇不同向?qū)ь愋?。不幸的是,向?qū)Р]有提供訪問所有 ATL 功能(甚至大部分功能)。但是,設(shè)計生成的代碼很容易修改、開始之后很方便增加、刪除函數(shù)。熟悉這些向?qū)深惖念愋秃瓦x項最好的辦法就是實踐。

?

選擇類類型后(點擊 OK ), Visual Studio 通常會要求輸入一些其他的信息。一些類型的附加選項比 ATL 簡單類型多很多(圖 1-4 選擇簡單類型)。大部分的 COM 類都至少需要圖 1-5 和圖 1-6 所示的信息。

?

?o_1-5.JPG
1-5 設(shè)置 COM 類名稱

?

?o_1-6.JPG
1-6 設(shè)置 COM 類名稱

?

ATL 簡單對象向?qū)υ捒虻拿Q頁中,只需要輸入短名稱,如 CalcPi 。短名稱被用來組成對話框中的其他部分信息(你也可以選擇不使用這些自動生成的信息)。這些信息分為兩類:必要的 C++ 信息,包括 C++ 類名稱、 C++ 類的頭文件、實現(xiàn)文件名稱;必要的 COM 信息,包括 coclass 名稱(接口定義語言使用 [IDL] )、默認接口的名稱(也用于 IDL )、稱為類型的友好名稱(用于 IDL 和注冊設(shè)置)、最后還有版本獨立的程序標(biāo)識符(用于注冊設(shè)置)。版本相關(guān)的 ProgID 是版本獨立的 ProgID 加上“ .1 ”后綴組成。注意其中的 Attributed 選項不能選中。在非屬性工程中,我可以有選擇的添加屬性化的類到非屬性化的工程中,只需要選中圖 1-5 中的 Attributed 項。

?

Options 頁中,可以修改一些低級的 COM 設(shè)定。線程模型描述了你希望新增類的實例所生存的套間類型:單線程套間( STA ,也稱為套間模型);多線程套間( MTA ,也稱為自由套間模型)。 Single 模型通常用于少數(shù)的類,不管客戶是怎樣的套間模型,類的所有實例都要求共享在應(yīng)用程序的主 STA 中。 Both 模型使對象和客戶處于相同的套間類型,這樣可以避免過多的代理 / 存根對。中立線程模型只有在 Window 2000 及以后的操作系統(tǒng)才可以用。使對象可以在調(diào)用者的線程中安全執(zhí)行。調(diào)用中立類型的組件通常速度更快,因為它不需要線程交換,而在不同類型的套間之間進行跨套間調(diào)用通常是需要線程交換的。你選擇的線程模型設(shè)置會影響服務(wù)器在注冊中存儲的 ThreadingModel 值,它決定了對象應(yīng)該如何實現(xiàn)線程安全的 AddRef Release

?

接口類型設(shè)置允許你設(shè)定新增類的默認接口類型: Custom (需要自定義的代理 / 存根,不從接口 IDispatch 繼承); Dual (使用類型庫進行列集,從 IDispatch 繼承)。此設(shè)置影響生成的 IDL 默認接口。選擇 Custom 后可以進一步選中自動化兼容選項。選中后的 IDL 定義中會添加 [oleautomation] 修飾屬性,它限制了接口方法的變量類型可以使用 OLE 自動化兼容類型。比如, [oleautomation] 接口方法必須使用 SAFEARRAY 代替方便的 C 風(fēng)格數(shù)組。如果你希望 ATL COM 對象適用于不同的客戶環(huán)境,比如 Visual Basic ,你就應(yīng)該選中這個選項。而且,如果使用了 [oleautomation] 接口,運行在微軟 .Net 框架中的代碼訪問 COM 對象更簡單。部署 [oleautomation] 接口的 COM 對象也可能更簡單,因為此類接口總是使用統(tǒng)一的列集在不同的套間傳遞接口。在支持 COM 的計算機上總是存在類型庫列集的,因此你不需要與組件一起發(fā)布額外的代理 / 存根 DLL 。

?

聚集設(shè)置允許你設(shè)定是否希望你的對象被聚集使用,也就是能否作為受控的內(nèi)部對象參與聚合。這個設(shè)置不會影響當(dāng)前新增類對象是否可以作為外部控制對象使用聚合。關(guān)于被聚合的更多信息請參考第四章的“ ATL 的對象”。關(guān)于如何聚合其他對象請參考第六章的“接口映射”。

?

支持 ISupportErrorInfo 設(shè)置指示向?qū)梢粋€ ISupportErrorInfo 接口的實現(xiàn)。如果希望拋出 COM 異常那么就有必要選中這個選項。在跨語言和套間邊界時,與單獨的 HRESULT 所提供的錯誤相比, COM 異常(也稱為 COM 錯誤信息對象)可以傳遞更多的錯誤細節(jié)。關(guān)于拋出、捕捉 COM 異常的更多信息請參考第五章“ COM 服務(wù)器”。

?

支持連接點指示向?qū)?/span> IConnectionPoint 的實現(xiàn),它允許對象激發(fā)腳本環(huán)境中的事件,比如瀏覽器的寄宿腳本??丶瑯邮褂眠B接點激發(fā)控件容器的事件,詳細參考第九章“連接點”。

?

在幫助信息中, Free-Threaded Marshaler 的解釋是:允許同一接口的客戶獲得一個原始接口,即使它們的線程模型不匹配。這個幫助信息并沒有說使用這個選項的危險性。不辛的是, Free-Threaded Marshaler FTM )如果代價高昂的汽車:雖然你想擁有,但你卻負擔(dān)不起。在選擇此項前請先參考第六章“接口映射”關(guān)于 FTM 的描述。

?

支持 IObjectWithSite 設(shè)置使向?qū)梢粋€ IObjectWithSite 的實現(xiàn)。寄宿于容器(如 IE 瀏覽器)的對象需要使用這個接口。容器使用這個接口傳遞接口指針到它們?nèi)菁{的對象,然后對象就可以直接和容器通信。

?

1.3.2 ATL 簡單對象向?qū)У慕Y(jié)果

?

在設(shè)置好這些選項后,簡單對象向?qū)婺闵梢粋€單獨的文件,以開始增加自己的實現(xiàn)。對于此新增類:生成一個新的類定義頭文件;一個新的實現(xiàn) CPP 文件;一個 .RGS 文件包含注冊信息。而且 IDL 文件也會更新包含新接口的定義。

?

生成的類定義文件如下:

?

// CalcPi.h : 聲明 CCalcPi

#pragma once

#include "resource.h"?????? // 主要資源符號

?

#include "PiSvr.h"

#include "_ICalcPiEvents_CP.h"

?

class ATL_NO_VTABLE CCalcPi :

??? public CComObjectRootEx<CComSingleThreadModel>,

??? public CComCoClass<CCalcPi, &CLSID_CalcPi>,

??? public ISupportErrorInfo,

??? public IConnectionPointContainerImpl<CCalcPi>,

??? public CProxy_ICalcPiEvents<CCalcPi>,

??? public IDispatchImpl<ICalcPi, &IID_ICalcPi, &LIBID_PiSvrLib,

??????? /*wMajor =*/ 1, /*wMinor =*/ 0>

{

public:

??? CCalcPi() { }

DECLARE_REGISTRY_RESOURCEID(IDR_CALCPI)

?

BEGIN_COM_MAP(CCalcPi)

??? COM_INTERFACE_ENTRY(ICalcPi)

??? COM_INTERFACE_ENTRY(IDispatch)

??? COM_INTERFACE_ENTRY(ISupportErrorInfo)

??? COM_INTERFACE_ENTRY(IConnectionPointContainer)

END_COM_MAP()

?

BEGIN_CONNECTION_POINT_MAP(CCalcPi)

??? CONNECTION_POINT_ENTRY(__uuidof(_ICalcPiEvents))

END_CONNECTION_POINT_MAP()

// ISupportsErrorInfo

??? STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

?

??? DECLARE_PROTECT_FINAL_CONSTRUCT()

?

??? HRESULT FinalConstruct() {

??????? return S_OK;

??? }

?

??? void FinalRelease() {

??? }

?

public:

?

};

?

OBJECT_ENTRY_AUTO(__uuidof(CalcPi), CCalcPi)

?

第一個需要注意的就是基類列表。此例中, ATL 同時使用了模板和多繼承技術(shù)。每個基類都提供了 COM 對象需要的一些普通實現(xiàn)代碼。

?????? 1 、 CComObjectRootEx 提供了 IUnknown 接口實現(xiàn)

?????? 2 、 CComcoClass 提供了類廠實現(xiàn)

?????? 3 、 ISupportErrorInfo 是一個接口,實現(xiàn)了 CPP 文件的一個方法。

?????? 4 、選中了支持連接點選項后, IConnectionPointContainerImpl 提供了此功能的實現(xiàn)

?????? 5 、 CProxy_ICalcPiEvents 也是連接點的部分實現(xiàn)

?????? 6 IDispatchImpl 提供了對象雙接口 IDispatch 的實現(xiàn)。

?

另一個需要注意的是 COM_MAP 宏,它是 ATL 映射的一個實例:一系列生成代碼的宏(通常用來生成查找表)。特別的是 COM_MAP 宏用來實現(xiàn)所有 COM 對象都要求支持的 QueryInterface 方法。

?

關(guān)于 ATL 基類如何實現(xiàn)基本 COM 功能,如何利用這些實現(xiàn)建立對象層次以及適當(dāng)?shù)耐蕉嗑€程對象,請參考第四章“ ATL 對象”。如何使用 COM_MAP 宏的詳細信息請參考第六章“接口映射”。