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