В COM каждый метод является функцией, которая возвращает HRESULT
:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
Это абсолютное правило в COM:
- в COM * нет исключений
- все возвращает HRESULT
- отрицательный HRESULT указывает на сбой
- в языках более высокого уровня ошибки сопоставляются с исключениями
Разработчики COM планировали, что языки более высокого уровня автоматически переведут ошибочные методы в исключение.
Так что на вашем родном языке вызов COM будет представлен без HRESULT. E.g.:
- Delphi-подобные :
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 и выдает исключение EOleSysError
, если значение HRESULT указывает на ошибку (т.е. был отрицательным). Генерируемая компилятором версия 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;
Дополнительный бонус Chatter - 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 возвращаются автоматически преобразуется в исключения.