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?

Foi útil?

Solução

Safecall passa parâmetros da direita para a esquerda, em vez do pascal ou registar-se (default) da esquerda para a direita

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top