Was ist sicher?

Ich arbeite an der Erstellung eines ActiveX-EXE mit VB6, und das einzige Beispiel, das ich bekam, ist alles in Delphi geschrieben.

Als ich den Beispielcode gelesen habe, habe ich bemerkt, dass es einige Funktionen gibt, auf deren Signaturen das Schlüsselwort safecall folgt. Hier ist ein Beispiel:

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

Was ist der Zweck dieses Keywords?

0
hinzugefügt bearbeitet
Ansichten: 8
<< a href = "http://en.wikipedia.org/wiki/X86_calling_conventions#safecall>" rel = "nofollow noreferrer"> de.wikipedia.org/wiki/X86_calling_conventions#safecall> ;
hinzugefügt der Autor Greg Hewgill, Quelle

4 Antworten

In COM ist jede Methode eine Funktion, die einen HRESULT zurückgibt:

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

Dies ist eine absolute Regel in COM:

  • Es gibt keine Ausnahmen in COM
  • alles gibt ein HRESULT
  • zurück
  • negativ HRESULT zeigt einen Fehler an
  • In höheren Sprachen werden Fehler Ausnahmen zugeordnet

Es war die Absicht der COM-Designer, dass höhere Sprachen automatisch Failed Methoden in eine Ausnahme übersetzen.

In Ihrer eigenen Sprache würde also der COM-Aufruf ohne HRESULT dargestellt. Z.B.:

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

In Delphi können Sie die Raw-Funktionssignatur verwenden:

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

Und kümmern Sie sich selbst um das Aufheben von Ausnahmen:

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

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

oder das kürzere Äquivalent:

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

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

oder das kürzere Äquivalent:

bAdded: WordBool;
thingy: IThingy;

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

COM hatte nicht vor, dass Sie sich mit HRESULTs beschäftigen

Aber du kannst Delphi bitten, diese Klempnerei von dir fern zu halten, damit du mit der Programmierung weitermachen kannst:

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

Hinter den Kulissen überprüft der Compiler immer noch die Rückgabe von HRESULT und löst eine EOleSysError -Ausnahme aus, wenn HRESULT einen Fehler anzeigt (d. H. Negativ war). Die vom Compiler generierte safecall -Version ist funktional äquivalent zu:

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

Aber es befreit Sie, einfach zu rufen:

bAdded: WordBool;
thingy: IThingy;

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

tl; dr: Sie können entweder:

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

Aber die erste erfordert, dass Sie jedes Mal die HRESULTs behandeln.

Bonus-Chatter

Sie möchten die HRESULTs fast nie selbst behandeln. es verstopft das Programm mit Rauschen, das nichts hinzufügt. Aber manchmal möchten Sie vielleicht das HRESULT selbst überprüfen (z. B. möchten Sie einen Fehler behandeln, der nicht sehr außergewöhnlich ist). Nie Versionen von Delphi haben angefangen übersetzte Windows-Header-Interfaces, die beide Wege deklariert sind:

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;

oder von der RTL-Quelle:

  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;

Das Suffix SC steht für safecall . Beide Schnittstellen sind gleichwertig und Sie können auswählen, wie Sie Ihre COM-Variable nach Ihren Wünschen deklarieren möchten:

//thingy: IThingy;
thingy: IThingySC;

Sie können sogar zwischen ihnen wechseln:

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;

Extra Bonus-Chatter - C#

C # verwendet standardmäßig das Äquivalent zu Delphi safecall , außer in C #:

  • Sie müssen das Safecall Mapping ablehnen
  • anstatt sich einzuschalten

In C# würden Sie Ihre COM-Schnittstelle wie folgt deklarieren:

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

Sie werden bemerken, dass das COM HRESULT für Sie verborgen ist. Der C# -Compiler überprüft wie der Delphi-Compiler automatisch das zurückgegebene HRESULT und löst eine Ausnahme für Sie aus.

Und in C# wie in Delphi können Sie die HRESULTs selbst verwalten:

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

The [PreserveSig] tells the compiler to preserve the method signature exactly as is:

Gibt an, ob nicht verwaltete Methoden mit HRESULT oder retval -Rückgabewerten direkt übersetzt werden oder ob HRESULT oder retval sind Rückgabewerte werden automatisch in Ausnahmen konvertiert.

0
hinzugefügt

Safecall übergibt Parameter von rechts nach links anstelle von Pascal oder Register (Standard) von links nach rechts

Mit Safecall entfernt die Prozedur oder Funktion beim Zurückkehren Parameter aus dem Stapel (wie pascal, aber nicht wie cdecl, wo der Aufrufer liegt)

Safecall implementiert Ausnahme "Firewalls"; esp auf Win32 implementiert das Interprozess COM-Fehlerbenachrichtigung. Ansonsten wäre es identisch mit stdcall (die andere Aufrufkonvention, die mit der win-API verwendet wird)

0
hinzugefügt

Darüber hinaus funktionieren die Ausnahme-Firewalls, indem sie SetErrorInfo() mit einem Objekt aufrufen, das IErrorInfo unterstützt, sodass der Aufrufer erweiterte Informationen über die Ausnahme erhalten kann. Dies geschieht durch die TObject.SafeCallException-Überschreibung in TComObject und TAutoIntfObject. Beide Typen implementieren auch ISupportErrorInfo, um diese Tatsache zu markieren.

Im Falle einer Ausnahme kann der Aufrufer der safecall-Methode nach ISupportErrorInfo fragen und dann das für die Schnittstelle abfragen, deren Methode zu einem Fehler HRESULT (high bit set) geführt hat, und wenn S_OK zurückgegeben wird, GetErrorInfo() kann die Ausnahmeinfo (Beschreibung, Hilfe, etc.) in Form der IErrorInfo-Implementierung erhalten, die wurde an SetErrorInfo() von Delphi RTL in den SafeCallException-Überschreibungen übergeben .

0
hinzugefügt

What Francois said and if it wasn't for safecall your COM method call would have looked like below and you would have to do your own error checking instead of getting exceptions.

function AddSymbol(ASymbol: OleVariant; out Result: WordBool): HResult; stdcall;
0
hinzugefügt