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

C++ Coder

HCP高性能計算架構,實現,編譯器指令優化,算法優化, LLVM CLANG OpenCL CUDA OpenACC C++AMP OpenMP MPI

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

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

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

          COM即組件對象模型,是Component ObjectModel 取前三個字母的縮寫,這三個字母在當今Windows的世界中隨處可見。隨時涌現出來的大把大把的新技術都以COM為基礎。各種文檔中也充斥著諸如COM對象、接口、服務器之類的術語。因此,對于一個程序員來說,不僅要掌握使用COM的方法,而且還要徹底熟悉COM的所有一切。

         本文由淺入深描述COM的內在運行機制,教你如何使用第三方提供的COM對象(以Windows 外殼組件Shell為例)。讀完本文后,你就能掌握如何使用Windows操作系統中內建的組件和第三方提供的COM對象。

本文假設你精通C++語言。在例子代碼中使用了一點MFC和ATL,如果你不熟悉MFC和ATL也沒關系,本文會對這些代碼進行完全透徹的解釋。本文包括以下幾個部分:

       COM――到底是什么?――COM標準的要點介紹,它被設計用來解決什么問題

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

       使用和處理COM對象――如何創建、使用和銷毀COM對象

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

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

       應用COM技術――例子代碼,舉例說明本文所討論的所有概念

       處理HRESULT――HRESULT類型描述,如何監測錯誤及成功代碼

COM到底是什么

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

        Windows使用DLLs在二進制級共享代碼。這也是Windows程序運行的關鍵――重用kernel32.dll, user32.dll等。但DLLs是針對C接口而寫的,它們只能被C或理解C調用規范的語言使用。由編程語言來負責實現共享代碼,而不是由DLLs本身。這樣的話DLLs的使用受到限制。
MFC引入了另外一種MFC擴展DLLs二進制共享機制。但它的使用仍受限制――只能在MFC程序中使用。

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

       在內存中,COM對象的這種標準形式在C++虛函數中偶爾用到,所以這就是為什么許多COM代碼使用C++的原因。但是記住,編寫模塊所用的語言是無關的,因為結果二進制代碼為所有語言可用。

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

基本元素的定義

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

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

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

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

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

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

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

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

        1.GUIDs只是簡單的數字,任何編程語言都可以對之進行處理;

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

        HRESULT是COM用來返回錯誤和成功代碼的整型數字。除此之外,別無它意,雖然以H作前綴,但沒有句柄之意。下文會對它有更多的討論。

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

使用和處理COM對象

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

創建一個新對象

         C++中,用new操作符,或者在棧中創建對象。

         COM中,調用COM庫中的API。

刪除對象

          C++中,用delete操作符,或將棧對象踢出。

          COM中,所有的對象保持它們自己的引用計數。調用者必須通知對象什么時候用完這個對象。當引用計數為零時,COM對象將自己從內存中釋放。

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

創建COM對象

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

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

 

以下是參數解釋:

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

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

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

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

5.        因為在沒有安裝DCOM的Windows95系統上會導致失敗。

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

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

          當你調用CoCreateInstance()時,它負責在注冊表中查找COM服務器的位置,將服務器加載到內存,并創建你所請求的coclass實例。以下是一個調用的例子,創建一個CLSID_ShellLink對象的實例并請求指向這個對象IShellLink接口指針。

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

 

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

刪除COM對象

         前面說過,你不用釋放COM對象,只要告訴它們你已經用完對象。IUnknown是每一個COM對象必須實現的接口,它有一個方法,Release()。調用這個方法通知COM對象你不再需要對象。一旦調用了這個方法之后,就不能再次使用這個接口,因為這個COM對象可能從此就從內存中消失了。

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

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

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

 

  接下來將詳細討論IUnknown接口

基本接口――IUnknown

         每一個COM接口都派生于IUnknown。這個名字有點誤導人,其中沒有未知(Unknown)接口的意思。它的原意是如果有一個指向某COM對象的IUnknown指針,就不用知道潛在的對象是什么,因為每個COM對象都實現IUnknown。IUnknown有三個方法:

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

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

        QueryInterface() ―― 從COM對象請求一個接口指針。當coclass實現一個以上的接口時,就要用到這個方法;

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

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

 

以下是參數解釋:

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

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

        讓我們繼續外殼鏈接的例子。它實現了IShellLink和IPersistFile接口。如果你已經有一個IShellLink指針,pISL,可以從COM對象請求IPersistFile接口:

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

 

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

仔細做好串處理

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

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

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

          當你從某個COM方法返回得到一個Unicode串時,可以用下列幾種方法之一將它轉換成char類型串:

1.調用 WideCharToMultiByte()API;

2.調用CRT 函數wcstombs();

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

4.使用ATL 串轉換宏;

1.WideCharToMultiByte()

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

  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 );  

 

以下是參數解釋:

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

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

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

否則,Windows必須對之進行轉換。傳遞WC_COMPOSITECHECK使得這個API檢查非映射復合字符。

傳遞WC_SEPCHARS使得Windows將字符分為兩段,即字符加讀音,如e`。

傳遞WC_DISCARDNS使得Windows丟棄讀音符號。

傳遞WC_DEFAULTCHAR使得Windows用lpDefaultChar參數中說明的缺省字符替代復合字符。

缺省行為是WC_SEPCHARS。

lpWideCharStr 要轉換的Unicode串。

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

lpMultiByteStr 接受轉換的串的字符緩沖 cbMultiBytelpMultiByteStr的字節大小。

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

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

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

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

 

調用這個函數后,szANSIString將包含Unicode串的ANSI版本。調用這個函數后,szANSIString將包含Unicode串的ANSI版本。

2.wcstombs()

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

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

 

以下是參數解釋:

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

2.wcstr:要轉換的Unicode串。

3.count:mbstr參數所指的緩沖大小。

         wcstombs()在它對WideCharToMultiByte()的調用中使用WC_COMPOSITECHECK | WC_SEPCHARS標志。用wcstombs()轉換前面例子中的   Unicode串,結果一樣:

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

 

3.CString

     MFC中的CString包含有構造函數和接受Unicode串的賦值操作,所以你可以用CString來實現轉換。例如:

  1. // 假設有一個Unicode串wszSomeString...  
  2.  CString str1 ( wszSomeString ); // 用構造器轉換  
  3.  CString str2;  
  4.  str2 = wszSomeString; // 用賦值操作轉換  

 

4.ATL宏

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

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

 

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

         有個宏是OLE2CA()(Unicode到常量char串),可以被用到上面的例子中,OLE2CA()實際上是個更正宏,因為lstrcpy()的第二個參數是一個常量char*,關于這個問題本文將在以后作詳細討論。

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

  1. wcout << wszSomeString;  

 

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

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

 

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

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

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

用例子代碼總結上述內容

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

使用單接口COM對象

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

  1. 初始化COM庫。 (Initialize);
  2. 創建一個與活動桌面交互的COM對象,并取得IActiveDesktop接口;
  3. 調用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庫(讓Windows加載DLLs)。通常是在程序的InitInstance()中調用  
  6. // CoInitialize ( NULL )或其它啟動代碼。MFC程序使用AfxOleInit()。  
  7. CoInitialize ( NULL );  
  8. // 2. 使用外殼提供的活動桌面組件對象類創建COM對象。  
  9. // 第四個參數通知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對象被創建成功,則調用這個對象的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 程序不用這一步,它自動完成。  
  37.   CoUninitialize();  

 

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

使用多接口的COM對象

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

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

 

  1. CString      sWallpaper = wszWallpaper;  // 將墻紙路徑轉換為ANSI  
  2. IShellLink*   pISL;  
  3. IPersistFile* pIPF;  
  4.  // 1. 初始化COM庫(讓Windows 加載DLLs). 通常在InitInstance()中調用  
  5. // CoInitialize ( NULL )或其它啟動代碼。MFC 程序使用AfxOleInit()。  
  6. CoInitialize ( NULL );  
  7. // 2. 使用外殼提供的Shell Link組件對象類創建COM對象。.  
  8. // 第四個參數通知COM 需要什么接口(這里是IShellLink)。   
  9. hr = CoCreateInstance ( CLSID_ShellLink,NULL,CLSCTX_INPROC_SERVER,IID_IShellLink,(void**)&pISL );  
  10. if ( SUCCEEDED(hr) )  
  11. {  
  12.       // 3. 設置快捷方式目標(墻紙文件)的路徑。  
  13.       hr = pISL->SetPath ( sWallpaper );  
  14.       if ( SUCCEEDED(hr) )  
  15.       {  
  16.               // 4. 獲取這個對象的第二個接口(IPersistFile)。  
  17.                hr = pISL->QueryInterface (IID_IPersistFile, (void**) &pIPF );   
  18.                if ( SUCCEEDED(hr) )  
  19.                {  
  20.                       // 5. 調用Save() 方法保存某個文件得快捷方式。第一個參數是  
  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. // 輸出錯誤信息部分這里省略。  
  31. // 7. 收回COM 庫。MFC 程序不用這一步,它自動完成。  
  32. CoUninitialize();  

處理HRESULT

        這一部分準備用SUCCEEDED 和 FAILED宏進行一些簡單的出錯處理。主要是深入研究從COM方法返回的HRESULT,以便達到完全理解和熟練應用。

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

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

REGDB_E_READREGDB:

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

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

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

HRESULT;

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

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

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


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


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

(待續)

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视频精品全国免费| 久久精品五月婷婷| 久久久久高清| 久久综合激情| 欧美激情五月| 久久亚洲捆绑美女| 亚洲三级免费| 欧美 日韩 国产精品免费观看| 久久av二区| 欧美va亚洲va香蕉在线| 男女激情久久| 亚洲国产一区二区a毛片| 亚洲美女视频网| 一区二区三区av| 久久精品视频免费播放| 美女视频黄免费的久久| 欧美午夜a级限制福利片| 国产视频一区在线| 激情文学综合丁香| 欧美精品久久久久久久久老牛影院 | 亚洲高清自拍| 久久先锋影音| 亚洲国产高清一区| 亚洲永久免费观看| 久久综合色天天久久综合图片| 欧美激情一二区| 国产日韩精品一区观看| 精品999久久久| 99re热这里只有精品视频 | 国产免费一区二区三区香蕉精| 精品99一区二区| 在线亚洲美日韩| 欧美成人午夜激情| 亚洲一区二区动漫| 欧美人与性禽动交情品| 精品1区2区3区4区| 亚洲欧美日韩专区| 91久久精品美女高潮| 国产精品美女久久久浪潮软件| 亚洲高清资源| 久久偷窥视频| 国产精品主播| 一区二区高清视频| 欧美国产大片| 久久久成人精品| 国产视频一区在线观看| 中国成人黄色视屏| 亚洲日本电影在线| 欧美三区在线视频| 亚洲免费观看高清完整版在线观看熊 | 日韩一级免费| 欧美激情视频给我| 久久久精品国产一区二区三区| 一区二区三区成人 | 亚洲一区二区三区涩| 欧美国产精品日韩| 亚洲福利在线视频| 免费成人在线视频网站| 久久久久久**毛片大全| 黄色成人av在线| 羞羞答答国产精品www一本| 久久影院午夜片一区| 先锋亚洲精品| 国产人久久人人人人爽| 欧美在线地址| 久久国产精品久久久久久| 亚洲精品视频免费| 久久综合伊人| 亚洲国产高潮在线观看| 亚洲缚视频在线观看| 蜜臀99久久精品久久久久久软件| 亚洲国产成人porn| 欧美激情视频给我| 欧美日韩色一区| 午夜视频一区二区| 亚洲欧美日韩精品在线| 狠狠色综合网站久久久久久久| 另类天堂视频在线观看| 免费成人高清视频| 亚洲网站视频福利| 亚洲视频日本| 国产一级久久| 欧美激情一二三区| 国产精品久久久久9999| 久久久噜噜噜久久中文字幕色伊伊| 久久久久国内| 中文在线资源观看网站视频免费不卡| 一区二区三区免费在线观看| 香港成人在线视频| 亚洲一级特黄| 久久精品亚洲精品| 亚洲精品之草原avav久久| 亚洲精品日韩在线观看| 国产精品欧美经典| 麻豆精品一区二区av白丝在线| 欧美激情91| 亚洲主播在线| 免费在线国产精品| 久久国产视频网站| 欧美日韩国产精品成人| 久久久精品日韩| 欧美亚州韩日在线看免费版国语版| 欧美诱惑福利视频| 免费黄网站欧美| 欧美一级片一区| 欧美成人免费一级人片100| 欧美一区二区三区四区在线观看地址| 蜜臀91精品一区二区三区| 亚洲女同精品视频| 欧美高清一区二区| 老牛嫩草一区二区三区日本| 欧美午夜不卡| 91久久精品一区| 国产在线一区二区三区四区| 亚洲看片网站| 久久成人精品电影| 欧美日韩精品一区二区三区四区| 久久久久一本一区二区青青蜜月| 久久青草久久| 亚洲欧美视频一区| 欧美全黄视频| 欧美激情乱人伦| 激情欧美一区二区| 性久久久久久| 午夜精品免费视频| 欧美日韩专区| 日韩一级大片| 日韩一级成人av| 免费亚洲一区| 亚洲高清视频中文字幕| 在线观看视频日韩| 欧美在线视频观看免费网站| 性xx色xx综合久久久xx| 欧美私人啪啪vps| 在线亚洲欧美视频| 亚洲欧洲av一区二区| 男女激情久久| 亚洲人在线视频| 一区二区精品国产| 欧美日韩一区二区三区四区五区| 亚洲国产成人精品久久| 亚洲国产精品99久久久久久久久| 久久久亚洲国产天美传媒修理工| 久久在线免费观看视频| 韩日欧美一区| 久久久久国产精品www| 牛牛影视久久网| 亚洲欧洲精品一区二区三区| 欧美高清在线一区| 看欧美日韩国产| 一区二区在线观看视频在线观看 | 久久精品九九| 乱码第一页成人| 国产一区av在线| 久久久亚洲高清| 99国产精品久久久久老师| 亚洲欧美精品| 一区二区视频免费在线观看| 蜜桃av综合| 夜夜嗨av一区二区三区四季av | 亚洲日韩视频| 中文欧美在线视频| 国产精品日韩一区二区| 欧美专区在线| 亚洲黄色免费| 午夜欧美大片免费观看| 国内自拍一区| 欧美激情精品久久久久久蜜臀| 亚洲美女精品久久| 久久久久91| 日韩一级精品| 国产精品一区一区三区| 久久激情婷婷| 日韩一级在线观看| 鲁大师影院一区二区三区| 亚洲精品在线三区| 国产精品区一区二区三区| 欧美一区永久视频免费观看| 国产永久精品大片wwwapp| 女人天堂亚洲aⅴ在线观看| 亚洲精品影视| 国产亚洲激情| 欧美日韩亚洲国产一区| 香蕉免费一区二区三区在线观看| 欧美国产专区| 久久久精品免费视频| 亚洲一级二级在线| 亚洲日本va午夜在线电影| 国产日韩一区二区三区在线| 欧美精品一区二区三区很污很色的| 欧美一区二区三区免费观看视频| 亚洲精选一区二区| 欧美暴力喷水在线| 久久在精品线影院精品国产| 午夜精品久久久久久99热软件 | 国产精品一二三四| 欧美黑人国产人伦爽爽爽|