翻譯:賴儀靈
出處:
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++
工程類型。
?
?
圖
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
所示。
?
?
圖
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ā)布。
?
?
圖
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
所示。
?
圖
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
所示的信息。
?
?
圖
1-5
設(shè)置
COM
類名稱
?
?
圖
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
宏的詳細信息請參考第六章“接口映射”。