H
hubdog
Unregistered / Unconfirmed
GUEST, unregistred user!
如何在COM中实现正确的错误处理 (作者Binh Ly 翻译hubdog)
在COM中,每个接口方法必须返回一个错误代码给客户端,错误代码是标准的32-位数值,也就是我们所熟悉的HRESULT。HRESULT数值可以分为几部分:一位用于表示成功或失败,几位用于表示错误分类,剩下几位用于表示错误代号(COM推荐错误代码应该在0200到FFFF 范围内。
虽然HRESULT可以用来指示错误,但是它也有很大的局限性,因为除了错误代码,我们可能还想让COM服务器告诉客户端错误的详细描述,发生位置以及客户在哪可以得到更多的相关帮助(通过指定帮助上下文来调用帮助文件)。因此COM引入了IErrorInfo接口,客户端可以通过这个接口来获得额外的错误信息。同时如果COM服务器支持IErrorInfo,COM同时建议服务器实现ISupportErrorInfo接口,虽然这个接口不是必须实现的,但一些客户端,比如Visual Basic将会向服务器请求这个接口。
Delphi本身已经为我们提供了安全调用处理。当我们在对象内部产生一个异常时,Delphi会自动俘获异常并把它转化为一个COM HRESULT,同时提供一个IErrorInfo 接口用于传递给客户端。这些是通过ComObj单元中的HandleSafeCallException函数实现的。此外,VCL 类也为我们实现了ISupportErrorInfo 接口。
下面举例来说,当我们在服务器内部产生一个Ewhatever的异常时,它总会被客户端认为是EOleException异常,EOleException异常包括HRESULT 和IErrorInfo 所包含的所有信息,比如错误代号,描述,发生位置以及上下文相关帮助。而为了提供客户端所需要信息,服务器必须把EWhatever转化为EoleSysError异常,同时要确保错误代码为格式化好的HRESULT。比如,假设我们有一个TFoo对象,它有一个Bar方法。在Bar方法中我们想产生一个异常,异常的错误代号为5,描述="错误消息",帮助文件="HelpFile.hlp",帮助上下文= 1,代码示意如下:
uses ComServ;
const
CODE_BASE = $200; //推荐代码在0200 – FFFF之间
procedure TFoo.Bar;
begin
//帮助文件
ComServer.HelpFileName := 'HelpFile.hlp';
//引发异常
raise EOleSysError.Create (
'错误消息', ErrorNumberToHResult (5 + CODE_BASE), //格式化HRESULT
1 //帮助上下文
);
end;
//格式化HResult
function ErrorNumberToHResult (ErrorNumber : integer) : HResult;
const
SEVERITY_ERROR = 1;
FACILITY_ITF = 4;
begin
Result := (SEVERITY_ERROR shl 31) or (FACILITY_ITF shl 16) or word (ErrorNumber);
end;
上面的ErrorNumberToHResult函数就是简单的把我们的错误代号转化为标准的HRESULT。同时我们给错误代号加上了CODE_BASE (0x200)以便遵循COM的建议就是使错误代码位于0200到 FFFF之间。
下面是客户端利用EOleException俘获错误的代码:
const
CODE_BASE = $200;
procedure CallFooBar;
var
Foo : IFoo;
begin
Foo := CoFoo.Create;
try
Foo.Bar;
except
on E : EOleException do
ShowMessage ('错误信息: ' + E.Message + #13 +
'错误代号: ' + IntToStr (HResultToErrorNumber (E.ErrorCode) - CODE_BASE) + #13 +
'发生位置: ' + E.Source + #13 +
'帮助文件: ' + E.HelpFile + #13 +
'帮助上下文: ' + IntToStr (E.HelpContext)
);
end;
end;
function HResultToErrorNumber (hr : HResult) : integer;
begin
Result := (hr and $FFFF);
end;
上面过程其实就是服务器的逆过程,就是从HRESULT中提取错误代码,并显示额外错误信息的过程。
发文庆祝一下))
在COM中,每个接口方法必须返回一个错误代码给客户端,错误代码是标准的32-位数值,也就是我们所熟悉的HRESULT。HRESULT数值可以分为几部分:一位用于表示成功或失败,几位用于表示错误分类,剩下几位用于表示错误代号(COM推荐错误代码应该在0200到FFFF 范围内。
虽然HRESULT可以用来指示错误,但是它也有很大的局限性,因为除了错误代码,我们可能还想让COM服务器告诉客户端错误的详细描述,发生位置以及客户在哪可以得到更多的相关帮助(通过指定帮助上下文来调用帮助文件)。因此COM引入了IErrorInfo接口,客户端可以通过这个接口来获得额外的错误信息。同时如果COM服务器支持IErrorInfo,COM同时建议服务器实现ISupportErrorInfo接口,虽然这个接口不是必须实现的,但一些客户端,比如Visual Basic将会向服务器请求这个接口。
Delphi本身已经为我们提供了安全调用处理。当我们在对象内部产生一个异常时,Delphi会自动俘获异常并把它转化为一个COM HRESULT,同时提供一个IErrorInfo 接口用于传递给客户端。这些是通过ComObj单元中的HandleSafeCallException函数实现的。此外,VCL 类也为我们实现了ISupportErrorInfo 接口。
下面举例来说,当我们在服务器内部产生一个Ewhatever的异常时,它总会被客户端认为是EOleException异常,EOleException异常包括HRESULT 和IErrorInfo 所包含的所有信息,比如错误代号,描述,发生位置以及上下文相关帮助。而为了提供客户端所需要信息,服务器必须把EWhatever转化为EoleSysError异常,同时要确保错误代码为格式化好的HRESULT。比如,假设我们有一个TFoo对象,它有一个Bar方法。在Bar方法中我们想产生一个异常,异常的错误代号为5,描述="错误消息",帮助文件="HelpFile.hlp",帮助上下文= 1,代码示意如下:
uses ComServ;
const
CODE_BASE = $200; //推荐代码在0200 – FFFF之间
procedure TFoo.Bar;
begin
//帮助文件
ComServer.HelpFileName := 'HelpFile.hlp';
//引发异常
raise EOleSysError.Create (
'错误消息', ErrorNumberToHResult (5 + CODE_BASE), //格式化HRESULT
1 //帮助上下文
);
end;
//格式化HResult
function ErrorNumberToHResult (ErrorNumber : integer) : HResult;
const
SEVERITY_ERROR = 1;
FACILITY_ITF = 4;
begin
Result := (SEVERITY_ERROR shl 31) or (FACILITY_ITF shl 16) or word (ErrorNumber);
end;
上面的ErrorNumberToHResult函数就是简单的把我们的错误代号转化为标准的HRESULT。同时我们给错误代号加上了CODE_BASE (0x200)以便遵循COM的建议就是使错误代码位于0200到 FFFF之间。
下面是客户端利用EOleException俘获错误的代码:
const
CODE_BASE = $200;
procedure CallFooBar;
var
Foo : IFoo;
begin
Foo := CoFoo.Create;
try
Foo.Bar;
except
on E : EOleException do
ShowMessage ('错误信息: ' + E.Message + #13 +
'错误代号: ' + IntToStr (HResultToErrorNumber (E.ErrorCode) - CODE_BASE) + #13 +
'发生位置: ' + E.Source + #13 +
'帮助文件: ' + E.HelpFile + #13 +
'帮助上下文: ' + IntToStr (E.HelpContext)
);
end;
end;
function HResultToErrorNumber (hr : HResult) : integer;
begin
Result := (hr and $FFFF);
end;
上面过程其实就是服务器的逆过程,就是从HRESULT中提取错误代码,并显示额外错误信息的过程。
发文庆祝一下))