在COM編程中正確的處理COM異常,是增強程式魯棒性的基礎。在delphi中有safecall的概念,在vb中雖然沒有提及此概念,但仍然在使用。在vc中,需要手工支援。那什麼是safecall,與COM異常又有什麼關係呢?
safecall其實是約定了COM組件的提供者與調用者之間調用的方式。 safecall首先是一個stdcall,也就是說COM組件暴露的所有介面方法採用與windows API一致的stdcall呼叫慣例。其次,safecall還要求介面方法均返回HRESULT錯誤碼,不能引發COM異常;調用者在調用介面方法後,均要檢查HRESULT,對錯誤進行相應的處理。也就是說,safecall保證在方法內部封裝了所有COM異常,調用者不必處理異常。
例如:IProvider提供一個方法Echo,我們看不同語言不同範式的區別。
vb
'provider'vb封裝了COM異常和HRESULT錯誤碼function Echo(sMessage as String) as String Echo=sMessageend function'caller'vb封裝了對HRESULT錯誤碼的處理,如有錯誤引發COM異常on error resume nextMsgBox Echo("Hello World!")
vc++ -- 一般方式
//provider//需要手工用try catch封裝異常,保證所有錯誤均通過HRESULT返回HRESULT Echo(BSTR bstrMessage,BSTR* pbstrValue){ try { *pbstrValue=::SysAllocString(L"Hello World!"); } catch(_com_error& e) { return e.Error(); } return S_OK;}//caller//一般方式_bstr_t bstrValue;HRESULT hr=pProvider->Echo(_bstr_t(L"Hello World!"),bstrValue.GetAddress());if(FAILED(hr)){ //這裡有兩種處理方式, //第一種,直接返回錯誤 return hr; //第二種,返回異常,就像vb的方式一樣 _com_util::CheckError(hr);}//如果provider組件是通過#import匯入的,可有如下的方式調用try{ _bstr_t bstrValue=pProvider->Echo(L"Hello World!");}catch(_com_error& e){ return e.Error();}
vb般的vc++
//provider//採用自訂宏封裝HRESULT Echo(BSTR bstrMessage,BSTR* pbstrValue){__SAFECALL_BEGIN; *pbstrValue=::SysAllocString(L"Hello World!");__SAFECALL_END;}//caller//通過#import匯入//如果是內部調用的方法,不必再檢測com異常;//因為向外暴露的方法均通過safecall對com異常進行了封裝_bstr_t bstrValue=pProvider->Echo(L"Hello World!");
自訂宏
#define __SAFECALL_BEGIN / try{/ #define __SAFECALL_END / / }/ catch(_com_error& e){/ return e.Error();/ }/ catch(...){/ return E_FAIL;/ }/ return S_OK