翻譯:賴儀靈
出處:
http://blog.csdn.net/laiyiling
聲明:版權(quán)歸原作者擁有,請勿隨意轉(zhuǎn)載此翻譯文檔,保留一切權(quán)利。
?
1.1???
什么是
ATL
?
ATL
的全稱并不能完整的說明
ATL
是什么,提供了什么功能。
Active
一詞實(shí)際上是過去
Microsoft
市場時(shí)期的遺留物,當(dāng)時(shí)的“
ActiveX
”表示
COM
。而現(xiàn)在,“
ActiveX
”表示控件。
ATL
對建立控件提供了非常強(qiáng)大的支持,但是它實(shí)際的功能遠(yuǎn)不止這些。
?
ATL
的主要功能:
?????? 1
、
對復(fù)雜的數(shù)據(jù)類型提供包裝類,比如智能指針、
VARIANT(S)
、
BSTR(S)
、
HWND(S)
。
?????? 2
、提供一些基本
COM
接口的實(shí)現(xiàn)類,比如
IUnknown, IClassFactory, IDispatch, IPersistXxx, IConnectionPointContainer,
和
IEnumXxx
。
?????? 3
、提供
COM
服務(wù)器的管理類,比如暴露類對象、實(shí)現(xiàn)自我注冊以及管理服務(wù)器生命期。
?????? 4
、提供建立
COM
控件和控件容器的支持類,以及建立舊的空白窗口程序。
?????? 5
、龐大的類庫支持建立
WEB
應(yīng)用程序和
XML Web Services
。
?????? 6
、快速建立應(yīng)用程序的向?qū)Чδ堋?/span>
?
ATL
的實(shí)現(xiàn)啟發(fā)于當(dāng)今
C++
領(lǐng)域的類庫標(biāo)準(zhǔn)——標(biāo)準(zhǔn)模板庫(
STL
)。
ATL
由一些短小、高效、靈活的類組成。權(quán)利與職責(zé)同在,與
STL
一樣,只有具有一定經(jīng)驗(yàn)的
C++
程序員才能很好的使用
ATL
。
?
當(dāng)然,我們的主要目的編寫
COM
應(yīng)用程序。使用、實(shí)現(xiàn)
COM
對象、
COM
服務(wù)器的經(jīng)驗(yàn)也是要求的。對于沒有任何
COM
知識(shí)、希望建立
COM
對象的人,
ATL
并不適合(這種情況其他的任何工具如
Visual Basic
、
MFC
都是不適合的)。使用
ATL
要求非常熟悉
C++
中
COM
如何實(shí)現(xiàn),以及一些
ATL
本身的實(shí)現(xiàn)細(xì)節(jié)。
?
事實(shí)上,
ATL
也實(shí)現(xiàn)了一些向?qū)椭覀兩沙跏嫉拇a。在本章的剩余部分,展示了在
Visual Studio 2005
中如何使用
ATL
向?qū)АR黄饋眢w驗(yàn)吧!
?
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ù)器建立一個(gè)工程。
COM
服務(wù)器既可以是
DLL
也可以是
EXE
,
EXE
又可以進(jìn)一步分為標(biāo)準(zhǔn)應(yīng)用程序惡化
NT
服務(wù)。
ATL
工程模板支持所有的三種服務(wù)器類型。默認(rèn)情況下,初始選擇
DLL
服務(wù)器類型,如圖
1-2
所示。
?
?
圖
1-2 ?? ATL
工程設(shè)置
?
1.2.2 ATL
工程向?qū)нx項(xiàng)
??????
圖
1-2
的工程向?qū)е辛谐龅牟糠诌x擇我們需要做進(jìn)一步的解釋。第一個(gè)是選擇工程建立使用
ATL
屬性。正如在前言中所述,在
ATL 8
中首選使用非屬性工程,所以本書也集中討論非屬性工程。如果你需要使用屬性,請參考附錄
D
的“
ATL
屬性”,介紹了屬性工程的知識(shí)。
?
在附加選擇中,第一個(gè)選項(xiàng)允許綁定自定義的代理
/
存根代碼到
DLL
服務(wù)器中。默認(rèn)不選擇。選中后,
Visual Studio
會(huì)為代理
/
存根生成一個(gè)單獨(dú)的工程,名稱為
<ProjectName>PS.vcproj
。同時(shí)添加到服務(wù)器的解決方案中。此工程編譯生成一個(gè)代理
/
存根
DLL
,把這個(gè)
DLL
分發(fā)到所有需要列集和散列自定義接口的客戶、服務(wù)器機(jī)器上。注意,在解決方案的默認(rèn)編譯配置中(
Debug, Release
),都沒有選中代理
/
存根
DLL
的同時(shí)編譯,在圖
1-3
所示的解決方案屬性頁中,選中
PiSvrPS
工程最后
Build
下的選擇框,然后在你編譯解決方案的時(shí)候代理
/
存根
DLL
工程也會(huì)同時(shí)編譯發(fā)布。
?
?
圖
1-3
新建
ATL COM
服務(wù)器的應(yīng)用程序設(shè)置
?
如果希望把代理
/
存根
DLL
捆綁到服務(wù)器
DLL
一起(要求服務(wù)器安裝在客戶計(jì)算機(jī)),可以把
Allow Merging of Proxy/Stub Code
選項(xiàng)選中(非屬性工程)。這樣做以后解決方案就只有一個(gè)單獨(dú)的工程(主服務(wù)器工程),同時(shí)為了合并代理
/
存根
DLL
的代碼,部分條件編譯語句會(huì)添加到服務(wù)器工程中。預(yù)編譯符號(hào)
_MERGE_PROXYSTUB會(huì)控制是否把
代理
/
存根的代碼編譯到服務(wù)器,這個(gè)定義符號(hào)默認(rèn)會(huì)添加到所有的配置文件中。
?
如果沒有足夠的理由(已經(jīng)超出了本書的討論范圍),應(yīng)該盡量避免把代理
/
存根代碼插入到服務(wù)器中,使用雙重接口或者
OLE
自動(dòng)化兼容的自定義接口更合適。
?
ATL
工程向?qū)У牡诙€(gè)選項(xiàng)可以給工程添加微軟基礎(chǔ)類庫(
MFC
)支持。坦白說,應(yīng)該避免選擇這個(gè)選項(xiàng)。下面列舉了一些缺陷,說明開發(fā)人員為什么應(yīng)該關(guān)閉這個(gè)選項(xiàng):
?????? 1
、離開
CString
(
CMap CList
等等),我就無法工作。
MFC
工具類庫只是在
C++
標(biāo)準(zhǔn)委員會(huì)建立標(biāo)準(zhǔn)程序庫之前的臨時(shí)產(chǎn)品。在標(biāo)準(zhǔn)程序庫建立之后,應(yīng)該停止使用
MFC
版本,因?yàn)闃?biāo)準(zhǔn)程序庫提供的類
string, map, list
等,與等價(jià)的
MFC
版本相比更靈活健壯。而且,現(xiàn)在的
CString
類是
MFC
和
ATL
的共享類,因此在
ATL
最新版中不需要額外包含其他的
MFC
部分,也不需要鏈接
MFC
庫,可直接使用。其他的
MFC
工具類也稱為了共享類,如
CPoint
和
CRect
。對于這些和
MFC
類似的集合類庫,在
ATL
最新版實(shí)現(xiàn)了相應(yīng)的集合類(
CAtlMap, CAtlList
等等)。
?????? 2
、沒有向?qū)Ь筒荒芄ぷ鳌1菊碌膬?nèi)容全部是關(guān)于
Visual Studio
提供給程序員的向?qū)Чδ堋?/span>
ATL
的向?qū)Чδ芎?/span>
MFC
的一樣強(qiáng)大。
?????? 3
、我已經(jīng)掌握
MFC
,不想學(xué)其他的。幸運(yùn)的是,有這樣思想的人現(xiàn)在都不會(huì)讀本書。
?
ATL
工程向?qū)У牡谌齻€(gè)選項(xiàng)是支持
COM+
,使得工程鏈接
COM+
組件服務(wù)庫
comsvs.dll
,包含相應(yīng)的頭文件
comsvcs.h
。所有應(yīng)用程序就可以訪問
COM+
的各種接口。選中
COM+
支持后,可以選中子項(xiàng)以支持組件注冊,在工程中生成額外的
coclass
類實(shí)現(xiàn)
IComponentRegister
接口。
?
1.2.3 ATL
工程向?qū)У慕Y(jié)果
?
不管有沒有選中上面介紹的三個(gè)工程項(xiàng),
ATL
向?qū)傻?/span>
COM
服務(wù)器都實(shí)現(xiàn)了如下的三個(gè)功能:自注冊、服務(wù)器生命期控制、暴露類對象。作為額外提供的方便,向?qū)г诿看尉幾g成功后發(fā)送一個(gè)事件以注冊
COM
服務(wù)器。根據(jù)
DLL/EXE
服務(wù)器的類型,響應(yīng)事件執(zhí)行
regsvr32.exe <project>.dll
或者
<project>.exe /regserver
。
?
關(guān)于
COM
服務(wù)器支持此三項(xiàng)功能的更詳細(xì)信息,以及如何擴(kuò)展進(jìn)行高級的同步控制和聲明期需要,請參考第五章
COM
服務(wù)器。
?
1.3???
插入
COM
類
?
1.3.1
添加
ATL
簡單對象
?
建立了
ATL COM
服務(wù)器后,可能需要插入一個(gè)新的
COM
類。選擇菜單
Project => Add Class Item
實(shí)現(xiàn)。插入
ATL
類時(shí),需要線選中插入類的類型,如果
1-4
所示。
?
圖
1-4
添加
ATL COM
類
?
如果時(shí)間充足,你也許想花點(diǎn)時(shí)候稍微的瀏覽一下可用的類類型。每個(gè)類型都會(huì)生成一系列特殊的代碼,它們使用
ATL
基礎(chǔ)類提供大部分的功能,然后生成自定義接口的框架。
COM
類實(shí)現(xiàn)不同的
COM
接口時(shí),你可以選擇不同向?qū)ь愋汀2恍业氖牵驅(qū)Р]有提供訪問所有
ATL
功能(甚至大部分功能)。但是,設(shè)計(jì)生成的代碼很容易修改、開始之后很方便增加、刪除函數(shù)。熟悉這些向?qū)深惖念愋秃瓦x項(xiàng)最好的辦法就是實(shí)踐。
?
選擇類類型后(點(diǎn)擊
OK
),
Visual Studio
通常會(huì)要求輸入一些其他的信息。一些類型的附加選項(xiàng)比
ATL
簡單類型多很多(圖
1-4
選擇簡單類型)。大部分的
COM
類都至少需要圖
1-5
和圖
1-6
所示的信息。
?
?
圖
1-5
設(shè)置
COM
類名稱
?
?
圖
1-6
設(shè)置
COM
類名稱
?
在
ATL
簡單對象向?qū)υ捒虻拿Q頁中,只需要輸入短名稱,如
CalcPi
。短名稱被用來組成對話框中的其他部分信息(你也可以選擇不使用這些自動(dòng)生成的信息)。這些信息分為兩類:必要的
C++
信息,包括
C++
類名稱、
C++
類的頭文件、實(shí)現(xiàn)文件名稱;必要的
COM
信息,包括
coclass
名稱(接口定義語言使用
[IDL]
)、默認(rèn)接口的名稱(也用于
IDL
)、稱為類型的友好名稱(用于
IDL
和注冊設(shè)置)、最后還有版本獨(dú)立的程序標(biāo)識(shí)符(用于注冊設(shè)置)。版本相關(guān)的
ProgID
是版本獨(dú)立的
ProgID
加上“
.1
”后綴組成。注意其中的
Attributed
選項(xiàng)不能選中。在非屬性工程中,我可以有選擇的添加屬性化的類到非屬性化的工程中,只需要選中圖
1-5
中的
Attributed
項(xiàng)。
?
在
Options
頁中,可以修改一些低級的
COM
設(shè)定。線程模型描述了你希望新增類的實(shí)例所生存的套間類型:單線程套間(
STA
,也稱為套間模型);多線程套間(
MTA
,也稱為自由套間模型)。
Single
模型通常用于少數(shù)的類,不管客戶是怎樣的套間模型,類的所有實(shí)例都要求共享在應(yīng)用程序的主
STA
中。
Both
模型使對象和客戶處于相同的套間類型,這樣可以避免過多的代理
/
存根對。中立線程模型只有在
Window 2000
及以后的操作系統(tǒng)才可以用。使對象可以在調(diào)用者的線程中安全執(zhí)行。調(diào)用中立類型的組件通常速度更快,因?yàn)樗恍枰€程交換,而在不同類型的套間之間進(jìn)行跨套間調(diào)用通常是需要線程交換的。你選擇的線程模型設(shè)置會(huì)影響服務(wù)器在注冊中存儲(chǔ)的
ThreadingModel
值,它決定了對象應(yīng)該如何實(shí)現(xiàn)線程安全的
AddRef
和
Release
。
?
接口類型設(shè)置允許你設(shè)定新增類的默認(rèn)接口類型:
Custom
(需要自定義的代理
/
存根,不從接口
IDispatch
繼承);
Dual
(使用類型庫進(jìn)行列集,從
IDispatch
繼承)。此設(shè)置影響生成的
IDL
默認(rèn)接口。選擇
Custom
后可以進(jìn)一步選中自動(dòng)化兼容選項(xiàng)。選中后的
IDL
定義中會(huì)添加
[oleautomation]
修飾屬性,它限制了接口方法的變量類型可以使用
OLE
自動(dòng)化兼容類型。比如,
[oleautomation]
接口方法必須使用
SAFEARRAY
代替方便的
C
風(fēng)格數(shù)組。如果你希望
ATL COM
對象適用于不同的客戶環(huán)境,比如
Visual Basic
,你就應(yīng)該選中這個(gè)選項(xiàng)。而且,如果使用了
[oleautomation]
接口,運(yùn)行在微軟
.Net
框架中的代碼訪問
COM
對象更簡單。部署
[oleautomation]
接口的
COM
對象也可能更簡單,因?yàn)榇祟惤涌诳偸鞘褂媒y(tǒng)一的列集在不同的套間傳遞接口。在支持
COM
的計(jì)算機(jī)上總是存在類型庫列集的,因此你不需要與組件一起發(fā)布額外的代理
/
存根
DLL
。
?
聚集設(shè)置允許你設(shè)定是否希望你的對象被聚集使用,也就是能否作為受控的內(nèi)部對象參與聚合。這個(gè)設(shè)置不會(huì)影響當(dāng)前新增類對象是否可以作為外部控制對象使用聚合。關(guān)于被聚合的更多信息請參考第四章的“
ATL
的對象”。關(guān)于如何聚合其他對象請參考第六章的“接口映射”。
?
支持
ISupportErrorInfo
設(shè)置指示向?qū)梢粋€(gè)
ISupportErrorInfo
接口的實(shí)現(xiàn)。如果希望拋出
COM
異常那么就有必要選中這個(gè)選項(xiàng)。在跨語言和套間邊界時(shí),與單獨(dú)的
HRESULT
所提供的錯(cuò)誤相比,
COM
異常(也稱為
COM
錯(cuò)誤信息對象)可以傳遞更多的錯(cuò)誤細(xì)節(jié)。關(guān)于拋出、捕捉
COM
異常的更多信息請參考第五章“
COM
服務(wù)器”。
?
支持連接點(diǎn)指示向?qū)?/span>
IConnectionPoint
的實(shí)現(xiàn),它允許對象激發(fā)腳本環(huán)境中的事件,比如瀏覽器的寄宿腳本。控件同樣使用連接點(diǎn)激發(fā)控件容器的事件,詳細(xì)參考第九章“連接點(diǎn)”。
?
在幫助信息中,
Free-Threaded Marshaler
的解釋是:允許同一接口的客戶獲得一個(gè)原始接口,即使它們的線程模型不匹配。這個(gè)幫助信息并沒有說使用這個(gè)選項(xiàng)的危險(xiǎn)性。不辛的是,
Free-Threaded Marshaler
(
FTM
)如果代價(jià)高昂的汽車:雖然你想擁有,但你卻負(fù)擔(dān)不起。在選擇此項(xiàng)前請先參考第六章“接口映射”關(guān)于
FTM
的描述。
?
支持
IObjectWithSite
設(shè)置使向?qū)梢粋€(gè)
IObjectWithSite
的實(shí)現(xiàn)。寄宿于容器(如
IE
瀏覽器)的對象需要使用這個(gè)接口。容器使用這個(gè)接口傳遞接口指針到它們?nèi)菁{的對象,然后對象就可以直接和容器通信。
?
1.3.2 ATL
簡單對象向?qū)У慕Y(jié)果
?
在設(shè)置好這些選項(xiàng)后,簡單對象向?qū)婺闵梢粋€(gè)單獨(dú)的文件,以開始增加自己的實(shí)現(xiàn)。對于此新增類:生成一個(gè)新的類定義頭文件;一個(gè)新的實(shí)現(xiàn)
CPP
文件;一個(gè)
.RGS
文件包含注冊信息。而且
IDL
文件也會(huì)更新包含新接口的定義。
?
生成的類定義文件如下:
?
// CalcPi.h :
聲明
CCalcPi
#pragma once
#include "resource.h"?????? //
主要資源符號(hào)
?
#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)
?
第一個(gè)需要注意的就是基類列表。此例中,
ATL
同時(shí)使用了模板和多繼承技術(shù)。每個(gè)基類都提供了
COM
對象需要的一些普通實(shí)現(xiàn)代碼。
?????? 1
、
CComObjectRootEx
提供了
IUnknown
接口實(shí)現(xiàn)
?????? 2
、
CComcoClass
提供了類廠實(shí)現(xiàn)
?????? 3
、
ISupportErrorInfo
是一個(gè)接口,實(shí)現(xiàn)了
CPP
文件的一個(gè)方法。
?????? 4
、選中了支持連接點(diǎn)選項(xiàng)后,
IConnectionPointContainerImpl
提供了此功能的實(shí)現(xiàn)
?????? 5
、
CProxy_ICalcPiEvents
也是連接點(diǎn)的部分實(shí)現(xiàn)
?????? 6
、
IDispatchImpl
提供了對象雙接口
IDispatch
的實(shí)現(xiàn)。
?
另一個(gè)需要注意的是
COM_MAP
宏,它是
ATL
映射的一個(gè)實(shí)例:一系列生成代碼的宏(通常用來生成查找表)。特別的是
COM_MAP
宏用來實(shí)現(xiàn)所有
COM
對象都要求支持的
QueryInterface
方法。
?
關(guān)于
ATL
基類如何實(shí)現(xiàn)基本
COM
功能,如何利用這些實(shí)現(xiàn)建立對象層次以及適當(dāng)?shù)耐蕉嗑€程對象,請參考第四章“
ATL
對象”。如何使用
COM_MAP
宏的詳細(xì)信息請參考第六章“接口映射”。