1. 程式人生 > >COM元件設計與應用(十二)——錯誤與異常處理

COM元件設計與應用(十二)——錯誤與異常處理

一、前言

  程式設計中,錯誤處理必不可少,而且通常要佔用很大的篇幅。本回書著落在 COM 中的錯誤(異常)的處理方法。

  在元件程式中,如果遇到錯誤,一般有兩個方式進行處理。

  二、簡單返回

  對於比較簡單的錯誤,直接返回表示錯誤原因的 HRESULT。比如下面幾個就是常見的錯誤值:

E_INVALIDARG 0x80070057 引數錯誤
E_OUTOFMEMORY 0x8007000E 記憶體錯誤
E_NOTIMPL 0x80004001 未實現
E_POINTER 0x80004003 無效指標
E_HANDLE 0x80070006 無效控制代碼
E_ABORT 0x80004004 終止操作
E_ACCESSDENIED 0x80070005 拒絕訪問
E_NOINTERFACE 0x80004002 不支援介面
  另外,你還可以返回自己構造 HRESULT 錯誤值。方法是使用巨集 MAKE_HRESULT(sev,fac,code)
引數 含義 值(二進位制)

  sev 嚴重程度

成功 00
成功,但有一些報告資訊 01
警告 10
錯誤 11

  fac 裝置資訊

FACILITY_AAF 00000010010
FACILITY_ACS 00000010100
FACILITY_BACKGROUNDCOPY 00000100000
FACILITY_CERT 00000001011
FACILITY_COMPLUS 00000010001
FACILITY_CONFIGURATION 00000100001
FACILITY_CONTROL 00000001010
FACILITY_DISPATCH 00000000010
FACILITY_DPLAY 00000010101
FACILITY_HTTP 00000011001
FACILITY_INTERNET 00000001100
FACILITY_ITF 00000000100
FACILITY_MEDIASERVER 00000001101
FACILITY_MSMQ 00000001110
FACILITY_NULL 00000000000
FACILITY_RPC 00000000001
FACILITY_SCARD 00000010000
FACILITY_SECURITY 00000001001
FACILITY_SETUPAPI 00000001111
FACILITY_SSPI 00000001001
FACILITY_STORAGE 00000000011
FACILITY_SXS 00000010111
FACILITY_UMI 00000010110
FACILITY_URT 00000010011
FACILITY_WIN32 00000000111
FACILITY_WINDOWS 00000001000
FACILITY_WINDOWS_CE 00000011000

  code 唯一錯誤碼

16位(bit) 你自己定義去吧

  呼叫者得到返回的 HRESULT 值後,也可以使用巨集 HRESULT_SEVERITY()、HRESULT_FACILITY()、HRESULT_CODE() 來取得sev錯誤程度、fac裝置資訊和 code 錯誤程式碼。

  三、錯誤資訊介面

  既然 COM 是靠各種各樣的介面來提供服務的,於是很自然地就會想到,是否有一個介面能夠提供更豐富的錯誤資訊報告那?答案是:ISupportErrorInfo。下面這段程式碼是使用 ISupportErrorInfo 的一般方法:

雙擊程式碼全選
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 STDMETHODIMP Cxxx::fun() {   ... ... ... ...   CComQIPtr< ICreateErrorInfo> spCEI;   ::CreateErrorInfo( &spCEI );   spCEI->SetGUID( IID_Ixxx );    // 發生錯誤的介面IID   spCEI->SetSource( L"xxx.xxx" );  // ProgID   // 如果你的元件同時提供了幫助檔案,那麼就可以:   spCEI->SetHelpContext( 0 );    // 設定幫助檔案的主題號   spCEI->SetHelpFile( L"xxx.hlp" );  // 設定幫助檔案的檔名   spCEI->SetDescription( L"錯誤描述資訊" );   CComQIPtr < IErrorInfo > spErrInfo = spCEI;   if( spErrInfo )    ::SetErrorInfo( 0, spErrInfo );  // 這時呼叫者就可以得到錯誤資訊了   return E_FAIL; }

  上面是原理性程式碼,在我們寫的程式中,不用這麼麻煩。因為 ATL 已經把上述的程式碼給我們包裝成 CComCoClass::Error() 的6個過載函數了。如此,我們可以非常簡單的改寫為:

雙擊程式碼全選
1 2 3 4 5 STDMETHODIMP Cxxx::fun() {   ... ... ... ...   return Error( L"錯誤描述資訊" ); }

  四、關於 try/catch

學習了 C++ 後,很多人都喜歡使用 try/catch 的異常處理結構。如果你使用 vc6.0 的ATL,編譯器預設是不支援異常處理的,編譯後會報告“warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX”,解決方法是手工加上編譯開關:

  圖一、加上編譯開關,支援C++的異常處理結構

  在vc.net 2003 中,編譯器預設是支援異常處理結構的,所以不用特別進行設定。如果想減小目標檔案的尺寸,你也可以決定不使用 C++ 異常處理,那麼在專案屬性中

  圖二、在vc.net中修改是否支援C++異常結構的編譯開關

  五、客戶端接收元件的錯誤資訊

  1、如果使用 API 方式呼叫元件,接收錯誤的方法是:

雙擊程式碼全選
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 HRESULT hr = spXXX->fun()  // 呼叫元件功能 if( FAILED( hr ) )  // 如果發生了錯誤 {   CComQIPtr < ISupportErrorInfo > spSEI = spXXX;  // 元件是否提供了 ISupportErrorInfo 介面?   if( spSEI )  // 如果支援,那麼   {     hr = spSEI->InterfaceSupportsErrorInfo( IID_Ixxx );  // 是否支援 Ixxx 介面的錯誤處理?     if( SUCCEEDED( hr ) )     {  // 支援,太好了。取出錯誤資訊       CComQIPtr < IErrorInfo > spErrInfo;    // 宣告 IErrorInfo 介面       hr = ::GetErrorInfo( 0, &spErrInfo );  // 取得介面       if( SUCCEEDED( hr ) )       {         CComBSTR bstrDes;         spErrInfo->GetDescription( &bstrDes );  // 取得錯誤描述         ......  // 還可以取得其它的資訊       }     }   } }

  2、如果使用 #import 等包裝方式呼叫元件,接收錯誤的方法是:

雙擊程式碼全選
1 2 3 4 5 6 7 8 9 try {   ......  // 呼叫元件功能 } catch( _com_error &e ) {   e.Description();  // 取得錯誤描述資訊   ......  // 還可以呼叫 _com_error 函式取得其它資訊 }

  六、編寫支援錯誤處理的元件程式

  非常簡單,只要在增加 ATL 元件物件的時候選中 ISupportErrorInfo 即可。

  圖三、vc6.0 中,選中元件支援錯誤處理介面

  圖四、vc.net 2003 中,選中元件支援錯誤處理介面

  七、小結

  閱讀文章後,請下載本回的示例程式。示例程式中演示了三種錯誤處理方法和三種接收錯誤的方法,同時程式中也有比較詳細的註釋。

http://tech.ddvip.com/2006-04/11447206804882_2.html