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 的一般方法:
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个重载函数了。如此,我们可以非常简单的改写为:
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 方式调用组件,接收错误的方法是:
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 等包装方式调用组件,接收错误的方法是:
try
{
...... // 调用组件功能
}
catch( _com_error &e )
{
e.Description(); // 取得错误描述信息
...... // 还可以调用 _com_error 函数取得其它信息
}六、编写支持错误处理的组件程序 非常简单,只要在增加 ATL 组件对象的时候选中 ISupportErrorInfo 即可。

图三、vc6.0 中,选中组件支持错误处理接口

图四、vc.net 2003 中,选中组件支持错误处理接口
七、小结
阅读文章后,请下载本回的示例程序。示例程序中演示了三种错误处理方法和三种接收错误的方法,同时程序中也有比较详细的注释。
posted on 2005-08-26 22:14 杨老师的茅屋 阅读(1348)
评论(2) 编辑 收藏