在 COM 中,每个方法都是一个返回 HRESULT 的函数:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
这是 COM 中的绝对规则:
- COM 中没有异常
- 一切都返回 HRESULT
- 负 HRESULT 表示失败
- 在高级语言中,失败被映射到异常
COM 设计者的意图是,高级语言会自动将 Failed 方法转换为异常。
因此,在您自己的语言中,COM 调用将在没有 HRESULT 的情况下表示。例如:
-
类似德尔福:
function AddSymbol(ASymbol: OleVariant): WordBool;
-
类 C#:
WordBool AddSymbol(OleVariant ASymbol);
在 Delphi 中,您可以选择使用原始函数签名:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
并自己处理引发的异常:
bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;
hr := thingy.AddSymbol('Seven', {out}bAdded);
if Failed(hr) then
OleError(hr);
或更短的等价物:
bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;
hr := thingy.AddSymbol('Seven', {out}bAdded);
OleCheck(hr);
或更短的等价物:
bAdded: WordBool;
thingy: IThingy;
OleCheck(thingy.AddSymbol('Seven'), {out}bAdded);
COM 不打算让你处理 HRESULT
但您可以要求 Delphi 将管道隐藏起来,以便您继续编程:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
end;
在幕后,编译器仍会检查返回的 HRESULT,如果 HRESULT 指示失败(即是否定的),则抛出 EOleSysError 异常。编译器生成的 safecall 版本在功能上等同于:
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
var
hr: HRESULT;
begin
hr := AddSymbol(ASymbol, {out}Result);
OleCheck(hr);
end;
但它让你可以简单地调用:
bAdded: WordBool;
thingy: IThingy;
bAdded := thingy.AddSymbol('Seven');
tl;dr:您可以使用任一:
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
但前者要求您每次都处理 HRESULT。
奖金聊天
您几乎不想自己处理 HRESULT;它使程序杂乱无章,并没有增加任何噪音。但有时您可能想自己检查 HRESULT(例如,您想处理不是非常特殊的故障)。从来没有版本的 Delphi 开始包含以两种方式声明的已翻译的 Windows 标头接口:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
IThingySC = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant): WordBool); safecall;
end;
或来自 RTL 来源:
ITransaction = interface(IUnknown)
['{0FB15084-AF41-11CE-BD2B-204C4F4F5020}']
function Commit(fRetaining: BOOL; grfTC: UINT; grfRM: UINT): HResult; stdcall;
function Abort(pboidReason: PBOID; fRetaining: BOOL; fAsync: BOOL): HResult; stdcall;
function GetTransactionInfo(out pinfo: XACTTRANSINFO): HResult; stdcall;
end;
{ Safecall Version }
ITransactionSC = interface(IUnknown)
['{0FB15084-AF41-11CE-BD2B-204C4F4F5020}']
procedure Commit(fRetaining: BOOL; grfTC: UINT; grfRM: UINT); safecall;
procedure Abort(pboidReason: PBOID; fRetaining: BOOL; fAsync: BOOL); safecall;
procedure GetTransactionInfo(out pinfo: XACTTRANSINFO); safecall;
end;
SC 后缀代表 safecall。这两个接口是等效的,您可以根据需要选择声明 COM 变量的方式:
//thingy: IThingy;
thingy: IThingySC;
你甚至可以在它们之间投射:
thingy: IThingSC;
bAdded: WordBool;
thingy := CreateOleObject('Supercool.Thingy') as TThingySC;
if Failed(IThingy(thingy).AddSymbol('Seven', {out}bAdded) then
begin
//Couldn't seven? No sixty-nine for you
thingy.SubtractSymbol('Sixty-nine');
end;
额外奖励聊天 - C#
C# 默认情况下相当于 Delphi safecall,但在 C# 中除外:
在 C# 中,您可以将 COM 接口声明为:
[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
WordBool AddSymbol(OleVariant ASymbol);
WordBool SubtractSymbol(OleVariant ASymbol);
}
您会注意到 COM HRESULT 对您隐藏。 C#编译器和Delphi编译器一样,会自动检查返回的HRESULT并为你抛出异常。
在 C# 中,与在 Delphi 中一样,您可以选择自己处理 HRESULT:
[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
[PreserveSig]
HRESULT AddSymbol(OleVariant ASymbol, out WordBool RetValue);
WordBool SubtractSymbol(OleVariant ASymbol);
}
[PreserveSig] 告诉编译器按原样保留方法签名:
指示具有 HRESULT 或 retval 返回值的非托管方法是直接转换还是 HRESULT 或 retval返回值会自动转换为异常。