Pergunta
Eu estou trabalhando na criação de um EXE ActiveX usando VB6, e o único exemplo que eu tenho é tudo escrito em Delphi.
A leitura do código de exemplo, notei que há algumas funções cujas assinaturas são seguidos pelo safecall palavra-chave. Aqui está um exemplo:
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
Qual é o propósito desta palavra-chave?
Solução
Safecall passa parâmetros da direita para a esquerda, em vez do pascal ou registar-se (default) da esquerda para a direita ??p>
Com safecall, os parâmetros de procedimento ou função remove da pilha sobre o retorno (como Pascal, mas não como cdecl onde cabe ao chamador)
Safecall implementos de exceção 'firewalls'; esp em Win32, notificação de erro COM esta entre processos implementos. Caso contrário, seria idêntico ao stdcall (a outra convenção de chamada usada com a vitória api)
Outras dicas
Além disso, o firewalls de exceção trabalho chamando SetErrorInfo () com um objeto que suporta IErrorInfo, de modo que o chamador pode obter informações estendidas sobre a exceção. Isso é feito pela substituição TObject.SafeCallException tanto TComObject e TAutoIntfObject. Ambos os tipos também implementar ISupportErrorInfo para marcar este fato.
No caso de uma exceção, o método safecall chamador pode consultar ISupportErrorInfo, em seguida, consulta que para a interface cujo método resultou em uma falha HRESULT (conjunto de bits alta), e se isso retorna S_OK, GetErrorInfo () pode obter a informação de exceção (descrição, ajuda, etc., na forma do implementação IErrorInfo que foi passado para SetErrorInfo () pelo Delphi RTL no SafeCallException substituições).
O que François disse e se não fosse por safecall sua chamada de método COM teria parecido abaixo e você teria que fazer sua própria verificação de erros em vez de ficar exceções.
function AddSymbol(ASymbol: OleVariant; out Result: WordBool): HResult; stdcall;
No COM, cada método é uma função que retorna um HRESULT
:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
Esta é uma regra absoluta no COM:
- não há exceções em COM
- tudo retorna um HRESULT
- HRESULT negativo indica uma falha
- em linguagens de alto nível, as falhas são mapeados para exceções
Foi a intenção dos designers COM que linguagens de alto nível, automaticamente, traduzir Falha métodos em uma exceção.
Assim, em seu próprio idioma, a invocação COM seria representada sem o HRESULT. Por exemplo:.
- Delphi-like :
function AddSymbol(ASymbol: OleVariant): WordBool;
- C # -como :
WordBool AddSymbol(OleVariant ASymbol);
Em Delphi você pode optar por usar a assinatura da função cru:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;
E lidar com o aumento de exceções-se:
bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;
hr := thingy.AddSymbol('Seven', {out}bAdded);
if Failed(hr) then
OleError(hr);
ou o mais curto equivalentes:
bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;
hr := thingy.AddSymbol('Seven', {out}bAdded);
OleCheck(hr);
ou o mais curto equivalentes:
bAdded: WordBool;
thingy: IThingy;
OleCheck(thingy.AddSymbol('Seven'), {out}bAdded);
COM não tinha a intenção para você lidar com HRESULTs
Mas você pode pedir Delphi para esconder que o encanamento longe de você, para que você possa continuar com a programação:
IThingy = interface
['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
function AddSymbol(ASymbol: OleVariant): WordBool); safecall;
end;
Nos bastidores, o compilador irá ainda verificar o HRESULT retorno, e lançar uma exceção EOleSysError
se o HRESULT indicou uma falha (ou seja, foi negativo). O gerado pelo compilador safecall versão é funcionalmente equivalente a:
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
var
hr: HRESULT;
begin
hr := AddSymbol(ASymbol, {out}Result);
OleCheck(hr);
end;
Mas libera-lo para chamar simplesmente:
bAdded: WordBool;
thingy: IThingy;
bAdded := thingy.AddSymbol('Seven');
tl; dr: Você pode usar:
function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
Mas o ex-exige que você lidar com as HRESULTs de cada vez.
Bonus Chatter
Você quase nunca quer lidar com as HRESULTs si mesmo; que enche-se o programa com o ruído que não acrescenta nada. Mas às vezes você pode querer verificar o HRESULT-se (por exemplo, você deseja lidar com uma falha que não é muito excepcional). Nunca versões do Delphi ter partida incluiu interfaces de cabeçalho do Windows traduzido que são declaradas em ambos os sentidos:
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;
ou a partir da fonte 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;
O SC sufixo significa safecall . Ambas as interfaces são equivalentes, e você pode escolher qual a declarar sua variável COM como dependendo do seu desejo:
//thingy: IThingy;
thingy: IThingySC;
Você pode até mesmo lançar entre eles:
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;
Bônus Extra Chatter - C #
C # por padrão faz o equivalente a Delphi safecall , exceto em C #:
- você tem que opt-out de safecall mapeamento
- em vez de opt-in
Em C # você iria declarar sua interface COM como:
[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
WordBool AddSymbol(OleVariant ASymbol);
WordBool SubtractSymbol(OleVariant ASymbol);
}
Você notará que o HRESULT
COM te é oculto. O compilador C #, como o compilador Delphi, irá verificar automaticamente o HRESULT retornado e lançar uma exceção para você.
E em C #, como em Delphi, você pode escolher para lidar com as HRESULTs-se:
[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);
}
O [PreserveSig] diz o compilador para preservar a assinatura do método exatamente como é:
Indica se métodos não gerenciados que têm HRESULT ou retval valores de retorno estão diretamente traduzido ou se HRESULT ou retval valores de retorno são automaticamente convertidos para exceções.