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

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>
            亚洲国产精品成人| 亚洲日本久久| 麻豆亚洲精品| 日韩午夜精品| 久久天天躁夜夜躁狠狠躁2022| 欧美国产欧美亚洲国产日韩mv天天看完整 | 欧美视频一区二区三区…| 欧美日韩国产精品| 在线播放中文字幕一区| 欧美一区二区视频观看视频| 亚洲精品在线免费| 欧美黄色aaaa| 日韩亚洲国产欧美| 91久久久久久久久久久久久| 久久综合九色综合久99| 久久都是精品| 亚洲欧美电影在线观看| 欧美日韩在线播放三区四区| 在线一区亚洲| 日韩亚洲欧美成人| 欧美激情精品久久久久久变态| 91久久极品少妇xxxxⅹ软件| 榴莲视频成人在线观看| 久久精品国产免费看久久精品| 国产女优一区| 久久精品亚洲一区| 久久精品免费| 亚洲国产精品999| 欧美激情精品久久久久| 欧美成人精品激情在线观看 | 在线精品高清中文字幕| 蜜臀av性久久久久蜜臀aⅴ四虎| 欧美专区亚洲专区| 亚洲成人在线| 亚洲国产毛片完整版 | 国产精品大全| 牛夜精品久久久久久久99黑人| 亚洲一区一卡| 小处雏高清一区二区三区 | 亚洲在线视频一区| 国产欧美亚洲精品| 麻豆久久婷婷| 欧美成人69| 亚洲性线免费观看视频成熟| 亚洲天堂网站在线观看视频| 国产免费成人av| 美女视频黄 久久| 欧美日韩国产二区| 性欧美videos另类喷潮| 久久精品视频在线| 亚洲美女在线国产| 亚洲一区在线免费观看| 精品成人国产| 洋洋av久久久久久久一区| 国产综合在线看| 亚洲国产日韩欧美一区二区三区| 国产精品xxx在线观看www| 久久综合久久88| 国产精品va在线播放我和闺蜜| 久久aⅴ国产紧身牛仔裤| 免费在线看一区| 欧美综合国产| 欧美特黄一级| 欧美高清在线一区| 国产精品久久久久99| 欧美成人按摩| 国产欧美69| 亚洲激情校园春色| 国产亚洲a∨片在线观看| 91久久精品久久国产性色也91| 麻豆精品一区二区av白丝在线| 亚洲欧美中文日韩v在线观看| 狠狠色狠狠色综合日日91app| 亚洲精品免费观看| 国内外成人在线视频| 日韩亚洲欧美中文三级| 在线日韩av片| 亚欧美中日韩视频| 午夜激情亚洲| 欧美日韩免费区域视频在线观看| 狂野欧美激情性xxxx| 国产精品久久久一本精品| 亚洲电影在线播放| 国产欧美日韩综合一区在线观看 | 国产精品美女久久久久av超清| 久久久免费精品| 国产精品久久久久高潮| 91久久精品一区二区三区| 国内偷自视频区视频综合| 中文成人激情娱乐网| 在线亚洲高清视频| 欧美激情网友自拍| 亚洲国产成人porn| 亚洲欧洲日夜超级视频| 久久久精品国产99久久精品芒果| 欧美亚洲在线视频| 国产精自产拍久久久久久| 一区二区三区www| 宅男噜噜噜66国产日韩在线观看| 欧美99在线视频观看| 美女视频黄a大片欧美| 韩国三级在线一区| 久久久久一区| 你懂的视频一区二区| 亚洲高清av在线| 美女被久久久| 亚洲精品久久久一区二区三区| 亚洲免费成人| 亚洲一二三四久久| 欧美天堂亚洲电影院在线观看 | 欧美激情视频一区二区三区在线播放 | 亚洲欧洲一区| 免费在线成人av| 亚洲精品综合精品自拍| 中文精品99久久国产香蕉| 欧美色播在线播放| 亚洲午夜视频在线观看| 午夜一区在线| 国产主播一区二区三区| 久久国产一区二区| 欧美激情亚洲自拍| 一区二区三区不卡视频在线观看| 欧美视频在线不卡| 欧美国产综合视频| 亚洲一级网站| 欧美一区二区三区精品| 国产日韩欧美视频在线| 欧美在线播放视频| 欧美国产精品中文字幕| 亚洲免费观看视频| 国产精品成人观看视频国产奇米| 亚洲宅男天堂在线观看无病毒| 亚久久调教视频| 91久久国产综合久久91精品网站| 欧美日韩在线观看一区二区三区 | 在线亚洲+欧美+日本专区| 欧美一区二区三区在线观看视频| 国外成人在线视频网站| 欧美福利电影网| 亚洲欧美日本伦理| 亚洲国产经典视频| 亚洲欧美日本国产专区一区| 国内精品免费在线观看| 欧美日本网站| 久久久久久久波多野高潮日日 | 99精品视频免费在线观看| 国产精品日韩精品| 麻豆亚洲精品| 欧美一区二区三区的| 91久久精品国产91久久性色tv | 欧美在线观看一区二区三区| 欧美福利在线| 亚洲欧美综合精品久久成人| 红桃视频国产精品| 国产精品二区二区三区| 女生裸体视频一区二区三区| 亚洲一区二区三区涩| 亚洲成色777777在线观看影院| 午夜亚洲影视| 在线视频亚洲| 亚洲欧洲三级电影| 国产视频一区在线观看| 欧美国产成人精品| 久久综合久久88| 欧美一区二区三区婷婷月色 | 欧美一区二区国产| 亚洲精品中文字幕有码专区| 国产日韩一区欧美| 国产精品白丝av嫩草影院| 欧美福利视频网站| 麻豆av一区二区三区久久| 亚洲欧美日韩精品综合在线观看| 999亚洲国产精| 亚洲片区在线| 亚洲国产天堂久久综合网| 欧美成人xxx| 亚洲国产高清一区二区三区| 国产精品一区三区| 欧美性猛交99久久久久99按摩| 欧美a级片网| 欧美岛国激情| 蜜臀久久99精品久久久久久9| 午夜精品久久久久影视| 亚洲在线第一页| 一区二区三区久久网| 一本久久a久久免费精品不卡| 亚洲黄色免费电影| 亚洲毛片在线看| 亚洲激情在线视频| 亚洲免费观看| 欧美大片在线观看一区| 欧美激情一区二区久久久| 久久精品视频va| 午夜综合激情| 久久久久久国产精品一区| 久久久噜噜噜久久中文字免| 久久综合五月| 亚洲黄色尤物视频| 亚洲一级在线观看| 久久九九电影|