【转载】COM 组件设计与应用(十二)——错误与异常处理
原文:http://vckbase.com/index.php/wv/1238.html
一、前言
程序设计中,错误处理必不可少,而且通常要占用很大的篇幅。本回书着落在 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 中,选中组件支持错误处理接口
七、小结
阅读文章后,请下载本回的示例程序。示例程序中演示了三种错误处理方法和三种接收错误的方法,同时程序中也有比较详细的注释。
【转载】COM 组件设计与应用(十二)——错误与异常处理的更多相关文章
- AngularJs的UI组件ui-Bootstrap分享(十二)——Rating
		
Rating是一个用于打分或排名的控件.看一个最简单的例子: <!DOCTYPE html> <html ng-app="ui.bootstrap.demo" x ...
 - 2017-2018-1 《Linux内核原理与设计》第十二周作业
		
<linux内核原理与设计>第十二周作业 Sql注入基础原理介绍 分组: 和20179215袁琳完成实验 一.实验说明 SQL注入攻击通过构建特殊的输入作为参数传入Web应用程序,而这 ...
 - 【转载】COM 组件设计与应用(二)——GUID 和 接口
		
原文:http://vckbase.com/index.php/wv/1203.html COM 组件设计与应用 系列文章:http://vckbase.com/index.php/piwz?& ...
 - Bootstrap组件福利篇:十二款好用的组件推荐
		
阅读目录 一.时间组件 1.效果展示 2.源码说明 3.代码示例 二.自增器组件 1.效果展示 2.源码说明 3.代码示例 三.加载效果 一.实用型 二.炫酷型 四.流程图小插件 1.效果展示 2.源 ...
 - HT图形组件设计之道(二)
		
上一篇我们自定义CPU和内存的展示界面效果,这篇我们将继续采用HT完成一个新任务:实现一个能进行展开和合并切换动作的刀闸控件.对于电力SCADA和工业控制等领域的人机交互界面常需要预定义一堆的行业标准 ...
 - xmlplus 组件设计系列之十 - 网格(DataGrid)
		
这一章我们要实现是一个网格组件,该组件除了最基本的数据展示功能外,还提供排序以及数据过滤功能. 数据源 为了测试我们即将编写好网格组件,我们采用如下格式的数据源.此数据源包含两部分的内容,分别是表头数 ...
 - 把握linux内核设计思想(十二):内存管理之slab分配器
		
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流.请勿用于商业用途] 上一节最后说到对于小内存区的请求,假设採用伙伴系统来进行分配,则会在页内产生非 ...
 - (转载)JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件
		
博客源地址:http://www.cnblogs.com/xdp-gacl/p/4216311.html 一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封 ...
 - Kafka设计解析(十二)Kafka 如何读取offset topic内容 (__consumer_offsets)
		
转载自 huxihx,原文链接 Kafka 如何读取offset topic内容 (__consumer_offsets) 众所周知,由于Zookeeper并不适合大批量的频繁写入操作,新版Kafka ...
 - WCF入门(十二)---WCF异常处理
		
WCF服务开发者可能会遇到需要以适当的方式向客户端报告一些不可预见的错误.这样的错误,称为异常,通常是通过使用try/catch块来处理,但同样,这是非常具体的技术. 由于客户端的关注领域不是关于如何 ...
 
随机推荐
- Ajax 请求下载 Execl 文件
			
通过Ajax请求下载Execl 的问题,掉进一个坑里半个多小时,特此来记录一下 . 起初 我误以为是后台的问题,然而调试了一下并不是这样的,也不会报错,且进入了success 函数. 以下的事件及请 ...
 - 转:C# 线程同步技术   Monitor 和Lock
			
原文地址:http://www.cnblogs.com/lxblog/archive/2013/03/07/2947182.html 今天我们总结一下 C#线程同步 中的 Monitor 类 和 Lo ...
 - [翻译] GMCPagingScrollView
			
GMCPagingScrollView https://github.com/GalacticMegacorp/GMCPagingScrollView GMCPagingScrollView is a ...
 - Python入门-模块2(sys模块、shutil 模块)
			
sys模块: sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 s ...
 - mysql创建表的注意事项
			
1 库名,表名,字段名必须使用小写字母,"_"分割. 2 库名,表名,字段名必须不超过12个字符. 3 库名,表名,字段名见名识意,建议使用名词而不是动词. 4 建议使用InnoD ...
 - HBase HA的分布式集群部署(适合3、5节点)
			
本博文的主要内容有: .HBase的分布模式(3.5节点)安装 .HBase的分布模式(3.5节点)的启动 .HBase HA的分布式集群的安装 .HBase HA的分布式集群的启动 .H ...
 - September 07th 2017 Week 36th Thursday
			
With the most true of yourself, can you meet the most suitable one. 用最真实的自己,才能遇见最合适的那个人. You are alw ...
 - 打开文件  和         字符串中%s 的大坑
			
ai`....
 - java枚举学习入门
			
一.前言 在java编程过程中,我们通常需要定义一些固定数量的常量,在jdk1.5以前,通常的做法是定义一个静态常量类,但自jdk1.5后,java引入了枚举(关键字enum,全称为 enumera ...
 - 【Alpha】Daily Scrum Meeting——blog1
			
团队成员 吴吉键 201421122007(组长) 魏修祺 201421122008 孙劲林 201421122022 1. 会议当天照片 忘记拍了,能补上不? 2. 每个人的工作 3. 燃尽图