本文的對象是COM編程初學者,其目的旨在描述如何用ATL建立COM服務器,以及如何在VC或VB編寫的客戶端應用程序中調用COM服務器。爲了避免給初 學者增長負擔,本文不打算深刻討論COM和IDL的細節,而是展現用ATL建立簡單的COM對象所須要的步驟。但願經過這篇文章能刺激你學習COM編程的 慾望。 第一步:運行ATL COM嚮導(參見圖一) 你要作的第一件事情是啓動VC建立一個新的工程。選擇「ATL COM AppWizard」。注意這個嚮導建立的工程並無包含任何初始的COM對象,在完成這個嚮導以後,要從「ClassView」中用「New ATL Object」命令來指定你想要增長到這個工程中的對象類型。 在「Project name」編輯框中爲工程取個名字:「Simple_ATL」。在「Location」編輯框中指定工程的存放位置,如「E:\samples\Simple_ATL」,而後單擊OK按鈕。 |
![]() |
(圖一) |
你會看到一個選擇屏幕(圖二)。 |
![]() |
(圖二) |
第一部分單選按鈕選項是要建立的服務器類型 「Server Type」。由於咱們要建立一個進程內服務器(Server DLL),因此應該選擇的類型是動態連接庫「Dynamic Link Library——DLL」,注意全部進程內服務器都是DLL。下面是三個複選框不用去管它,它和咱們建立的這個工程不要緊。單擊「Finish」按鈕。 嚮導將自動產生相應的文件。而後出現「New Project Information」窗口告訴你嚮導將要建立一些什麼文件(圖三)。確認後單擊OK按鈕。 |
![]() |
(圖三) |
第二步:建立新的ATL對象 肯定在VC的IDE環境中見到「Workspace View」。也就是工做間視圖。你也能夠進入「View」菜單,而後選擇「Workspace」。這個視圖中由三個標籤,單擊「ClassView」標 籤。你會看到其中的「Simple_ATL Classes」。選中後單擊右鍵並從彈出菜單中選擇「New ATL Object」。你會見到以下窗口:(圖四) |
![]() |
(圖四) |
咱們就用缺省選擇(Simple Object),也就是簡單對象。單擊「Next」按鈕進入「ATL Object Wizard Properties」,也就是ATL對象嚮導的屬性對話框(圖五)。程序員 |
![]() |
(圖五)編程 attriubtes 能夠採用默認的方式,若是須要支持事件,則要勾上"Support Connection Points"服務器 |
在「Short Name」文本編輯框中輸入「First_ATL」。注意嚮導會自動填寫其他的文本編輯框。單擊「Attributes」標籤。其中有幾組單選按鈕選項和 幾個複選框。第一組單選按鈕是線程模型「Threading Model」,咱們取缺省值「Apartment Model」。第二組單選按鈕是接口「Interface」,單擊「Dual」,也就是雙接口。最後,第三組單選按鈕是聚合「Aggregation」, 由於咱們不想涉及接口的聚合,因此在此選擇「No」。至於底下的三個複選框,咱們選擇支持事件類型Support Connection_Points,單擊OK按鈕讓嚮導建立新的「ATL Simple Object」 第三步:添加方法(對不一樣的方法調用將會統一作說明) 若是你單擊工做間的「ClassView」標籤,你會注意到嚮導在裏面添加了一些內容。添加一個方法很容易,(圖六)選中「IFirst_ATL」後單擊右鍵並選擇「Add Method」。 |
![]() |
(圖六) |
單擊「Add Method」後,你會看到「Add Method to Interface」對話框(圖七)。 |
![]() |
(圖七) |
在「Return Type」編輯框中(已成灰色)這個方法的返回值已經缺省爲 「HRESULT」。大多數狀況下都應該是這個值類型。下一個編輯框是方法名「Method Name」,輸入方法名「AddNumbers」。最後一個編輯框是要你輸入但願使用的參數「Parameters」。因爲咱們打算將兩個數字相加,而後 返回相加結果,因此要使用三個參數。最後一個參數是一個指針。如今你不用去關心繁雜的接口定義語言IDL,只要在這個參數編輯框中輸入以下內容: [in] long Num1, [in] long Num2, [out] long *ReturnVal 它的意思是聲明兩個long類型輸入[in]參數和一個指針返回值[out](剛開始可能會不習慣這樣怪怪的寫法,但等你閱讀了一兩本關於COM的書之 後,會慢慢接收它的)。單擊OK按鈕。展開全部「ClassView」的節點「+」號。從這個視圖能夠清楚地瞭解Simple_ATL各個類之間的層次關 系。雙擊最上面「IFirst_ATL」(接口)節點下的「AddNumbers」(方法)節點,右邊屏幕將會顯示這個方法的實現代碼。添加以下的代碼: STDMETHODIMP CFirst_ATL::AddNumbers(long Num1, long Num2, long *ReturnVal) { // TODO: Add your implementation code here *ReturnVal = Num1 + Num2; return S_OK; }
繼續增長它的方法:這裏會講到使用不一樣的參數。ide Add([in] VARIANT v1, [in] VARIANT v2, [out, retval] VARIANT * pVal);函數 它的意思是聲明兩個VARIAN類型輸入[in]和一個函數返回值[out,retval] ,VARIANT能夠返回一個對象。也能夠返回其它的,如[out,int] *nRst 學習 使用VARIANT的好處是:函數內部動態判斷參數類型,若是是整數則進行整數加法,若是是字符串,則進行字符串加法(字符串加法就是字符串鏈接哈)測試 Upper([in] BSTR str, [out,retval] BSTR * pVal);ui 它的意思是聲明一個BSTR 的字符串類型輸入[in]參數和一個指針返回值[out], pVal能夠帶回一個字符串,用以返回一個連續的內存內容。spa
//其它方法 // CString m_str("返回內容");
以上幾種參數格式,能夠自由組合使用,包括函數返回值、指針返回值的各類類型, 傳入參數的各類普通、指針類型。
第四步:添加事件: 若是你單擊工做間的「ClassView」標籤,你會注意到嚮導在裏面添加了一些內容。添加一個事件很容易,選中「_IFirst_ATLEvents」後單擊右鍵並選擇「Add Method」。 單擊「Add Method」後,你會看到Add Method to Interface 在對話框的Method Name中輸入你的事件名稱,參數與添加其它方法同樣。而後點擊「OK」 在_IFirst_ATLEvenns中會看到你剛纔添加的事件方法名。 對工程進行一次全編譯(注意,必定要編譯,才能往下走),須要生成一些資源文件,以供後面調用。 選擇你的ATL類CFirst_ATL,右擊選擇「Implement Connection Point...」 而後,彈出對話框「Implement Connection Point」,勾上_IFirst_ATLEvents,點擊「OK」,將會生成一個事件類 事件類以下:這個CProxy_IFirst_ATLEvents<class T>就是自動生成的事件類,
雙擊事件類中的成員方法,能夠直接進入方法的代碼,系統已經幫咱們生成好了,只要調用方去實現這個事件的回調函數就能夠了。
第五步:編譯這個DLL 這個時候,從新編譯,將會生成一個DLL。若是編譯出現如下問題: error C2065: 'IID__IFirst_ATLEvents' : undeclared identifier error C2440: 'static_cast' : cannot convert from 'class CFirst_ATL *' to 'class ATL::_ICPLocator *'Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast fatal error C1903: unable to recover from previous error(s); stopping compilation error C2065: 'IID__IFirst_ATLEvents' : undeclared identifier error C2440: 'static_cast' : cannot convert from 'class CFirst_ATL *' to 'class ATL::_ICPLocator *'Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast fatal error C1903: unable to recover from previous error(s); stopping compilation 明顯,這是咱們增長了事件鏈接點後形成的,在First_ATL.h中找到 CONNECTION_POINT_ENTRY(IID__IFirst_ATLEvents) 將CONNECTION_POINT_ENTRY( IID__IFirst_ATLEvents)修改成 CONNECTION_POINT_ENTRY( DIID__IFirst_ATLEvents) 你會發現IID__IFirst_ATLEvents這個常量根本就不存在,應該是IDE生成時出錯了,咋錯的,不知道。 無論你改不改,反正我是改過就行了 無論你想不相信,到目前爲止,咱們用ATL所建立的COM已經徹底能運行!固然,還須要編譯它才行。按下「F7」功能鍵,幾秒鐘以後,VC++便會完成編譯並註冊你所建立的DLL。這樣其它的應用程序就可使用這個COM了。試一試吧! 第六步:用VB測試這個服務器 爲了用VB來測試COM服務器,首先你必須安裝VB的開發環境。(若是你沒有安裝VB或不想用VB測試,能夠跳過這一步到下一步:用VC++測試COM服 務器。)首先啓動VB,選擇建立標準EXE「Standard EXE」工程。在對話框中放一個按鈕。而後咱們要引用COM服務器。單擊「Project」菜單並選擇「References」。而後下翻頁直到能夠看見 「Simple ATL 1.0 Type Library」(如圖八),而後選中它。 |
![]() |
(圖八) |
單擊OK按鈕。如今雙擊前面在對話框中建立的命令按鈕,VB將會帶你到這個按鈕的代碼窗口。而後添加如下代碼: Private Sub Command1_Click() Dim objTestATL As SIMPLE_ATLLib.First_ATL Set objTestATL = New First_ATL Dim lngReturnValue As Long objTestATL.AddNumbers 5, 7, lngReturnValue MsgBox "The value of 5 + 7 is: " & lngReturnValue Set objTestATL = Nothing End Sub 若是你是個VB程序員,這對你來講簡直太容易了。聲明變量和對象,調用COM服務器的「AddNumbers」方法,而後顯示結果。按下「F5」功能鍵運行這個VB工程,單擊對話框中的命令按鈕,你會看到輸出的結果。(如圖九) |
![]() |
(圖九) |
下面再讓咱們用VC++測試這個COM服務器。 第六步:用VC測試這個服務器 保存並關閉Simple_ATL工程,而後建立一個新的Win32 控制檯應用程序。選擇「Win32 Console Application」並取名爲「Test_ATL」。單擊OK按鈕並接受對話框中的缺省設置(空的工程)。單擊「Finish」按鈕,而後再按OK按 鈕。這樣就建立好了一個空的工程。按下「Control+N」鍵向工程中添加一個文件。從彈出的窗口中選擇「C++ Source File」併爲它取名爲「Test_ATL.cpp」。按下OK按鈕。這樣工程中就有了一個空的.CPP文件。咱們要在這個文件中添加一些測試COM的代碼: // 將頭文件的目錄指到Simple_ATL工程所在的目錄 #include "..\Simple_ATL\Simple_ATL.h" #include // 從Simple_ATL 工程所在目錄的Simple_ATL_i.c 文件中拷貝如下內容 // 注意: 你也能夠不拷貝這些東西,而是把文件Simple_ATL_i.c包含進來。 // 我之因此將它拷進來,是想更清楚地展現這些敞亮來自什麼地方一擊它們的代碼 const IID IID_IFirst_ATL = {0xC8F6E230,0x2672,0x11D3,{0xA8,0xA8,0x00,0x10,0x5A,0xA9,0x43,0xDF}}; const CLSID CLSID_First_ATL = {0x970599E0,0x2673,0x11D3,{0xA8,0xA8,0x00,0x10,0x5A,0xA9,0x43,0xDF}}; void main(void) { // 聲明HRESULT和Simple_ATL接口指針 HRESULT hr; IFirst_ATL *IFirstATL = NULL; // 初始化COM hr = CoInitialize(0); // 使用SUCCEEDED 宏並檢查咱們是否能獲得一個接口指針 if(SUCCEEDED(hr)) { hr = CoCreateInstance( CLSID_First_ATL, NULL, CLSCTX_INPROC_SERVER, IID_IFirst_ATL, (void**) &IFirstATL); // 若是成功,則調用AddNumbers方法,不然顯示相應的出錯信息 if(SUCCEEDED(hr)) { long ReturnValue; IFirstATL->AddNumbers(5, 7, &ReturnValue); cout << "The answer for 5 + 7 is: " << ReturnValue << endl; IFirstATL->Release(); } else { cout << "CoCreateInstance Failed." << endl; } } // 釋放COM CoUninitialize(); } 第七步:編譯並運行測試程序 一樣,C#也能成功調用 |