簡單com編寫流程及註冊事項

本文的對象是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

STDMETHODIMP CDispSimple::Add(VARIANT v1, VARIANT v2, VARIANT *pVal)
{
::VariantInit( pVal ); // 永遠初始化返回值是個好習慣
CComVariant v_1( v1 );
CComVariant v_2( v2 );
if((v1.vt & VT_I4) && (v2.vt & VT_I4) ) // 若是都是整數類型
{ // 這裏比較沒有使用 == ,而使用了運算符 & ,你知道這是爲何嗎?
v_1.ChangeType( VT_I4 ); // 轉換爲整數
v_2.ChangeType( VT_I4 ); // 轉換爲整數
pVal->vt = VT_I4;
pVal->lVal = v_1.lVal + v_2.lVal; // 加法
}
else
{
v_1.ChangeType( VT_BSTR ); // 轉換爲字符串
v_2.ChangeType( VT_BSTR ); // 轉換爲字符串
CComBSTR bstr( v_1.bstrVal );
bstr.AppendBSTR( v_2.bstrVal ); // 字符串鏈接
pVal->vt = VT_BSTR;
pVal->bstrVal = bstr.Detach();
}
return S_OK;
}
STDMETHODIMP CDispSimple::Upper(BSTR str, BSTR *pVal)
{
*pVal = NULL; // 永遠初始化返回值是個好習慣
CComBSTR s(str);
s.ToUpper(); // 轉換爲大寫
*pVal = s.Copy();
線程

return S_OK;

//其它方法 

// CString m_str("返回內容");
   // *pVal=m_str.AllocSysString();


}

以上幾種參數格式,能夠自由組合使用,包括函數返回值、指針返回值的各類類型, 傳入參數的各類普通、指針類型。

 

第四步:添加事件:

若是你單擊工做間的「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。若是編譯出現如下問題:
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生成時出錯了,咋錯的,不知道。
無論你改不改,反正我是改過就行了
第五步:編譯這個DLL 
  無論你想不相信,到目前爲止,咱們用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#也能成功調用

相關文章
相關標籤/搜索