青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

面對現(xiàn)實(shí),超越自己
逆水行舟,不進(jìn)則退
posts - 269,comments - 32,trackbacks - 0

本文轉(zhuǎn)自:http://www.vckbase.com/document/viewdoc/?id=212

     本文的目的是為剛剛接觸COM的程序員提供編程指南,并幫助他們理解COM的基本概念。內(nèi)容包括COM規(guī)范簡介,重要的COM術(shù)語以及如何重用現(xiàn)有的COM組件。本文不包括如何編寫自己的COM對象和接口。
  COM即組件對象模型,是Component Object Model 取前三個字母的縮寫,這三個字母在當(dāng)今Windows的世界中隨處可見。隨時涌現(xiàn)出來的大把大把的新技術(shù)都以COM為基礎(chǔ)。各種文檔中也充斥著諸如COM對象、接口、服務(wù)器之類的術(shù)語。因此,對于一個程序員來說,不僅要掌握使用COM的方法,而且還要徹底熟悉COM的所有一切。
  本文由淺入深描述COM的內(nèi)在運(yùn)行機(jī)制,教你如何使用第三方提供的COM對象(以Windows 外殼組件Shell為例)。讀完本文后,你就能掌握如何使用Windows操作系統(tǒng)中內(nèi)建的組件和第三方提供的COM對象。
  本文假設(shè)你精通C++語言。在例子代碼中使用了一點(diǎn)MFC和ATL,如果你不熟悉MFC和ATL也沒關(guān)系,本文會對這些代碼進(jìn)行完全透徹的解釋。本文包括以下幾個部分:

  • COM——到底是什么?——COM標(biāo)準(zhǔn)的要點(diǎn)介紹,它被設(shè)計用來解決什么問題

  • 基本元素的定義——COM術(shù)語以及這些術(shù)語的含義

  • 使用和處理COM對象——如何創(chuàng)建、使用和銷毀COM對象

  • 基本接口——描述IUnknown基本接口及其方法

  • 掌握串的處理——在COM代碼中如何處理串

  • 應(yīng)用COM技術(shù)——例子代碼,舉例說明本文所討論的所有概念

  • 處理HRESULT——HRESULT類型描述,如何監(jiān)測錯誤及成功代碼

COM——到底是什么 
      COM是由Microsoft提出的標(biāo)準(zhǔn)組件,它不僅定義了組件程序之間進(jìn)行交互的標(biāo)準(zhǔn),并且也提供了組件程序運(yùn)行所需要的環(huán)境。在COM標(biāo)準(zhǔn)中,一個組件程序也被稱為一個模塊,它可以是一個動態(tài)鏈接庫(DLL),被稱作進(jìn)程內(nèi)組件;也可以是一個可執(zhí)行程序,被稱作進(jìn)程外組件,一個組件程序可以包含一個或多個組件對象,因?yàn)镃OM是以對象為基本單元的模型,所以在程序和程序之間進(jìn)行通信時,通信的雙方應(yīng)該是組件對象,也叫COM對象,而組件程序(或叫COM 程序)是提供COM對象的代碼載體。
      COM對象不同于一般面向?qū)ο笳Z言(如C++語言)中的對象概念,COM對象是建立在二進(jìn)制可執(zhí)行代碼級的基礎(chǔ)上,而C++等語言中的對象是建立在源代碼級基礎(chǔ)上的,因此COM對象是語言無關(guān)的。這一特性使用不同編程語言開發(fā)的組件對象進(jìn)行交互稱為可能。
      Microsoft不僅僅定義了COM規(guī)范標(biāo)準(zhǔn),而且也提供了具體的實(shí)現(xiàn),在Windows系統(tǒng)平臺上,COM技術(shù)被應(yīng)用于系統(tǒng)的各個層次,從底層的COM對象管理到上層的應(yīng)用程序交互都用到了COM標(biāo)準(zhǔn)。

基本元素的定義

  我們從下往上看。接口是一組邏輯上相關(guān)的函數(shù)集合。這些函數(shù)被稱接口成員函數(shù)。接口名字以大寫的I開頭。接口可以從其它接口繼承,這里所說的繼承的原理就好像C++中的單繼承。接口是不允許多繼承的。coclass(簡稱組件對象類——component object class)被包含在DLL或EXE中,并且包含著一個或者多個接口的代碼。組件對象類(coclasss)實(shí)現(xiàn)這些接口。COM對象在內(nèi)存中表現(xiàn)為組件對象類(coclasss)的一個實(shí)例。注意COM“類”和C++“類”是不相同的,盡管常常COM類實(shí)現(xiàn)的就是一個C++類。

COM服務(wù)器是包含了一個或多個coclass的二進(jìn)制(DLL或EXE)。

注冊(Registration)是創(chuàng)建注冊表入口的一個過程,告訴Windows 操作系統(tǒng)COM服務(wù)器放在什么位置。取消注冊(Unregistration)則相反——從注冊表刪除這些注冊入口。

GUID(諧音為“fluid”,意思是全球唯一標(biāo)示符——globally unique identifier)是個128位的數(shù)字。它是一種獨(dú)立于COM編程語言的標(biāo)示方法。每一個接口和coclass有一個GUID。因?yàn)槊恳粋€GUID都是全球唯一的,所以避免了名字沖突(只要你用COM API創(chuàng)建它們)。有時你還會碰到另一個術(shù)語UUID(意思也是全球唯一標(biāo)示符——universally unique identifier)。UUIDs和GUIDs在實(shí)際使用時的用途是一樣的。

類ID或者CLSID是命名coclass的GUID。接口ID或者IID是命名接口的GUID。

在COM中廣泛地使用GUID有兩個理由:

  1. GUIDs只是簡單的數(shù)字,任何編程語言都可以對之進(jìn)行處理;

  2. GUIDs可以在任何機(jī)器上被任何人創(chuàng)建,一旦完成創(chuàng)建,它就是唯一的。因此,COM開發(fā)人員可以創(chuàng)建自己特有的GUIDs而不會與其它開發(fā)人員所創(chuàng)建的GUIDs有沖突。這樣就消除了集中授權(quán)發(fā)布GUIDs的必要。

  HRESULT是COM用來返回錯誤和成功代碼的整型數(shù)字。除此之外,別無它意,雖然以H作前綴,但沒有句柄之意。下文會對它有更多的討論。
  最后,COM庫是在你使用COM時與你交互的操作系統(tǒng)的一部分,它常常指的就是COM本身。但是為了避免混淆才分開描述的。

使用和處理COM對象

  每一種語言都有其自己處理對象的方式。例如,C++是在棧中創(chuàng)建對象,或者用new動態(tài)分配。因?yàn)镃OM必須獨(dú)立于語言,所以COM庫為自己提供對象管理例程。下面是對COM對象管理和C++對象管理所做的一個比較:

創(chuàng)建一個新對象

C++中,用new操作符,或者在棧中創(chuàng)建對象。
COM中,調(diào)用COM庫中的API。

刪除對象

C++中,用delete操作符,或?qū)ο筇叱觥?/span>
COM中,所有的對象保持它們自己的引用計數(shù)。調(diào)用者必須通知對象什么時候用完這個對象。當(dāng)引用計數(shù)為零時,COM對象將自己從內(nèi)存中釋放。
  由此可見,對象處理的兩個階段:創(chuàng)建和銷毀,缺一不可。當(dāng)創(chuàng)建COM對象時要通知COM庫使用哪一個接口。如果這個對象創(chuàng)建成功,COM庫返回所請求接口的指針。然后通過這個指針調(diào)用方法,就像使用常規(guī)C++對象指針一樣。

創(chuàng)建COM對象

為了創(chuàng)建COM對象并從這個對象獲得接口,必須調(diào)用COM庫的API函數(shù),CoCreateInstance()。其原型如下:

HRESULT CoCreateInstance ( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv );

以下是參數(shù)解釋:

rclsid:coclass的CLSID,例如,可以傳遞CLSID_ShellLink創(chuàng)建一個COM對象來建立快捷方式。 pUnkOuter:這個參數(shù)只用于COM對象的聚合,利用它向現(xiàn)有的coclass添加新方法。參數(shù)值為null表示不使用聚合。 dwClsContext:表示所使用COM服務(wù)器的種類。本文使用的是最簡單的COM服務(wù)器,一個進(jìn)程內(nèi)(in-process)DLL,        所以傳遞的參數(shù)值為CLSCTX_INPROC_SERVER。注意這里不要隨意使用CLSCTX_ALL(在ATL中,它是個缺省值),        因?yàn)樵跊]有安裝DCOM的Windows95系統(tǒng)上會導(dǎo)致失敗。 riid:請求接口的IID。例如,可以傳遞IID_IShellLink獲得IShellLink接口指針。 ppv:接口指針的地址。COM庫通過這個參數(shù)返回請求的接口。

  當(dāng)你調(diào)用CoCreateInstance()時,它負(fù)責(zé)在注冊表中查找COM服務(wù)器的位置,將服務(wù)器加載到內(nèi)存,并創(chuàng)建你所請求的coclass實(shí)例。以下是一個調(diào)用的例子,創(chuàng)建一個CLSID_ShellLink對象的實(shí)例并請求指向這個對象IShellLink接口指針。

 1 HRESULT     hr;
 2 IShellLink* pISL;
 3     hr = CoCreateInstance ( CLSID_ShellLink,         // coclass 的CLSID 
 4                             NULL,                    // 不是用聚合
 5                            CLSCTX_INPROC_SERVER,    // 服務(wù)器類型
 6                             IID_IShellLink,          // 接口的IID 
 7                             (void**&pISL );        // 指向接口的指針
 8 
 9     if ( SUCCEEDED ( hr ) )
10         {
11         // 用pISL調(diào)用方法
12         }
13     else
14         {
15         // 不能創(chuàng)建COM對象,hr 為出錯代碼
16         }

  首先聲明一個接受CoCreateInstance()返回值的HRESULT和IShellLink指針。調(diào)用CoCreateInstance()來創(chuàng)建新的COM對象。如果hr接受到一個表示成功的代碼,則SUCCEEDED宏返回TRUE,否則返回FALSE。FAILED是一個與SUCCEEDED對應(yīng)的宏用來檢查失敗代碼。

刪除COM對象

  前面說過,你不用釋放COM對象,只要告訴它們你已經(jīng)用完對象。IUnknown是每一個COM對象必須實(shí)現(xiàn)的接口,它有一個方法,Release()。調(diào)用這個方法通知COM對象你不再需要對象。一旦調(diào)用了這個方法之后,就不能再次使用這個接口,因?yàn)檫@個COM對象可能從此就從內(nèi)存中消失了。
  如果你的應(yīng)用程序使用許多不同的COM對象,因此在用完某個接口后調(diào)用Release()就顯得非常重要。如果你不釋放接口,這個COM對象(包含代碼的DLLs)將保留在內(nèi)存中,這會增加不必要的開銷。如果你的應(yīng)用程序要長時間運(yùn)行,就應(yīng)該在應(yīng)用程序處于空閑期間調(diào)用CoFreeUnusedLibraries() API。這個API將卸載任何沒有明顯引用的COM服務(wù)器,因此這也降低了應(yīng)用程序使用的內(nèi)存開銷。
繼續(xù)用上面的例子來說明如何使用Release():

// 像上面一樣創(chuàng)建COM 對象, 然后, if ( SUCCEEDED ( hr ) ) { // 用pISL調(diào)用方法 // 通知COM 對象不再使用它 pISL->Release(); }

接下來將詳細(xì)討論IUnknown接口。

基本接口——IUnknown

  每一個COM接口都派生于IUnknown。這個名字有點(diǎn)誤導(dǎo)人,其中沒有未知(Unknown)接口的意思。它的原意是如果有一個指向某COM對象的IUnknown指針,就不用知道潛在的對象是什么,因?yàn)槊總€COM對象都實(shí)現(xiàn)IUnknown。

IUnknown 有三個方法:

  • AddRef() —— 通知COM對象增加它的引用計數(shù)。如果你進(jìn)行了一次接口指針的拷貝,就必須調(diào)用一次這個方法,并且原始的值和拷貝的值兩者都要用到。在本文的例子中沒有用到AddRef()方法;

  • Release() —— 通知COM對象減少它的引用計數(shù)。參見前面的Release()示例代碼段;

  • QueryInterface() —— 從COM對象請求一個接口指針。當(dāng)coclass實(shí)現(xiàn)一個以上的接口時,就要用到這個方法;

  前面已經(jīng)看到了Release()的使用,但如何使用QueryInterface()呢?當(dāng)你用CoCreateInstance()創(chuàng)建對象的時候,你得到一個返回的接口指針。如果這個COM對象實(shí)現(xiàn)一個以上的接口(不包括IUnknown),你就必須用QueryInterface()方法來獲得任何你需要的附加的接口指針。QueryInterface()的原型如下:

HRESULT IUnknown::QueryInterface ( REFIID iid, void** ppv );

以下是參數(shù)解釋:

iid:所請求的接口的IID。 ppv:接口指針的地址,QueryInterface()通過這個參數(shù)在成功時返回這個接口。

  讓我們繼續(xù)外殼鏈接的例子。它實(shí)現(xiàn)了IShellLink 和IPersistFile接口。如果你已經(jīng)有一個IShellLink指針,pISL,可以從COM對象請求IPersistFile接口:

HRESULT hr; IPersistFile* pIPF; hr = pISL->QueryInterface ( IID_IPersistFile, (void**) &pIPF );

  然后使用SUCCEEDED宏檢查hr的值以確定QueryInterface()的調(diào)用情況,如果成功的話你就可以象使用其它接口指針那樣使用新的接口指針,pIPF。但必須記住調(diào)用pIPF->Release()通知COM對象已經(jīng)用完這個接口。

仔細(xì)做好串處理

  這一部分將花點(diǎn)時間來討論如何在COM代碼中處理串。如果你熟悉Unicode 和ANSI,并知道如何對它們進(jìn)行轉(zhuǎn)換的話,你就可以跳過這一部分,否則還是讀一下這一部分的內(nèi)容。
  不管什么時候,只要COM方法返回一個串,這個串都是Unicode串(這里指的是寫入COM規(guī)范的所有方法)。Unicode是一種字符編碼集,類似ASCII,但用兩個字節(jié)表示一個字符。如果你想更好地控制或操作串的話,應(yīng)該將它轉(zhuǎn)換成TCHAR類型串。
  TCHAR和以_t開頭的函數(shù)(如_tcscpy())被設(shè)計用來讓你用相同的源代碼處理Unicode和ANSI串。在大多數(shù)情況下編寫的代碼都是用來處理ANSI串和ANSI WindowsAPIs,所以在下文中,除非另外說明,我所說的字符/串都是指TCHAR類型。你應(yīng)該熟練掌握TCHAR類型,尤其是當(dāng)你閱讀其他人寫的有關(guān)代碼時,要特別注意TCHAR類型。
  當(dāng)你從某個COM方法返回得到一個Unicode串時,可以用下列幾種方法之一將它轉(zhuǎn)換成char類型串:

  1. 調(diào)用 WideCharToMultiByte() API;

  2. 調(diào)用CRT 函數(shù)wcstombs();

  3. 使用CString 構(gòu)造器或賦值操作(僅用于MFC );

  4. 使用ATL 串轉(zhuǎn)換宏;

WideCharToMultiByte()

你可以用WideCharToMultiByte()將一個Unicode串轉(zhuǎn)換成一個ANSI串。此函數(shù)的原型如下:

int WideCharToMultiByte ( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar );

以下是參數(shù)解釋:

  • CodePage:Unicode字符轉(zhuǎn)換成的代碼頁。你可以傳遞CP_ACP來使用當(dāng)前的ANSI代碼頁。代碼頁是256個字符集。字符0——127與ANSI編碼一樣。字符128——255與ANSI字符不同,它可以包含圖形字符或者讀音符號。每一種語言或地區(qū)都有其自己的代碼頁,所以使用正確的代碼頁對于正確地顯示重音字符很重要。

  • dwFlags:dwFlags 確定Windows如何處理“復(fù)合” Unicode字符,它是一種后面帶讀音符號的字符。
    如è就是一個復(fù)合字符。如果這些字符在CodePage參數(shù)指定的代碼頁中,不會出什么事。
    否則,Windows必須對之進(jìn)行轉(zhuǎn)換。傳遞WC_COMPOSITECHECK使得這個API檢查非映射復(fù)合字符。
    傳遞WC_SEPCHARS使得Windows將字符分為兩段,即字符加讀音,如e`。
    傳遞WC_DISCARDNS使得Windows丟棄讀音符號。
    傳遞WC_DEFAULTCHAR使得Windows用lpDefaultChar參數(shù)中說明的缺省字符替代復(fù)合字符。
    缺省行為是WC_SEPCHARS。

  • lpWideCharStr 要轉(zhuǎn)換的Unicode串。

  • cchWideChar lpWideCharStr在Unicode 字符中的長度。通常傳遞-1,表示這個串是以0x00結(jié)尾。

  • lpMultiByteStr 接受轉(zhuǎn)換的串的字符緩沖 cbMultiByte lpMultiByteStr的字節(jié)大小。

  • lpDefaultChar 可選——當(dāng)dwFlags包含WC_COMPOSITECHECK | WC_DEFAULTCHAR并且某個Unicode字符不能被映射到同等的ANSI串時所傳遞的一個單字符ANSI串,包含被插入的“缺省”字符。可以傳遞NULL,讓API使用系統(tǒng)缺省字符(一種寫法是一個問號)。

  • lpUsedDefaultChar 可選——指向BOOL類型的一個指針,設(shè)置它來表示是否缺省字符曾被插入ANSI串。可以傳遞NULL來忽略這個參數(shù)。

  我自己都有點(diǎn)暈菜了……!,萬事開頭難啊……,不搞清楚這些東西就很難搞清楚COM的串處理。何況文檔中列出的比實(shí)際應(yīng)用的要復(fù)雜得 多。下面就給出了如何使用這個API的例子:

// 假設(shè)已經(jīng)有了一個Unicode 串 wszSomeString... char szANSIString [MAX_PATH]; WideCharToMultiByte ( CP_ACP, // ANSI 代碼頁 WC_COMPOSITECHECK, // 檢查重音字符 wszSomeString, // 原Unicode 串 -1, // -1 意思是串以0x00結(jié)尾 szANSIString, // 目的char字符串 sizeof(szANSIString), // 緩沖大小 NULL, // 肥缺省字符串 NULL ); // 忽略這個參數(shù)

調(diào)用這個函數(shù)后,szANSIString將包含Unicode串的ANSI版本。調(diào)用這個函數(shù)后,szANSIString將包含Unicode串的ANSI版本。

wcstombs()

這個CRT函數(shù)wcstombs()是個簡化版,但它終結(jié)了WideCharToMultiByte()的調(diào)用,所以最終結(jié)果是一樣的。其原型如下:

size_t wcstombs ( char* mbstr, const wchar_t* wcstr, size_t count );

以下是參數(shù)解釋:

mbstr:接受結(jié)果ANSI串的字符(char)緩沖。 wcstr:要轉(zhuǎn)換的Unicode串。count:mbstr參數(shù)所指的緩沖大小。

  wcstombs()在它對WideCharToMultiByte()的調(diào)用中使用WC_COMPOSITECHECK | WC_SEPCHARS標(biāo)志。用wcstombs()轉(zhuǎn)換前面例子中的Unicode串,結(jié)果一樣:

wcstombs ( szANSIString, wszSomeString, sizeof(szANSIString) );

CString

MFC中的CString包含有構(gòu)造函數(shù)和接受Unicode串的賦值操作,所以你可以用CString來實(shí)現(xiàn)轉(zhuǎn)換。例如:

// 假設(shè)有一個Unicode串wszSomeString... CString str1 ( wszSomeString ); // 用構(gòu)造器轉(zhuǎn)換CString str2; str2 = wszSomeString; // 用賦值操作轉(zhuǎn)換

ATL宏

  ATL有一組很方便的宏用于串的轉(zhuǎn)換。W2A()用于將Unicode串轉(zhuǎn)換為ANSI串(記憶方法是“wide to ANSI”——寬字符到ANSI)。實(shí)際上使用OLE2A()更精確,“OLE”表示的意思是COM串或者OLE串。下面是使用這些宏的例子:

#include <atlconv.h> // 還是假設(shè)有一個Unicode串wszSomeString...{ char szANSIString [MAX_PATH]; USES_CONVERSION; // 聲明這個宏要使用的局部變量 lstrcpy ( szANSIString, OLE2A(wszSomeString) );}

  OLE2A()宏“返回”轉(zhuǎn)換的串的指針,但轉(zhuǎn)換的串被存儲在某個臨時棧變量中,所以要用lstrcpy()來獲得自己的拷貝。其它的幾個宏是W2T()(Unicode 到 TCHAR)以及W2CT()(Unicode到常量TCHAR串)。
  有個宏是OLE2CA()(Unicode到常量char串),可以被用到上面的例子中,OLE2CA()實(shí)際上是個更正宏,因?yàn)閘strcpy()的第二個參數(shù)是一個常量char*,關(guān)于這個問題本文將在以后作詳細(xì)討論。
  另一方面,如果你不想做以上復(fù)雜的串處理,盡管讓它還保持為Unicode串,如果編寫的是控制臺應(yīng)用程序,輸出/顯示Unicode串時應(yīng)該用全程變量std::wcout,如:

wcout << wszSomeString;

  但是要記住,std::wcout只認(rèn)Unicode,所以你要是“正常”串的話,還得用std::cout輸出/顯示。對于Unicode串文字量,要使用前綴L標(biāo)示,如:

wcout << L"The Oracle says..." << endl << wszOracleResponse;

如果保持串為Unicode,編程時有兩個限制:

  • 必須使用wcsXXX() Unicode串處理函數(shù),如wcslen();

  • 在Windows 9x環(huán)境中不能在Windows API中傳遞Unicode串。要想編寫能在9x和NT上都能運(yùn)行的應(yīng)用,必須使用TCHAR類型,詳情請參 考MSDN;

用例子代碼總結(jié)上述內(nèi)容

下面用兩個例子演示本文所講的COM概念。代碼中還包含了本文的例子工程。

使用單接口COM對象

  第一個例子展示的是單接口COM對象。這可能是你碰到得最簡單的例子。它使用外殼中的活動桌面組件對象類(CLSID_ActiveDesktop)來獲得當(dāng)前桌面墻紙的文件名。請確認(rèn)系統(tǒng)中安裝了活動桌面(Active Desktop)。 以下是編程步驟:

  • 初始化COM庫。 (Initialize);

  • 創(chuàng)建一個與活動桌面交互的COM對象,并取得IActiveDesktop接口;

  • 調(diào)用COM對象的GetWallpaper()方法;

  • 如果GetWallpaper()成功,則輸出/顯示墻紙文件名;

  • 釋放接口(Release());

  • 收回COM庫(Uninitialize);

 1 WCHAR   wszWallpaper [MAX_PATH];
 2 CString strPath;
 3 HRESULT hr;
 4 IActiveDesktop* pIAD;
 5 
 6     // 1. 初始化COM庫(讓W(xué)indows加載DLLs)。通常是在程序的InitInstance()中調(diào)用
 7     // CoInitialize ( NULL )或其它啟動代碼。MFC程序使用AfxOleInit()。
 8 
 9     CoInitialize ( NULL );
10 
11     // 2. 使用外殼提供的活動桌面組件對象類創(chuàng)建COM對象。
12     // 第四個參數(shù)通知COM需要什么接口(這里是IActiveDesktop).
13 
14     hr = CoCreateInstance ( CLSID_ActiveDesktop,
15                             NULL,
16                             CLSCTX_INPROC_SERVER,
17                             IID_IActiveDesktop,
18                             (void**&pIAD );
19 
20     if ( SUCCEEDED(hr) )
21     {
22         // 3. 如果COM對象被創(chuàng)建成功,則調(diào)用這個對象的GetWallpaper() 方法。
23         hr = pIAD->GetWallpaper ( wszWallpaper, MAX_PATH, 0 );
24 
25         if ( SUCCEEDED(hr) )
26         {
27             // 4. 如果 GetWallpaper() 成功,則輸出它返回的文件名字。
28             // 注意這里使用wcout 來顯示Unicode 串wszWallpaper.  wcout 是
29             // Unicode 專用,功能與cout.相同。
30             wcout << L"Wallpaper path is:\n    " << wszWallpaper << endl << endl;
31         }
32         else
33         {
34             cout << _T("GetWallpaper() failed."<< endl << endl;
35         }
36 
37         // 5. 釋放接口。
38         pIAD->Release();
39     }
40     else
41     {
42         cout << _T("CoCreateInstance() failed."<< endl << endl;
43     }
44     // 6. 收回COM庫。MFC 程序不用這一步,它自動完成。
45   CoUninitialize();

 
 在這個例子中,輸出/顯示Unicode 串 wszWallpaper用的是std::wcout。

使用多接口的COM對象

  第二個例子展示了如何使用一個提供單接口的COM對象QueryInterface()函數(shù)。其中的代碼用外殼的Shell Link組件對象類創(chuàng)建我們在第一個例子中獲得的墻紙文件的快捷方式 。以下是編程步驟:

  • 初始化 COM 庫;

  • 創(chuàng)建一個用于建立快捷方式的COM 對象并取得IShellLink 接口;

  • 調(diào)用IShellLink 接口的SetPath()方法;

  • 調(diào)用對象的QueryInterface()函數(shù)并取得IPersistFile接口;

  • 調(diào)用IPersistFile 接口的Save()方法;

  • 釋放接口;

  • 收回COM庫;

 1 CString       sWallpaper = wszWallpaper;  // 將墻紙路徑轉(zhuǎn)換為ANSI
 2 IShellLink*   pISL;
 3 IPersistFile* pIPF;
 4 
 5     // 1. 初始化COM庫(讓W(xué)indows 加載DLLs). 通常在InitInstance()中調(diào)用
 6     // CoInitialize ( NULL )或其它啟動代碼。MFC 程序使用AfxOleInit() 。
 7 
 8     CoInitialize ( NULL );
 9 
10     // 2. 使用外殼提供的Shell Link組件對象類創(chuàng)建COM對象。.
11     // 第四個參數(shù)通知COM 需要什么接口(這里是IShellLink)。
12 
13     hr = CoCreateInstance ( CLSID_ShellLink,
14                             NULL,
15                             CLSCTX_INPROC_SERVER,
16                             IID_IShellLink,
17                             (void**&pISL );
18 
19     if ( SUCCEEDED(hr) )
20     {
21         // 3. 設(shè)置快捷方式目標(biāo)(墻紙文件)的路徑。
22         hr = pISL->SetPath ( sWallpaper );
23 
24         if ( SUCCEEDED(hr) )
25         {
26             // 4. 獲取這個對象的第二個接口(IPersistFile)。
27             hr = pISL->QueryInterface ( IID_IPersistFile, (void**&pIPF );
28 
29             if ( SUCCEEDED(hr) )
30             {
31                 // 5. 調(diào)用Save() 方法保存某個文件得快捷方式。第一個參數(shù)是
32                 // Unicode 串。
33                 hr = pIPF->Save ( L"C:\\wallpaper.lnk", FALSE );
34 
35                 // 6a. 釋放IPersistFile 接口。
36                 pIPF->Release();
37             }
38         }
39 
40         // 6. 釋放IShellLink 接口。
41         pISL->Release();
42     }
43 
44     // 輸出錯誤信息部分這里省略。
45 
46     // 7. 收回COM 庫。MFC 程序不用這一步,它自動完成。
47     CoUninitialize();


處理HRESULT

  這一部分準(zhǔn)備用SUCCEEDED 和 FAILED宏進(jìn)行一些簡單的出錯處理。主要是深入研究從COM方法返回的HRESULT,以便達(dá)到完全理解和熟練應(yīng)用。
  HRESULT是個32位符號整數(shù),其非負(fù)值表示成功,負(fù)值表示失敗。HRESULT有三個域:程度位(表示成功或失敗),功能碼和狀態(tài)碼。功能碼表示HRESULT來自什么組件或程序。微軟給不同的組件多賦予功能碼,如:COM、任務(wù)調(diào)度程序等都有功能碼。功能碼是個16位的值,僅此而已,沒有其它內(nèi)在含義;它在數(shù)字和意義之間是隨意關(guān)聯(lián)的;類似GetLastError()返回的值。
  如果你在winerror.h頭文件中查找錯誤代碼,會看到許多按照[功能]_[程度]_[描述]命名規(guī)范列出的HRESULT值,由組件返回的通用的HRESULT(類似E_OUTOFMEMORY)在名字中沒有功能碼。如 :

REGDB_E_READREGDB:
功能碼 = REGDB, 指“注冊表數(shù)據(jù)庫(registry database)”;
程度 = E 意思是錯誤(error);
描述 = READREGDB 是對錯誤的描述(意思是不能讀注冊表數(shù)據(jù)庫)。 S_OK: 沒有功能碼——通用(generic)
HRESULT;
程度=S;表示成功(success);
OK 是狀態(tài)描述表示一切都好(everything''s OK)。

  好在有一種比察看winerror.h文件更容易的方法來確定HRESULT的意思。使用VC提供的錯誤查找工具(Error Lookup)可以輕松查到為HRESULT內(nèi)建功能碼。例如,假設(shè)你在CoCreateInstance()之前忘了調(diào)用CoInitialize()。CoCreateInstance()返回的值是0x800401F0。你只要將這個值輸入到錯誤查找工具按“Look Up”按鈕,便可以看到錯誤信息描述“尚未調(diào)用CoInitialize”如下圖所示:

  另外一種查找HRESULT描述的方法是在調(diào)試器中。假設(shè)有一個HRESULT變量是hres。在Watch窗口的左邊框中輸入“hres,hr”,表示想要看的值,“hr”便會通知VC顯示HRESULT所描述的值。如下圖所示:

通過以上的討論,想必你對COM編程有了初步的認(rèn)識,本文第二部分將探討COM的內(nèi)部機(jī)制。教你如何用C++編寫自己的接口。

posted on 2012-06-11 14:58 王海光 閱讀(747) 評論(0)  編輯 收藏 引用 所屬分類: ATL
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲视频欧美在线| 国产日韩视频| 欧美一二三区精品| 久久av一区| 久久精品免费观看| 美女国产精品| 欧美日韩亚洲另类| 国产日韩欧美在线看| 奶水喷射视频一区| 久久精品国产免费看久久精品| 亚洲国产经典视频| 亚洲美女中出| 欧美亚洲一区| 免费成人高清在线视频| 亚洲国产精品一区| 一本一本大道香蕉久在线精品| 亚洲视频网站在线观看| 久久久九九九九| 欧美日韩一区二区三区高清| 国产精品永久| 亚洲高清免费| 午夜国产不卡在线观看视频| 蜜臀av性久久久久蜜臀aⅴ| 亚洲精品久久嫩草网站秘色| 欧美一级午夜免费电影| 欧美精品成人一区二区在线观看 | 国产欧美精品日韩区二区麻豆天美| 韩日成人av| 亚洲欧美大片| 亚洲国产高清视频| 欧美在线精品免播放器视频| 欧美经典一区二区| 一区在线视频观看| 亚洲欧美在线高清| 亚洲精品系列| 欧美sm视频| 国产一区二区在线观看免费播放| 99v久久综合狠狠综合久久| 久久精品视频va| 亚洲午夜精品在线| 欧美日韩一区二区在线观看视频| 亚洲第一页在线| 久久久蜜桃一区二区人| 亚洲一区二区三区在线| 欧美美女福利视频| 亚洲人成在线观看网站高清| 老司机亚洲精品| 久久激情五月激情| 国产视频久久久久| 欧美一区二区在线看| 夜夜夜久久久| 欧美三级日本三级少妇99| 亚洲国产人成综合网站| 久久综合久久美利坚合众国| 香蕉尹人综合在线观看| 国产精品高清网站| 亚洲一区二区三区欧美| 日韩视频一区二区三区| 欧美精品一区二区在线观看| 亚洲精品永久免费| 亚洲清纯自拍| 欧美日韩免费区域视频在线观看| 日韩视频在线观看一区二区| 亚洲激情不卡| 免费成人高清视频| 欧美在线观看www| 午夜精品亚洲| 国内成人在线| 蜜臀av在线播放一区二区三区| 久久激情婷婷| 91久久精品一区二区三区| 亚洲成色www久久网站| 欧美va天堂| 一区二区三区国产| 日韩亚洲欧美成人| 国产日韩欧美一区二区| 久久阴道视频| 欧美成人一品| 亚洲欧美不卡| 久久久99国产精品免费| 亚洲欧洲一区| 亚洲天堂成人| 在线成人亚洲| 99精品热6080yy久久| 国产精品永久免费视频| 久久午夜羞羞影院免费观看| 免费在线日韩av| 亚洲视频网在线直播| 午夜激情综合网| 亚洲国产一区二区三区高清| 亚洲免费激情| 一区二区在线视频| 亚洲精品视频在线播放| 国产亚洲精品自拍| 亚洲国产精品黑人久久久| 国产精品久久久一区二区三区| 麻豆精品视频在线| 国产精品v欧美精品v日韩 | 久久综合久色欧美综合狠狠| 免费影视亚洲| 久久精品成人一区二区三区| 欧美国产欧美亚洲国产日韩mv天天看完整| 正在播放日韩| 久久躁狠狠躁夜夜爽| 亚洲小视频在线| 久热精品视频在线观看| 亚洲欧美日韩在线不卡| 女仆av观看一区| 久久久久久久尹人综合网亚洲 | 国产精品亚洲综合色区韩国| 欧美国产一区二区在线观看| 国产伦精品一区二区三区四区免费| 亚洲国产精品999| 国产一区视频在线观看免费| 一区二区三区欧美亚洲| 亚洲欧洲一区二区在线观看| 午夜一区二区三区在线观看| 艳女tv在线观看国产一区| 久久久久高清| 久久人人爽人人| 国产日韩欧美中文在线播放| 亚洲午夜精品久久久久久浪潮 | 亚洲一区精品视频| 亚洲精品日韩精品| 久久精品国产清自在天天线| 午夜精品久久久久久久| 欧美日韩日韩| 亚洲精品久久久久久久久久久久| 亚洲第一毛片| 免费av成人在线| 欧美成年人网站| 在线观看日韩专区| 久久中文字幕一区| 欧美韩国日本一区| 亚洲国产日韩欧美在线99 | 日韩亚洲精品在线| av成人免费在线观看| 欧美激情一区二区三区不卡| 欧美第一黄色网| 亚洲人体大胆视频| 欧美成人一区二区三区在线观看| 欧美电影美腿模特1979在线看 | 亚洲区欧美区| 欧美精品一区二区三区很污很色的 | 亚洲午夜精品久久| 欧美一区二区三区久久精品| 国产精品乱码妇女bbbb| 亚洲永久网站| 久久久久一区二区三区四区| 在线日韩av永久免费观看| 美女国内精品自产拍在线播放| 亚洲国产精品一区二区第一页| 99国产精品视频免费观看一公开| 欧美精品www在线观看| 妖精成人www高清在线观看| 午夜精品久久久久久久久久久| 国产欧美日韩在线视频| 久久国产乱子精品免费女| 欧美大尺度在线观看| 9人人澡人人爽人人精品| 国产精品久久久久久久app| 欧美一区二区女人| 亚洲国产精品久久久久秋霞影院 | 欧美日韩精品免费观看视频完整 | 欧美激情免费观看| 亚洲一区美女视频在线观看免费| 国产伦精品一区二区三区照片91| 久久久99免费视频| 亚洲美女毛片| 美女91精品| 亚洲在线观看免费视频| 国语自产精品视频在线看8查询8 | 欧美亚洲一区二区三区| 欧美丰满少妇xxxbbb| 欧美午夜电影一区| 男女精品视频| 亚洲夜间福利| 黄色一区二区三区| 欧美日韩国产精品一区二区亚洲| 亚洲一区二区三区四区五区黄| 美女精品在线观看| 亚洲中字黄色| 亚洲欧洲精品一区二区三区| 国产欧美日韩一区二区三区在线观看 | 国产精品久久久久久久9999| 久久天天狠狠| 亚洲综合精品| 一本色道久久综合亚洲精品小说| 久久亚洲精品一区二区| 亚洲欧美福利一区二区| 亚洲欧洲一区| 亚洲二区在线| 国产一区二区在线观看免费| 欧美视频在线看| 欧美精品三级在线观看| 久久久久久午夜| 亚洲日韩欧美一区二区在线| 国产一区二区按摩在线观看| 欧美色视频日本高清在线观看| 欧美寡妇偷汉性猛交|