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

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>
            99一区二区| 在线亚洲伦理| 欧美成人午夜视频| 亚洲理伦电影| 欧美激情久久久久| 欧美日韩高清在线观看| 欧美黑人多人双交| 欧美日韩精选| 国产综合色产| 91久久午夜| 亚洲婷婷在线| 久久久亚洲高清| 亚洲电影av| 欧美成人资源网| 99在线精品视频| 欧美中文字幕在线播放| 久久影院午夜论| 欧美连裤袜在线视频| 国产精品视频yy9299一区| 狠狠色噜噜狠狠色综合久| 亚洲精品中文字幕在线观看| 亚洲影院免费| 欧美成人免费全部| 这里只有精品视频| 久热精品视频在线观看一区| 欧美日韩国产成人在线观看| 国精品一区二区| 亚洲一区二区精品视频| 久久久噜噜噜久久中文字免| 亚洲另类一区二区| 久久久久久穴| 国产精品日韩欧美大师| 亚洲国产欧美国产综合一区| 欧美一级片久久久久久久| 亚洲国产精品久久久久婷婷老年 | 亚洲乱码久久| 久久都是精品| 99精品视频一区| 免费成人高清在线视频| 国产精品亚洲精品| 一个人看的www久久| 免费成人av在线看| 亚洲欧美日韩综合aⅴ视频| 欧美另类亚洲| 亚洲精品一区在线观看| 久久美女艺术照精彩视频福利播放| 亚洲伦理一区| 免费日韩视频| 亚洲大片免费看| 久久久精品久久久久| 亚洲尤物精选| 国产精品高清一区二区三区| 99国产精品视频免费观看| 欧美99在线视频观看| 欧美影院午夜播放| 国产午夜精品视频免费不卡69堂| 亚洲综合导航| 亚洲一区二区三区精品在线观看| 欧美日韩视频在线一区二区 | 久久国产精品99久久久久久老狼| 日韩亚洲欧美一区二区三区| 美女网站在线免费欧美精品| 国产一区欧美日韩| 久久久久免费观看| 久久人人97超碰精品888| 今天的高清视频免费播放成人 | 午夜精品一区二区三区电影天堂| 9i看片成人免费高清| 欧美久久99| 亚洲性视频h| 亚洲制服av| 国产一区二区三区精品久久久| 久久精品成人| 久久久久天天天天| 亚洲人www| 亚洲最黄网站| 国产视频自拍一区| 久久这里有精品视频| 久久亚洲综合色一区二区三区| 亚洲国内精品在线| 99国内精品久久| 国产日韩在线看片| 欧美成人第一页| 欧美日韩亚洲一区二区三区在线 | 久久精品成人| 亚洲国产美国国产综合一区二区| 亚洲美女少妇无套啪啪呻吟| 欧美午夜精品久久久久久孕妇| 亚洲免费一级电影| 欧美伊人久久久久久午夜久久久久| 一区精品久久| 日韩午夜电影在线观看| 国产日本亚洲高清| 亚洲国产日韩精品| 国产精品男gay被猛男狂揉视频| 久久久精品国产一区二区三区| 欧美高清视频免费观看| 欧美影院视频| 欧美日韩国产综合视频在线| 欧美一区二区播放| 欧美精品一区二区视频| 久久亚洲国产成人| 国产精品久久久爽爽爽麻豆色哟哟| 午夜激情一区| 欧美激情亚洲| 另类天堂av| 国产伦精品一区| 亚洲三级性片| 在线观看日韩国产| 亚洲欧美国产日韩天堂区| 亚洲精品久久久久| 久久精品亚洲国产奇米99| 亚洲午夜久久久久久久久电影院| 久久九九国产精品| 欧美影院午夜播放| 欧美体内she精视频| 亚洲精品在线观| 亚洲高清视频的网址| 国产无一区二区| 99国产精品视频免费观看| 91久久精品一区| 性18欧美另类| 欧美在线观看视频在线| 欧美日韩在线亚洲一区蜜芽| 欧美高清影院| 精品成人一区| 久久久999精品视频| 欧美一区二区三区视频| 国产精品v日韩精品v欧美精品网站| 亚洲国产日韩欧美在线99 | 欧美激情中文字幕在线| 久久字幕精品一区| 国产一区二区看久久| 午夜国产精品影院在线观看| 亚洲综合电影| 国产精品久久久久国产a级| 99精品视频一区二区三区| 亚洲天堂激情| 国产精品日韩欧美一区二区| 亚洲午夜激情网页| 亚洲欧美美女| 国产精品嫩草影院av蜜臀| 在线视频日本亚洲性| 亚洲欧美日韩一区二区| 国产精品日韩久久久| 欧美有码在线观看视频| 久久精品成人欧美大片古装| 激情久久中文字幕| 蜜乳av另类精品一区二区| 亚洲国产精品久久久久秋霞蜜臀| 亚洲丁香婷深爱综合| 欧美1区3d| 99re在线精品| 欧美在线视频a| 激情综合色丁香一区二区| 久久男人资源视频| 亚洲国产小视频在线观看| 亚洲伦理在线观看| 欧美午夜美女看片| 久久精品99| 亚洲人成绝费网站色www| 国产精品99久久久久久久vr | 欧美精品麻豆| 亚洲视频在线一区观看| 欧美中日韩免费视频| 亚洲大片免费看| 欧美吻胸吃奶大尺度电影| 欧美亚洲午夜视频在线观看| 欧美高清视频免费观看| 亚洲一区高清| 在线视频成人| 国产精品美女www爽爽爽视频| 久久精品视频在线免费观看| 亚洲欧洲一区二区在线播放| 亚洲午夜一区二区| 亚洲成人资源| 国产精品日韩欧美| 欧美—级在线免费片| 午夜国产精品视频| 亚洲精品国精品久久99热一| 欧美在线观看网站| 亚洲精品一区在线观看| 久久免费国产精品1| 国产精品久久国产精品99gif| 性色av一区二区三区红粉影视| 欧美成人r级一区二区三区| 亚洲免费在线观看| 亚洲人成在线播放| 国产主播在线一区| 欧美性大战久久久久| 麻豆久久精品| 欧美中文字幕第一页| 99视频日韩| 最新国产成人在线观看| 久久国产欧美| 西西裸体人体做爰大胆久久久| 亚洲理论在线观看| 亚洲福利久久| 狠狠色噜噜狠狠色综合久 | 免费永久网站黄欧美|