在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=sMessage
end function
'caller
'vb封装了对HRESULT错误代码的处理,如有错误引发COM异常
on error resume next
MsgBox 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

本文探讨了COM编程中safecall的概念及其在不同编程语言中的实现方式,包括VB、VC++等,强调了safecall对于增强程序稳定性和正确处理COM异常的重要性。

61

被折叠的 条评论
为什么被折叠?



