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

C++ Coder

HCP高性能計(jì)算架構(gòu),實(shí)現(xiàn),編譯器指令優(yōu)化,算法優(yōu)化, LLVM CLANG OpenCL CUDA OpenACC C++AMP OpenMP MPI

C++博客 首頁 新隨筆 聯(lián)系 聚合 管理
  98 Posts :: 0 Stories :: 0 Comments :: 0 Trackbacks
http://blog.csdn.net/wangqiulin123456/article/details/8026270

目錄(?)[-]
  1. COM到底是什么
  2. 基本元素的定義
    1. 創(chuàng)建一個(gè)新對象
    2. 刪除對象
      1. 創(chuàng)建COM對象
      2. 刪除COM對象
    3. 接下來將詳細(xì)討論IUnknown接口
      1. 基本接口――IUnknown
    4. 仔細(xì)做好串處理
      1. WideCharToMultiByte()
      2. wcstombs()
      3. CString
      4. ATL宏
    5. 用例子代碼總結(jié)上述內(nèi)容
      1. 使用單接口COM對象
      2. 使用多接口的COM對象
    6. 處理HRESULT

             本文的目的是為剛剛接觸COM的程序員提供編程指南,并幫助他們理解COM的基本概念。內(nèi)容包括COM規(guī)范簡介,重要的COM術(shù)語以及如何重用現(xiàn)有的COM組件。本文不包括如何編寫自己的COM對象和接口。

          COM即組件對象模型,是Component ObjectModel 取前三個(gè)字母的縮寫,這三個(gè)字母在當(dāng)今Windows的世界中隨處可見。隨時(shí)涌現(xiàn)出來的大把大把的新技術(shù)都以COM為基礎(chǔ)。各種文檔中也充斥著諸如COM對象、接口、服務(wù)器之類的術(shù)語。因此,對于一個(gè)程序員來說,不僅要掌握使用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)行完全透徹的解釋。本文包括以下幾個(gè)部分:

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

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

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

       基本接口――描述IUnknown基本接口及其方法

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

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

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

COM到底是什么

        簡單地說,COM是一種跨應(yīng)用和語言共享二進(jìn)制代碼的方法。與C++不同,它提倡源代碼重用。ATL便是一個(gè)很好的例證。源碼級重用雖然好,但只能用于C++。它還帶來了名字沖突的可能性,更不用說不斷拷貝重用代碼而導(dǎo)致工程膨脹和臃腫。

        Windows使用DLLs在二進(jìn)制級共享代碼。這也是Windows程序運(yùn)行的關(guān)鍵――重用kernel32.dll, user32.dll等。但DLLs是針對C接口而寫的,它們只能被C或理解C調(diào)用規(guī)范的語言使用。由編程語言來負(fù)責(zé)實(shí)現(xiàn)共享代碼,而不是由DLLs本身。這樣的話DLLs的使用受到限制。
MFC引入了另外一種MFC擴(kuò)展DLLs二進(jìn)制共享機(jī)制。但它的使用仍受限制――只能在MFC程序中使用。

        COM通過定義二進(jìn)制標(biāo)準(zhǔn)解決了這些問題,即COM明確指出二進(jìn)制模塊(DLLs和EXEs)必須被編譯成與指定的結(jié)構(gòu)匹配。這個(gè)標(biāo)準(zhǔn)也確切規(guī)定了在內(nèi)存中如何組織COM對象。COM定義的二進(jìn)制標(biāo)準(zhǔn)還必須獨(dú)立于任何編程語言(如C++中的命名修飾)。一旦滿足了這些條件,就可以輕松地從任何編程語言中存取這些模塊。由編譯器負(fù)責(zé)所產(chǎn)生的二進(jìn)制代碼與標(biāo)準(zhǔn)兼容。這樣使后來的人就能更容易地使用這些二進(jìn)制代碼。

       在內(nèi)存中,COM對象的這種標(biāo)準(zhǔn)形式在C++虛函數(shù)中偶爾用到,所以這就是為什么許多COM代碼使用C++的原因。但是記住,編寫模塊所用的語言是無關(guān)的,因?yàn)榻Y(jié)果二進(jìn)制代碼為所有語言可用。

       此外,COM不是Win32特有的。從理論上講,它可以被移植到Unix或其它操作系統(tǒng)。但是我好像還從來沒有在Windows以外的地方聽說過COM。

基本元素的定義

        我們從下往上看。接口只不過是一組函數(shù)。這些函數(shù)被稱為方法。接口名字以大寫的I開頭,例如C++中的IShellLink,接口被設(shè)計(jì)成一個(gè)抽象基類,其中只有純粹的虛擬函數(shù)。

        接口可以從其它接口繼承,這里所說的繼承的原理就好像C++中的單繼承。接口是不允許多繼承的。

        coclass(簡稱組件對象類――componentobject class)被包含在DLL或EXE中,并且包含著一個(gè)或者多個(gè)接口的代碼。組件對象類(coclasss)實(shí)現(xiàn)這些接口。COM對象在內(nèi)存中表現(xiàn)為組件對象類(coclasss)的一個(gè)實(shí)例。注意COM“類”和C++“類”是不相同的,盡管常常COM類實(shí)現(xiàn)的就是一個(gè)C++類。

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

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

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

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

        在COM中廣泛地使用GUID有兩個(gè)理由:

        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用來返回錯(cuò)誤和成功代碼的整型數(shù)字。除此之外,別無它意,雖然以H作前綴,但沒有句柄之意。下文會對它有更多的討論。

最后,COM庫是在你使用COM時(shí)與你交互的操作系統(tǒng)的一部分,它常常指的就是COM本身。但是為了避免混淆才分開描述的。

使用和處理COM對象

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

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

         C++中,用new操作符,或者在棧中創(chuàng)建對象。

         COM中,調(diào)用COM庫中的API。

刪除對象

          C++中,用delete操作符,或?qū)ο筇叱觥?/span>

          COM中,所有的對象保持它們自己的引用計(jì)數(shù)。調(diào)用者必須通知對象什么時(shí)候用完這個(gè)對象。當(dāng)引用計(jì)數(shù)為零時(shí),COM對象將自己從內(nèi)存中釋放。

         由此可見,對象處理的兩個(gè)階段:創(chuàng)建和銷毀,缺一不可。當(dāng)創(chuàng)建COM對象時(shí)要通知COM庫使用哪一個(gè)接口。如果這個(gè)對象創(chuàng)建成功,COM庫返回所請求接口的指針。然后通過這個(gè)指針調(diào)用方法,就像使用常規(guī)C++對象指針一樣。

創(chuàng)建COM對象

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

  1. HRESULT CoCreateInstance (  
  2. REFCLSID  rclsid,  
  3. LPUNKNOWN pUnkOuter,  
  4. DWORD     dwClsContext,  
  5. REFIID    riid,  
  6. LPVOID*   ppv );  

 

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

1.rclsid:coclass的CLSID,例如,可以傳遞CLSID_ShellLink創(chuàng)建一個(gè)COM對象來建立快捷方式。

2.pUnkOuter:這個(gè)參數(shù)只用于COM對象的聚合,利用它向現(xiàn)有的coclass添加新方法。參數(shù)值為null表示不使用聚合。

3.dwClsContext:表示所使用COM服務(wù)器的種類。本文使用的是最簡單的COM服務(wù)器,一個(gè)進(jìn)程內(nèi)(in-process)DLL,

4.        所以傳遞的參數(shù)值為CLSCTX_INPROC_SERVER。注意這里不要隨意使用CLSCTX_ALL(在ATL中,它是個(gè)缺省值),

5.        因?yàn)樵跊]有安裝DCOM的Windows95系統(tǒng)上會導(dǎo)致失敗。

6.riid:請求接口的IID。例如,可以傳遞IID_IShellLink獲得IShellLink接口指針。

7.ppv:接口指針的地址。COM庫通過這個(gè)參數(shù)返回請求的接口。     

          當(dāng)你調(diào)用CoCreateInstance()時(shí),它負(fù)責(zé)在注冊表中查找COM服務(wù)器的位置,將服務(wù)器加載到內(nèi)存,并創(chuàng)建你所請求的coclass實(shí)例。以下是一個(gè)調(diào)用的例子,創(chuàng)建一個(gè)CLSID_ShellLink對象的實(shí)例并請求指向這個(gè)對象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. if ( SUCCEEDED ( hr ) )  
  9. {  
  10. // 用pISL調(diào)用方法  
  11. }  
  12. else  
  13. {  
  14. // 不能創(chuàng)建COM對象,hr 為出錯(cuò)代碼  
  15. }  

 

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

刪除COM對象

         前面說過,你不用釋放COM對象,只要告訴它們你已經(jīng)用完對象。IUnknown是每一個(gè)COM對象必須實(shí)現(xiàn)的接口,它有一個(gè)方法,Release()。調(diào)用這個(gè)方法通知COM對象你不再需要對象。一旦調(diào)用了這個(gè)方法之后,就不能再次使用這個(gè)接口,因?yàn)檫@個(gè)COM對象可能從此就從內(nèi)存中消失了。

          如果你的應(yīng)用程序使用許多不同的COM對象,因此在用完某個(gè)接口后調(diào)用Release()就顯得非常重要。如果你不釋放接口,這個(gè)COM對象(包含代碼的DLLs)將保留在內(nèi)存中,這會增加不必要的開銷。如果你的應(yīng)用程序要長時(shí)間運(yùn)行,就應(yīng)該在應(yīng)用程序處于空閑期間調(diào)用CoFreeUnusedLibraries() API。這個(gè)API將卸載任何沒有明顯引用的COM服務(wù)器,因此這也降低了應(yīng)用程序使用的內(nèi)存開銷。

          繼續(xù)用上面的例子來說明如何使用Release():

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

 

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

基本接口――IUnknown

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

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

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

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

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

  1. HRESULT IUnknown::QueryInterface (  
  2. REFIID iid,  
  3. void** ppv );  

 

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

1.iid:所請求的接口的IID。

2.ppv:接口指針的地址,QueryInterface()通過這個(gè)參數(shù)在成功時(shí)返回這個(gè)接口。

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

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

 

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

仔細(xì)做好串處理

         這一部分將花點(diǎn)時(shí)間來討論如何在COM代碼中處理串。如果你熟悉Unicode 和ANSI,并知道如何對它們進(jìn)行轉(zhuǎn)換的話,你就可以跳過這一部分,否則還是讀一下這一部分的內(nèi)容。

         不管什么時(shí)候,只要COM方法返回一個(gè)串,這個(gè)串都是Unicode串(這里指的是寫入COM規(guī)范的所有方法)。Unicode是一種字符編碼集,類似ASCII,但用兩個(gè)字節(jié)表示一個(gè)字符。如果你想更好地控制或操作串的話,應(yīng)該將它轉(zhuǎn)換成TCHAR類型串。

          TCHAR和以_t開頭的函數(shù)(如_tcscpy())被設(shè)計(jì)用來讓你用相同的源代碼處理Unicode和ANSI串。在大多數(shù)情況下編寫的代碼都是用來處理ANSI串和ANSI WindowsAPIs,所以在下文中,除非另外說明,我所說的字符/串都是指TCHAR類型。你應(yīng)該熟練掌握TCHAR類型,尤其是當(dāng)你閱讀其他人寫的有關(guān)代碼時(shí),要特別注意TCHAR類型。

          當(dāng)你從某個(gè)COM方法返回得到一個(gè)Unicode串時(shí),可以用下列幾種方法之一將它轉(zhuǎn)換成char類型串:

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

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

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

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

1.WideCharToMultiByte()

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

  1. int WideCharToMultiByte (  
  2. UINT    CodePage,  
  3. DWORD   dwFlags,  
  4. LPCWSTR lpWideCharStr,  
  5. int     cchWideChar,  
  6. LPSTR   lpMultiByteStr,  
  7. int     cbMultiByte,  
  8. LPCSTR  lpDefaultChar,  
  9. LPBOOL  lpUsedDefaultChar );  

 

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

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

dwFlags:dwFlags 確定Windows如何處理“復(fù)合” Unicode字符,它是一種后面帶讀音符號的字符。

如è就是一個(gè)復(fù)合字符。如果這些字符在CodePage參數(shù)指定的代碼頁中,不會出什么事。

否則,Windows必須對之進(jìn)行轉(zhuǎn)換。傳遞WC_COMPOSITECHECK使得這個(gè)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,表示這個(gè)串是以0x00結(jié)尾。

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

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

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

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

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

 

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

2.wcstombs()

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

  1. size_t wcstombs (  
  2. char*         mbstr,  
  3. const wchar_t* wcstr,  
  4. size_t         count );  

 

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

1.mbstr:接受結(jié)果ANSI串的字符(char)緩沖。

2.wcstr:要轉(zhuǎn)換的Unicode串。

3.count:mbstr參數(shù)所指的緩沖大小。

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

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

 

3.CString

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

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

 

4.ATL宏

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

  1. // 還是假設(shè)有一個(gè)Unicode串wszSomeString...  
  2. {  
  3. char szANSIString[MAX_PATH];  
  4. USES_CONVERSION; // 聲明這個(gè)宏要使用的局部變量  
  5. lstrcpy ( szANSIString, OLE2A(wszSomeString));  
  6. }  

 

        OLE2A()宏“返回”轉(zhuǎn)換的串的指針,但轉(zhuǎn)換的串被存儲在某個(gè)臨時(shí)棧變量中,所以要用lstrcpy()來獲得自己的拷貝。其它的幾個(gè)宏是W2T()(Unicode 到 TCHAR)以及W2CT()(Unicode到常量TCHAR串)。

         有個(gè)宏是OLE2CA()(Unicode到常量char串),可以被用到上面的例子中,OLE2CA()實(shí)際上是個(gè)更正宏,因?yàn)閘strcpy()的第二個(gè)參數(shù)是一個(gè)常量char*,關(guān)于這個(gè)問題本文將在以后作詳細(xì)討論。

         另一方面,如果你不想做以上復(fù)雜的串處理,盡管讓它還保持為Unicode串,如果編寫的是控制臺應(yīng)用程序,輸出/顯示Unicode串時(shí)應(yīng)該用全程變量std::wcout,如:

  1. wcout << wszSomeString;  

 

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

  1. wcout << L"The Oraclesays..." << endl << wszOracleResponse;  

 

 如果保持串為Unicode,編程時(shí)有兩個(gè)限制:

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

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

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

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

使用單接口COM對象

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

  1. 初始化COM庫。 (Initialize);
  2. 創(chuàng)建一個(gè)與活動(dòng)桌面交互的COM對象,并取得IActiveDesktop接口;
  3. 調(diào)用COM對象的GetWallpaper()方法;
  4. 如果GetWallpaper()成功,則輸出/顯示墻紙文件名;
  5. 釋放接口(Release());
  6. 收回COM庫(Uninitialize);

 

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

 

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

使用多接口的COM對象

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

  1. 初始化 COM 庫;
  2. 創(chuàng)建一個(gè)用于建立快捷方式的COM 對象并取得IShellLink 接口;
  3. 調(diào)用IShellLink 接口的SetPath()方法;
  4. 調(diào)用對象的QueryInterface()函數(shù)并取得IPersistFile接口;
  5. 調(diào)用IPersistFile 接口的Save()方法;
  6. 釋放接口;
  7. 收回COM庫;

 

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

處理HRESULT

        這一部分準(zhǔn)備用SUCCEEDED 和 FAILED宏進(jìn)行一些簡單的出錯(cuò)處理。主要是深入研究從COM方法返回的HRESULT,以便達(dá)到完全理解和熟練應(yīng)用。

        HRESULT是個(gè)32位符號整數(shù),其非負(fù)值表示成功,負(fù)值表示失敗。HRESULT有三個(gè)域:程度位(表示成功或失敗),功能碼和狀態(tài)碼。功能碼表示HRESULT來自什么組件或程序。微軟給不同的組件多賦予功能碼,如:COM、任務(wù)調(diào)度程序等都有功能碼。功能碼是個(gè)16位的值,僅此而已,沒         有其它內(nèi)在含義;它在數(shù)字和意義之間是隨意關(guān)聯(lián)的;類似GetLastError()返回的值。

        如果你在winerror.h頭文件中查找錯(cuò)誤代碼,會看到許多按照[功能]_[程度]_[描述]命名規(guī)范列出的HRESULT值,由組件返回的通用的 HRESULT(類似E_OUTOFMEMORY)在名字中沒有功能碼。如 :

REGDB_E_READREGDB:

功能碼 = REGDB, 指“注冊表數(shù)據(jù)庫(registry database)”;

程度 = E 意思是錯(cuò)誤(error);

描述 = READREGDB 是對錯(cuò)誤的描述(意思是不能讀注冊表數(shù)據(jù)庫)。 S_OK: 沒有功能碼――通用(generic)

HRESULT;

程度=S;表示成功(success);

OK 是狀態(tài)描述表示一切都好(everything''sOK)。

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


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


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

(待續(xù))

posted on 2012-10-17 22:13 jackdong 閱讀(401) 評論(0)  編輯 收藏 引用 所屬分類: Windows編程
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲欧美精品一区| 欧美一区二区三区视频免费播放| 亚洲精品综合| 亚洲国产精品va| 亚洲黄色毛片| 亚洲人成亚洲人成在线观看| 亚洲国产一区二区a毛片| 久久美女性网| 欧美肥婆在线| 亚洲区一区二| 亚洲先锋成人| 久久国产精彩视频| 另类尿喷潮videofree | 欧美日韩dvd在线观看| 欧美日韩美女| 国产日韩欧美视频在线| 在线成人国产| 欧美精品一区二区三区在线播放 | 亚洲精品在线三区| 亚洲一卡二卡三卡四卡五卡| 欧美亚洲日本一区| 99人久久精品视频最新地址| 国产精品久久九九| 黄色成人在线| 亚洲欧美视频一区二区三区| 女人色偷偷aa久久天堂| 一区二区三欧美| 久久九九国产| 国产精品久久久久7777婷婷| 亚洲电影免费在线观看| 亚洲一区激情| 亚洲大胆人体视频| 午夜国产欧美理论在线播放 | 久久精品国产精品| 欧美激情 亚洲a∨综合| 在线一区观看| 免费在线看一区| 国产欧美亚洲日本| 亚洲国产精品久久久久久女王| 日韩一级大片| 亚洲日本国产| 久久久久久婷| 久久综合久久美利坚合众国| 国产精品入口福利| 尤物九九久久国产精品的特点 | 亚洲影视中文字幕| 国产精品麻豆欧美日韩ww| 91久久中文字幕| 午夜欧美大尺度福利影院在线看 | 亚洲午夜激情在线| 欧美激情网站在线观看| 国产区二精品视| 亚洲一区二区在线视频| 女人色偷偷aa久久天堂| 久久久国产亚洲精品| 国产精品二区在线观看| 国产精品看片资源| 亚洲一区二区三区在线观看视频| 久久五月天婷婷| 欧美一区二区三区播放老司机| 亚洲激情成人在线| 久久深夜福利免费观看| 国产精品视频男人的天堂| 亚洲自拍电影| 91久久视频| 欧美亚洲视频在线观看| 亚洲日产国产精品| 欧美在线黄色| 香蕉成人伊视频在线观看| 欧美日本免费| 亚洲影院免费观看| 亚洲免费av片| 国产精品欧美一区喷水| 亚洲视频播放| 亚洲一区高清| 国产精品久久久久永久免费观看| 亚洲欧美日韩人成在线播放| 亚洲精品日韩综合观看成人91| 久久夜色精品国产欧美乱极品 | 91久久久久| 亚洲国产99| 免费欧美在线| 日韩视频在线一区二区| 欧美国产三区| 欧美日韩免费精品| 亚洲午夜一区二区| 久久久久国产精品人| 性欧美8khd高清极品| 亚洲精品资源| 国产精品videosex极品| 国产精品永久免费| 欧美1区免费| 欧美国产精品劲爆| 亚洲欧美国产不卡| 亚洲欧美综合另类中字| 亚洲黄色一区二区三区| 亚洲国产成人av好男人在线观看| 欧美美女日韩| 亚洲在线播放| 免费成人激情视频| 亚洲天堂成人在线视频| 久久九九国产| 99热精品在线| 欧美—级a级欧美特级ar全黄| 国产精品日韩一区| 女同一区二区| 欧美v日韩v国产v| 亚洲欧美清纯在线制服| 亚洲欧美日韩国产中文| 亚洲肉体裸体xxxx137| 亚洲精品国产品国语在线app | 性欧美1819性猛交| 国产精品日韩二区| 久久久久九九九| 国产精品国内视频| 久久最新视频| 国产精品一区二区男女羞羞无遮挡| 一区二区三区毛片| 蜜桃av噜噜一区二区三区| 亚洲精品小视频| 亚洲欧美国产另类| 国内伊人久久久久久网站视频 | 欧美亚洲一区在线| 久久手机免费观看| 久久精品二区亚洲w码| 麻豆精品网站| 久久午夜av| 欧美新色视频| 日韩一区二区精品| 亚洲福利专区| 久久久福利视频| 小嫩嫩精品导航| 国产精品地址| 亚洲高清资源综合久久精品| 精品999在线观看| 亚洲综合色视频| 午夜久久久久久| 欧美日韩国产丝袜另类| 亚洲国产日韩欧美在线99| 黄色成人在线网址| 亚洲女女女同性video| 欧美一二三视频| 久久婷婷激情| 亚洲国产天堂久久综合网| 国产精品地址| 亚洲免费视频一区二区| 亚洲欧美日本伦理| 亚洲男人的天堂在线| 国产精品丝袜久久久久久app| 亚洲精品日韩精品| 亚洲欧洲精品成人久久奇米网| 99精品福利视频| 国产精品伦子伦免费视频| 亚洲一区欧美激情| 亚洲精品一区在线观看| 欧美福利小视频| 欧美成人高清| 一区二区三区视频在线 | 欧美国产日韩精品免费观看| 亚洲高清免费| 六月婷婷一区| 日韩视频在线永久播放| 在线亚洲成人| 国产精品综合色区在线观看| 亚洲男人av电影| 麻豆精品视频在线观看| 一区二区三区亚洲| 欧美精品网站| 一区二区三区不卡视频在线观看 | 久久激情综合| 国产曰批免费观看久久久| 亚洲欧洲久久| 欧美在线日韩在线| 欧美日韩午夜在线视频| 亚洲综合精品一区二区| 亚洲视频自拍偷拍| 国产综合18久久久久久| 玖玖精品视频| 午夜精品一区二区三区在线播放| 一区二区欧美在线观看| 精品88久久久久88久久久| 中文精品视频一区二区在线观看| 久热成人在线视频| 国产一区二区三区观看| 欧美日韩精品在线视频| 亚洲一区视频| 亚洲区中文字幕| 在线亚洲+欧美+日本专区| 在线观看成人av| 久久精品人人做人人综合| 午夜精品久久久久99热蜜桃导演| 在线一区亚洲| 亚洲欧洲一区二区三区| 亚洲免费视频一区二区| 亚洲美女福利视频网站| 一区二区三区四区国产| 在线精品福利| 麻豆国产va免费精品高清在线| 亚洲综合精品自拍| 久久国产精品毛片|