• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-250  評(píng)論-20  文章-55  trackbacks-0

            一、前言

              上兩回中,咱們用 ATL 寫(xiě)了第一個(gè) COM 組件程序,這回中,主要介紹編譯、注冊(cè)和調(diào)用方法。示例程序你已經(jīng)下載了嗎?如果還沒(méi)有下載,vc6.0 的用戶點(diǎn)這里,vc.net 的用戶點(diǎn)這里


            二、關(guān)于編譯

              2-1 最小依賴

              “最小依賴”,表示編譯器會(huì)把 ATL 中必須使用的一些函數(shù)靜態(tài)連接到目標(biāo)程序中。這樣目標(biāo)文件尺寸會(huì)稍大,但獨(dú)立性更強(qiáng),安裝方便;反之系統(tǒng)執(zhí)行的時(shí)候需要有 ATL.DLL 文件的支持。如何選擇設(shè)置為“最小依賴”呢?答案是:刪除預(yù)定義宏“_ATL_DLL”,操作方法見(jiàn)圖一、圖二。


            圖一、在vc6.0中,設(shè)置方法


            圖二、在 vc.net 2003中,設(shè)置方法

              2-2 CRT庫(kù)
              如果在 ATL 組件程序中調(diào)用了 CRT 的運(yùn)行時(shí)刻庫(kù)函數(shù),比如開(kāi)平方 sqrt() ,那么編譯的時(shí)候可能會(huì)報(bào)錯(cuò)“error LNK2001: unresolved external symbol _main”。怎么辦?刪除預(yù)定義宏“_ATL_MIN_CRT”!操作方法也見(jiàn)圖一、圖二。(vc.net 2003 中的這個(gè)項(xiàng)目屬性叫“在 ATL 中最小使用 CRT”)

              2-3 MBCS/UNICODE
              這個(gè)不多說(shuō)了,在預(yù)定義宏中,分別使用 _MBCS 或 _UNICODE。

              2-4 IDL 的編譯
              COM 在設(shè)計(jì)初期,就定了一個(gè)目標(biāo):要能實(shí)現(xiàn)跨語(yǔ)言的調(diào)用。既然是跨語(yǔ)言的,那么組件的接口描述就必須在任何語(yǔ)言環(huán)境中都要能夠認(rèn)識(shí)。怎么辦?用 .h 文件描述?------ C語(yǔ)言程序員笑了,真方便!BASIC 程序員哭了:-( 因此,微軟使用了一個(gè)新的文件格式---IDL文件(接口定義描述語(yǔ)言)。IDL 是一個(gè)文本文件,它的語(yǔ)言語(yǔ)法比較簡(jiǎn)單,很象C。具體 IDL 文件的講解,見(jiàn)下一回《COM 組件設(shè)計(jì)與應(yīng)用(八)之添加新接口》。IDL 經(jīng)過(guò)編譯,生成二進(jìn)制的等價(jià)類型庫(kù)文件 TLB 提供給其它語(yǔ)言來(lái)使用。圖三示意了 ATL COM 程序編譯的過(guò)程:


            圖三、ATL 組件程序編譯過(guò)程

              說(shuō)明1:編譯后,類型庫(kù)以 TLB 文件形式單獨(dú)存在,同時(shí)也保存在目標(biāo)文件的資源中。因此,我們將來(lái)在 #import 引入類型庫(kù)的時(shí)候,既可以指定 TLB 文件,也可以指定目標(biāo)文件;
              說(shuō)明2:我們作為 C/C++ 的程序員,還算是比較幸福的。因?yàn)?IDL 編譯后,特意為我們提供了 C 語(yǔ)言形式的接口文件。
              說(shuō)明3:IDL 編譯后生成代理/存根源程序,有:dlldata.c、xxx_p.c、xxxps.def、xxxps.mak,我們可以用 NMAKE.EXE 再次編譯來(lái)產(chǎn)生真正的代理/存根DLL目標(biāo)文件(注1)。


            三、關(guān)于注冊(cè)

              情況1:當(dāng)我們使用 ATL 編寫(xiě)組件程序,注冊(cè)不用我們來(lái)負(fù)責(zé)。編譯成功后,IDE 會(huì)幫我們自動(dòng)注冊(cè);
              情況2:當(dāng)我們使用 MFC 編寫(xiě)組件程序,由于編譯器不知道你寫(xiě)的是否是 COM 組件,所以它不會(huì)幫我們自動(dòng)注冊(cè)。這個(gè)時(shí)候,我們可以執(zhí)行菜單“Tools\Register Control”來(lái)注冊(cè)。
              情況3:當(dāng)我們寫(xiě)一個(gè)具有 COM 功能的 EXE 程序時(shí),注冊(cè)的方法就是運(yùn)行一次這個(gè)程序;
              情況4:當(dāng)我們需要使用第三方提供的組件程序時(shí),可以命令行運(yùn)行“regsvr32.exe 文件名”來(lái)注冊(cè)。順便說(shuō)一句,反注冊(cè)的方法是“regsvr32.exe /u 文件名”;
              情況5:當(dāng)我們需要在程序中(比如安裝程序)需要執(zhí)行注冊(cè),那么:

            typedef HRESULT (WINAPI * FREG)();
            TCHAR szWorkPath[ MAX_PATH ];
            
            ::GetCurrentDirectory( sizeof(szWorkPath), szWorkPath );	// 保存當(dāng)前進(jìn)程的工作目錄
            ::SetCurrentDirectory( 組件目錄 );	// 切換到組件的目錄
            
            HMODULE hDLL = ::LoadLibrary( 組件文件名 );	// 動(dòng)態(tài)裝載組件
            if(hDLL)
            {
            	FREG lpfunc = (FREG)::GetProcAddress( hDLL, _T("DllRegisterServer") );	// 取得注冊(cè)函數(shù)指針
            	// 如果是反注冊(cè),可以取得"DllUnregisterServer"函數(shù)指針
            	if ( lpfunc )	lpfunc();	// 執(zhí)行注冊(cè)。這里為了簡(jiǎn)單,沒(méi)有判斷返回值
            	::FreeLibrary(hDLL);
            }
            
            ::SetCurrentDirectory(szWorkPath);	// 切換回原先的進(jìn)程工作目錄
            
              上面的示例,在多數(shù)情況下可以簡(jiǎn)化掉切換工作目錄的代碼部分。但是,如果這個(gè)組件在裝載的時(shí)候,它需要同時(shí)加載一些必須依賴的DLL時(shí),有可能由于它自身程序的 BUG 導(dǎo)致無(wú)法正確定位。咳......還是讓我們自己寫(xiě)的程序,來(lái)彌補(bǔ)它的錯(cuò)誤吧......誰(shuí)讓咱們是好人呢 ,誰(shuí)讓咱們的水平比他高呢,誰(shuí)讓咱們?cè)?vckbase 上是個(gè)“榜眼”呢......


            四、關(guān)于組件調(diào)用

              總的來(lái)說(shuō),調(diào)用組件程序大概有如下方法:
             
            #include 方法IDL編譯后,為方便C/C++程序員的使用,會(huì)產(chǎn)生xxx.h和xxx_i.c文件。我們真幸福,直接#include后就可以使用了
            #import 方法比較通用的方法,vc 會(huì)幫我們產(chǎn)生包裝類,讓我們的調(diào)用更方便
            加載類型庫(kù)包裝類 方法如果組件提供了 IDispatch 接口,用這個(gè)方法調(diào)用組件是最簡(jiǎn)單的啦。不過(guò)還沒(méi)講IDispatch,只能看以后的文章啦
            加載ActiveX包裝類 方法ActiveX 還沒(méi)介紹呢,以后再說(shuō)啦

              下載示例程序后,請(qǐng)逐項(xiàng)瀏覽使用方法:

            示例

            方法

            簡(jiǎn)要說(shuō)明

            1#include完全用最基本的 API 方式調(diào)用組件,使大家熟悉調(diào)用原理
            2#include大部分使用 API 方式,使用 CComBSTR 簡(jiǎn)化對(duì)字符串的使用
            3#include展示智能指針 CComPtr<> 的使用方法
            4#include展示智能指針 CComPtr<> 和 CComQIPtr<> 混合的使用方法
            5#include展示智能指針 CComQIPtr<> 的使用方法
            6#include展示智能指針的釋放方法
            7#importvc 包裝的智能指針 IxxxPtr、_bstr_t、_variant_t 的使用方法和異常處理
            8#importimport 后的命名空間的使用方法

              示例程序中都寫(xiě)有注釋,請(qǐng)讀者仔細(xì)閱讀并同時(shí)參考 MSDN 的函數(shù)說(shuō)明。這里,我給大家介紹一下“智能指針”:
              對(duì)于操作原始的接口指針是比較麻煩的,需要我們自己控制引用記數(shù)、API 調(diào)用、異常處理。于是 ATL 提供了2個(gè)智能指針的模板包裝類,CComPtr<> 和 CComQIPtr<>,這兩個(gè)類都在 <atlbase.h> 中聲明。CComQIPtr<> 包含了 CComPtr<>的所有功能,因此我們可以完全用 CComQIPtr<> 來(lái)使用智能接口指針,唯一要說(shuō)明的一點(diǎn)就是:CComQIPtr<> 由于使用了運(yùn)算符的重載功能,它會(huì)自動(dòng)幫我們調(diào)用QueryInterface()函數(shù),因此 CComQIPtr<> 唯一的缺點(diǎn)就是不能定義 IUnknown * 指針。

                // 智能指針 smart pointer,按照匈牙利命名法,一般以 sp 開(kāi)頭來(lái)表示變量類型
                CComPtr < IUnknown > spUnk;	// 正確
                // 假設(shè) IFun 是一個(gè)接口類型
                CComPtr < IFun > spFun;	// 正確
                CComQIPtr < IFun > spFun;	// 正確
                CComQIPtr < IFun, &IID_IFun > spFun;	// 正確
                CComQIPtr < IUnknown > spUnk;	// 錯(cuò)誤!CComQIPtr不能定義IUnknown指針
            給智能指針賦值的方法:
                CComQIPtr < IFun > spFun;	// 調(diào)用構(gòu)造函數(shù),還沒(méi)有賦值,被包裝的內(nèi)部接口指針為 NULL
                
                CComQIPtr < IFun > spFun( pOtherInterface );	// 調(diào)用構(gòu)造函數(shù),內(nèi)部接口指針賦值為
                // 通過(guò) pOtherInterface 這個(gè)普通接口指針調(diào)用QueryInterface()得到的IFun接口指針
                
                CComQIPtr < IFun > spFun( spOtherInterface ); // 調(diào)用構(gòu)造函數(shù),內(nèi)部接口指針賦值為
                // 通過(guò) spOtherInterface 這個(gè)只能接口指針調(diào)用QueryInterface()得到的IFun接口指針
                
                CComQIPtr < IFun > spFun ( pUnknown );	// 調(diào)用構(gòu)造函數(shù),由IUnknown的QueryInterface()得到IFun接口指針
                
                CComQIPtr < IFun > spFun = pOtherInterface;	// = 運(yùn)算符重載,含義和上面一樣
                spFun = spOtherInterface;	// 同上
                spFun = pUnknown;	// 同上
                
                pUnknown->QueryInterface( IID_IFun, &sp );	// 也可以通過(guò)QueryInterface賦值
                
                // 智能指針賦值后,可以用條件語(yǔ)句判斷是否合法有效
                if ( spFun ){}		// 如果指針有效
                if ( NULL != spFun ){}	// 如果指針有效
                
                if ( !spFun ){}		// 如果指針無(wú)效
                if ( NULL == spFun ){}	// 如果指針無(wú)效
            智能指針調(diào)用函數(shù)的方法:
                spFun.CoCreateInstance(...);	// 等價(jià)與 API 函數(shù)::CoCreateInstance(...)
                spFun.QueryInterface(...);	// 等價(jià)與 API 函數(shù)::QueryInterface()
                
                spFun->Add(...);	// 調(diào)用內(nèi)部接口指針的接口函數(shù)
            
                // 調(diào)用內(nèi)部接口指針的QueryInterface()函數(shù),其實(shí)效果和 spFun.QueryInterface(...) 一樣
                spFun->QueryInterface(...);	
                
                spFun.Release();	// 釋放內(nèi)部的接口指針,同時(shí)內(nèi)部指針賦值為 NULL
                spFun->Release();	// 錯(cuò)!!!一定不要這么使用。
                // 因?yàn)檫@個(gè)調(diào)用并不把內(nèi)部指針清空,那么析構(gòu)的時(shí)候會(huì)被再次釋放(釋放了兩次)
            咳......不說(shuō)了,不說(shuō)了,大家多看書(shū),多看MSND,多看示例程序吧。 寫(xiě)累了:-(

            五、小結(jié)

              敬請(qǐng)關(guān)注《COM 組件設(shè)計(jì)與應(yīng)用(八)》------如何增加 ATL 組件中的第二個(gè)接口


            注1:編譯代理/存根,vc6.0 中稍微麻煩,我們?cè)诤竺娼榻B“進(jìn)程外組件”和“遠(yuǎn)程組件”的時(shí)候再介紹。在 vc.net 2003 下則比較簡(jiǎn)單,因?yàn)榇?存根作為單獨(dú)的一個(gè)工程項(xiàng)目會(huì)自動(dòng)加到我們的解決方案中了。
            posted on 2007-03-12 11:01 jay 閱讀(343) 評(píng)論(0)  編輯 收藏 引用 所屬分類: ATL
            久久人人爽人人爽人人片AV东京热 | 久久亚洲天堂| 女人高潮久久久叫人喷水| 亚洲AV日韩AV永久无码久久| 久久国产精品国语对白| 国产三级久久久精品麻豆三级| 亚洲Av无码国产情品久久| 精品综合久久久久久88小说| 要久久爱在线免费观看| 久久久久亚洲精品中文字幕| 伊人久久大香线蕉av不变影院| 久久人人爽人人爽人人片AV东京热| 99久久精品费精品国产一区二区| 久久精品国产亚洲av麻豆色欲 | 人妻无码αv中文字幕久久琪琪布| 久久精品成人免费看| 久久久噜噜噜久久熟女AA片| 色综合久久久久综合99| 精品久久久久久中文字幕| 国产A级毛片久久久精品毛片| 久久er国产精品免费观看8| AV狠狠色丁香婷婷综合久久 | 777久久精品一区二区三区无码| 国产精品美女久久久久久2018| 色婷婷噜噜久久国产精品12p| 久久精品国产精品亚洲精品| 久久久精品人妻一区二区三区蜜桃| 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 久久99精品国产99久久6| 国产婷婷成人久久Av免费高清| 亚洲狠狠婷婷综合久久久久| 色婷婷综合久久久久中文字幕| 久久久久国产日韩精品网站| 日本WV一本一道久久香蕉| 伊人精品久久久久7777| 亚洲第一永久AV网站久久精品男人的天堂AV | 久久最新精品国产| 久久有码中文字幕| 久久免费视频6| 亚洲国产成人久久精品99 | 久久国产精品一区|