Question

Je travaille sur la création d'un EXE ActiveX à l'aide de VB6, et le seul exemple que j'ai obtenu est tout écrit en Delphi.

En lisant l'exemple de code, j'ai remarqué qu'il existe certaines fonctions dont les signatures sont suivies du mot clé safecall . Voici un exemple:

function AddSymbol(ASymbol: OleVariant): WordBool; safecall;

Quel est le but de ce mot clé?

Était-ce utile?

La solution

Safecall passe les paramètres de droite à gauche au lieu du pascal ou du registre (par défaut) de gauche à droite

Avec safecall, la procédure ou la fonction supprime les paramètres de la pile lors du renvoi (comme pascal, mais pas comme cdecl, à l'appelant)

Safecall implémente une exception "pare-feu"; En particulier sur Win32, cela implémente la notification d'erreur COM interprocess. Sinon, il serait identique à stdcall (l’autre convention d’appel utilisée avec win api)

Autres conseils

De plus, les pare-feux d’exception fonctionnent en appelant SetErrorInfo () avec un objet prenant en charge IErrorInfo, afin que l’appelant puisse obtenir des informations étendues sur l’exception. Cette opération est effectuée par la substitution TObject.SafeCallException dans TComObject et TAutoIntfObject. Ces deux types implémentent également ISupportErrorInfo pour marquer ce fait.

En cas d'exception, l'appelant de la méthode safecall peut interroger ISupportErrorInfo, puis l'interroger pour l'interface dont la méthode a provoqué une défaillance HRESULT (jeu de bits élevés). Si cela renvoie S_OK, GetErrorInfo () peut obtenir les informations sur l'exception (description, aide, etc., sous la forme du Implémentation IErrorInfo transmise à SetErrorInfo () par la RTL de Delphi dans le SafeCallException remplace).

Ce que François a dit et s'il n'avait pas été sécurisé, votre appel à la méthode COM aurait ressemblé ci-dessous et vous devrez faire votre propre vérification d'erreur au lieu d'obtenir des exceptions.

function AddSymbol(ASymbol: OleVariant; out Result: WordBool): HResult; stdcall;

Dans COM, chaque méthode est une fonction qui renvoie un HRESULT :

.
IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
end;

C’est une règle absolue dans COM:

  • il n'y a pas d'exception dans COM
  • tout retourne un HRESULT
  • négatif HRESULT indique un échec
  • dans les langues de niveau supérieur, les échecs sont mappés à des exceptions

Les concepteurs COM avaient l'intention de faire en sorte que les langages de niveau supérieur traduisent automatiquement les méthodes en échec en une exception.

Donc, dans votre propre langue, l’invocation COM serait représentée sans HRESULT. Exemple:

  • Delphi : fonction AddSymbol (ASymbol: OleVariant): WordBool;
  • C-like : WordBool AddSymbol (OleVariant ASymbol);

Dans Delphi, vous pouvez choisir d'utiliser la signature de fonction brute:

bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;

hr := thingy.AddSymbol('Seven', {out}bAdded);
if Failed(hr) then
    OleError(hr);

Et gérez vous-même la levée des exceptions:

bAdded: WordBool;
thingy: IThingy;
hr: HRESULT;

hr := thingy.AddSymbol('Seven', {out}bAdded);
OleCheck(hr);

ou l'équivalent le plus court:

bAdded: WordBool;
thingy: IThingy;

OleCheck(thingy.AddSymbol('Seven'), {out}bAdded);

ou l'équivalent le plus court:

IThingy = interface
   ['{357D8D61-0504-446F-BE13-4A3BBE699B05}']
   function AddSymbol(ASymbol: OleVariant): WordBool); safecall;
end;

COM ne souhaitait pas que vous traitiez avec les HRESULTs

Mais vous pouvez demander à Delphi de vous cacher cette tuyauterie afin de pouvoir poursuivre la programmation:

function AddSymbol(ASymbol: OleVariant): WordBool; safecall;
var
   hr: HRESULT;
begin
   hr := AddSymbol(ASymbol, {out}Result);
   OleCheck(hr);
end;

Dans les coulisses, le compilateur vérifiera toujours le retour HRESULT et lèvera une exception EOleSysError si HRESULT indiquait un échec (c.-à-d. qu'il était négatif). La version safecall générée par le compilateur est fonctionnellement équivalente à:

bAdded: WordBool;
thingy: IThingy;

bAdded := thingy.AddSymbol('Seven');

Mais cela vous libère d'appeler simplement:

function AddSymbol(ASymbol: OleVariant; out RetValue: WordBool): HRESULT; stdcall;
function AddSymbol(ASymbol: OleVariant): WordBool; safecall;

tl; dr: vous pouvez utiliser soit:

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;

Mais l'ancien vous oblige à gérer les HRESULT à chaque fois.

Chatter bonus

Vous ne voulez presque jamais gérer les HRESULTs vous-même; il encombre le programme avec un bruit qui n'ajoute rien. Mais parfois, vous voudrez peut-être vérifier le HRESULT vous-même (par exemple, vous voulez gérer un échec qui n’est pas exceptionnel). Jamais les versions de Delphi n'ont démarré des interfaces d'en-tête Windows traduites incluses déclarées dans les deux sens:

  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;

ou à partir de la source RTL:

//thingy: IThingy;
thingy: IThingySC;

Le suffixe SC désigne safecall . Les deux interfaces sont équivalentes, et vous pouvez choisir laquelle déclarer votre variable COM comme vous le souhaitez:

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;

Vous pouvez même choisir entre eux:

[ComImport]
[Guid("{357D8D61-0504-446F-BE13-4A3BBE699B05}")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThingy
{
   WordBool AddSymbol(OleVariant ASymbol);
   WordBool SubtractSymbol(OleVariant ASymbol);
}

Extra Bonus Chatter - C #

C # fait par défaut l'équivalent de safecall dans Delphi, sauf en C #:

  • vous devez désactiver la cartographie Safecall
  • plutôt que de vous y inscrire

En C #, vous déclareriez votre interface COM comme suit:

[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);
}

Vous remarquerez que le code HRESULT COM est masqué. Le compilateur C #, comme le compilateur Delphi, vérifie automatiquement le HRESULT renvoyé et lève une exception pour vous.

Et en C #, comme dans Delphi, vous pouvez choisir de gérer vous-même les HRESULTs:

<*>

Le [PreserveSig] indique au compilateur de conserver la signature de la méthode exactement telle quelle:

  

Indique si les méthodes non gérées ayant des valeurs de retour HRESULT ou retval sont traduites directement ou si HRESULT ou retval les valeurs renvoyées sont automatiquement converties en exceptions.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top