COM 組件設計與應用(七)--編譯、註冊、調用

來源:互聯網
上載者:User
 

一、前言

  上兩回中,咱們用 ATL 寫了第一個 COM 組件程式,這回中,主要介紹編譯、註冊和調用方法。樣本程式你已經下載了嗎?如果還沒有下載,vc6.0 的使用者點這裡,vc.net 的使用者點這裡。

二、關於編譯

  2-1 最小依賴

 
 “最小依賴”,表示編譯器會把 ATL
中必須使用的一些函數靜態串連到目標程式中。這樣目標檔案尺寸會稍大,但獨立性更強,安裝方便;反之系統執行的時候需要有 ATL.DLL
檔案的支援。如何選擇設定為“最小依賴”呢?答案是:刪除預定義宏“_ATL_DLL”,操作方法見圖一、圖二。


圖一、在vc6.0中,設定方法


圖二、在 vc.net 2003中,設定方法

  2-2 CRT庫
 
 如果在 ATL 組件程式中調用了 CRT 的運行時刻庫函數,比如開平方 sqrt() ,那麼編譯的時候可能會報錯“error
LNK2001: unresolved external symbol
_main”。怎麼辦?刪除預定義宏“_ATL_MIN_CRT”!操作方法也見圖一、圖二。(vc.net 2003 中的這個項目屬性叫“在
ATL 中最小使用 CRT”)

  2-3 MBCS/UNICODE
  這個不多說了,在預定義宏中,分別使用 _MBCS 或 _UNICODE。

  2-4 IDL 的編譯
 
 COM 在設計初期,就定了一個目標:要能實現跨語言的調用。既然是跨語言的,那麼組件的介面描述就必須在任何語言環境中都要能夠認識。怎麼辦?用
.h 檔案描述?------ C語言程式員笑了,真方便!BASIC 程式員哭了:-(
因此,微軟使用了一個新的檔案格式---IDL檔案(介面定義描述語言)。IDL 是一個文字檔,它的語言文法比較簡單,很象C。具體 IDL
檔案的講解,見下一回《COM 組件設計與應用(八)之添加新介面》。IDL 經過編譯,產生二進位的等價類別型庫檔案 TLB
提供給其它語言來使用。圖三示意了 ATL COM 程式編譯的過程:


圖三、ATL 組件程式編譯過程

  說明1:編譯後,類型庫以 TLB 檔案形式單獨存在,同時也儲存在目標檔案的資源中。因此,我們將來在 #import 引入類型庫的時候,既可以指定 TLB 檔案,也可以指定目標檔案;
  說明2:我們作為 C/C++ 的程式員,還算是比較幸福的。因為 IDL 編譯後,特意為我們提供了 C 語言形式的介面檔案。
  說明3:IDL 編譯後組建代理程式/存根來源程式,有:dlldata.c、xxx_p.c、xxxps.def、xxxps.mak,我們可以用 NMAKE.EXE 再次編譯來產生真正的代理/存根DLL目標檔案(注1)。

三、關於註冊

  情況1:當我們使用 ATL 編寫組件程式,註冊不用我們來負責。編譯成功後,IDE 會幫我們自動註冊;
  情況2:當我們使用 MFC 編寫組件程式,由於編譯器不知道你寫的是否是 COM 組件,所以它不會幫我們自動註冊。這個時候,我們可以執行菜單“Tools/Register Control”來註冊。
  情況3:當我們寫一個具有 COM 功能的 EXE 程式時,註冊的方法就是運行一次這個程式;
  情況4:當我們需要使用第三方提供的組件程式時,可以命令列運行“regsvr32.exe 檔名”來註冊。順便說一句,反註冊的方法是“regsvr32.exe /u 檔案名稱”;
  情況5:當我們需要在程式中(比如安裝程式)需要執行註冊,那麼:

typedef HRESULT (WINAPI * FREG)();
TCHAR szWorkPath[ MAX_PATH ];

::GetCurrentDirectory( sizeof(szWorkPath), szWorkPath );// 儲存當前進程的工作目錄
::SetCurrentDirectory( 組件目錄 );// 切換到組件的目錄

HMODULE hDLL = ::LoadLibrary( 組件檔案名稱 );// 動態裝載組件
if(hDLL)
{
FREG lpfunc = (FREG)::GetProcAddress( hDLL, _T("DllRegisterServer") );// 取得註冊函數指標
// 如果是反註冊,可以取得"DllUnregisterServer"函數指標
if ( lpfunc )lpfunc();// 執行註冊。這裡為了簡單,沒有判斷傳回值
::FreeLibrary(hDLL);
}

::SetCurrentDirectory(szWorkPath);// 切換回原先的進程工作目錄

  上面的樣本,在多數情況下可以簡化掉切換工作目錄的代碼部分。但是,如果這個組件在裝載的時候,它需要同時載入一些必須依賴的DLL時,
有可能由於它自身程式的 BUG 導致無法正確定位。咳......還是讓我們自己寫的程式,來彌補它的錯誤吧......誰讓咱們是好人呢
,誰讓咱們的水平比他高呢,誰讓咱們在 vckbase 上是個“榜眼”呢......

四、關於組件調用

  總的來說,調用組件程式大概有如下方法:

#include 方法 IDL編譯後,為方便C/C++程式員的使用,會產生xxx.h和xxx_i.c檔案。我們真幸福,直接#include後就可以使用了
#import 方法 比較通用的方法,vc 會幫我們產生封裝類,讓我們的調用更方便
載入類型庫封裝類 方法 如果組件提供了 IDispatch 介面,用這個方法調用組件是最簡單的啦。不過還沒講IDispatch,只能看以後的文章啦
載入ActiveX封裝類 方法 ActiveX 還沒介紹呢,以後再說啦

  下載樣本程式後,請逐項瀏覽使用方法:

樣本

方法

簡要說明

1 #include 完全用最基本的 API 方式調用組件,使大家熟悉調用原理
2 #include 大部分使用 API 方式,使用 CComBSTR 簡化對字串的使用
3 #include 展示智能指標 CComPtr<> 的使用方法
4 #include 展示智能指標 CComPtr<> 和 CComQIPtr<> 混合的使用方法
5 #include 展示智能指標 CComQIPtr<> 的使用方法
6 #include 展示智能指標的釋放方法
7 #import vc 封裝的智能指標 IxxxPtr、_bstr_t、_variant_t 的使用方法和異常處理
8 #import import 後的命名空間的使用方法

  樣本程式中都寫有注釋,請讀者仔細閱讀並同時參考 MSDN 的函數說明。這裡,我給大家介紹一下“智能指標”:
  對於操作原始的接
口指標是比較麻煩的,需要我們自己控制引用記數、API 呼叫、異常處理。於是 ATL
提供了2個智能指標的模板封裝類,CComPtr<> 和 CComQIPtr<>,這兩個類都在
<atlbase.h> 中聲明。CComQIPtr<> 包含了
CComPtr<>的所有功能,因此我們可以完全用 CComQIPtr<>
來使用智能介面指標,唯一要說明的一點就是:CComQIPtr<>
由於使用了運算子的重載功能,它會自動幫我們調用QueryInterface()函數,因此 CComQIPtr<>
唯一的缺點就是不能定義 IUnknown * 指標。

    // 智能指標 smart pointer,按照匈牙利命名法,一般以 sp 開頭來表示變數類型
   CComPtr < IUnknown > spUnk;// 正確
   // 假設 IFun 是一個介面類型
   CComPtr < IFun > spFun;// 正確
   CComQIPtr < IFun > spFun;// 正確
   CComQIPtr < IFun, &IID_IFun > spFun;// 正確
   CComQIPtr < IUnknown > spUnk;// 錯誤!CComQIPtr不能定義IUnknown指標

給智能指標賦值的方法:

    CComQIPtr < IFun > spFun;// 調用建構函式,還沒有賦值,被封裝的內部介面指標為 NULL
  
   CComQIPtr < IFun > spFun( pOtherInterface );// 調用建構函式,內部介面指標賦值為
   // 通過 pOtherInterface 這個普通介面指標調用QueryInterface()得到的IFun介面指標
  
   CComQIPtr < IFun > spFun( spOtherInterface ); // 調用建構函式,內部介面指標賦值為
   // 通過 spOtherInterface 這個只能介面指標調用QueryInterface()得到的IFun介面指標
  
   CComQIPtr < IFun > spFun ( pUnknown );// 調用建構函式,由IUnknown的QueryInterface()得到IFun介面指標
  
   CComQIPtr < IFun > spFun = pOtherInterface;// = 運算子多載,含義和上面一樣
   spFun = spOtherInterface;// 同上
   spFun = pUnknown;// 同上
  
   pUnknown->QueryInterface( IID_IFun, &sp );// 也可以通過QueryInterface賦值
  
   // 智能指標賦值後,可以用條件陳述式判斷是否合法有效
   if ( spFun ){}// 如果指標有效
   if ( NULL != spFun ){}// 如果指標有效
  
   if ( !spFun ){}// 如果指標無效
   if ( NULL == spFun ){}// 如果指標無效

智能指標調用函數的方法:

    spFun.CoCreateInstance(...);// 等價與 API 函數::CoCreateInstance(...)
   spFun.QueryInterface(...);// 等價與 API 函數::QueryInterface()
  
   spFun->Add(...);// 調用內部介面指標的介面函數

   // 調用內部介面指標的QueryInterface()函數,其實效果和 spFun.QueryInterface(...) 一樣
   spFun->QueryInterface(...);
  
   spFun.Release();// 釋放內部的介面指標,同時內部指標賦值為 NULL
   spFun->Release();// 錯!!!一定不要這麼使用。
   // 因為這個調用並不把內部指標清空,那麼析構的時候會被再次釋放(釋放了兩次)

咳......不說了,不說了,大家多看書,多看MSND,多看樣本程式吧。 寫累了:-(

五、小結

  敬請關注《COM 組件設計與應用(八)》------如何增加 ATL 組件中的第二個介面

注1:編譯代理/存根,vc6.0 中稍微麻煩,我們在後面介紹“進程外組件”和“遠程組件”的時候再介紹。在 vc.net 2003 下則比較簡單,因為代理/存根作為單獨的一個工程項目會自動加到我們的解決方案中了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.