Domanda

Sto lavorando alla creazione di un EXE ActiveX utilizzando VB6 e l'unico esempio che ho ottenuto è tutto scritto in Delphi.

Leggendo il codice di esempio, ho notato che ci sono alcune funzioni le cui firme sono seguite dal safecall parola chiave.Ecco un esempio:

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

Qual è lo scopo di questa parola chiave?

È stato utile?

Soluzione

Safecall passa i parametri da destra a sinistra, invece del pascal o del registro (impostazione predefinita) da sinistra a destra

Con safecall, la procedura o la funzione rimuove i parametri dallo stack al ritorno (come pascal, ma non come cdecl dove spetta al chiamante)

Safecall implementa "firewall" di eccezioni;specialmente su Win32, questo implementa la notifica di errore COM interprocesso.Altrimenti sarebbe identico a stdcall (l'altra convenzione di chiamata utilizzata con l'API win)

Altri suggerimenti

Inoltre, i firewall delle eccezioni funzionano chiamando SetErrorInfo() con un oggetto che supporta IErrorInfo, in modo che il chiamante possa ottenere informazioni estese sull'eccezione.Ciò viene eseguito tramite l'override di TObject.SafeCallException sia in TComObject che in TAutoIntfObject.Entrambi questi tipi implementano anche ISupportErrorInfo per evidenziare questo fatto.

In caso di eccezione, il chiamante del metodo safecall può eseguire una query per ISupportErrorInfo, quindi interrogarla per l'interfaccia il cui metodo ha provocato un errore HRESULT (bit alto impostato) e, se restituisce S_OK, Ottieni informazioni sull'errore() può ottenere le informazioni sull'eccezione (descrizione, aiuto, ecc., sotto forma di implementazione IErrorInfo che è stata passata a SetErrorInfo() da Delphi RTL negli override di SafeCallException).

Quello che ha detto François e se non fosse stato per Safecall, la chiamata al metodo COM sarebbe stata simile a quella riportata di seguito e dovresti eseguire il controllo degli errori invece di ottenere eccezioni.

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

In COM, ogni metodo è una funzione che restituisce un HRESULT:

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

Questa è una regola assoluta in COM:

  • non ci sono eccezioni in COM
  • tutto restituisce un HRESULT
  • HRESULT negativo indica un errore
  • nei linguaggi di livello superiore, gli errori vengono associati alle eccezioni

Era intenzione dei progettisti COM che i linguaggi di livello superiore si traducessero automaticamente Fallito metodi in un'eccezione.

Quindi nella tua lingua, l'invocazione COM verrebbe rappresentata senza HRESULT.Per esempio.:

  • Tipo Delfi: function AddSymbol(ASymbol: OleVariant): WordBool;
  • Simile a C#: WordBool AddSymbol(OleVariant ASymbol);

In Delphi puoi scegliere di utilizzare la firma della funzione grezza:

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

E gestisci tu stesso la generazione di eccezioni:

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

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

o l'equivalente più breve:

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

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

o l'equivalente più breve:

bAdded: WordBool;
thingy: IThingy;

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

COM non intendeva che tu avessi a che fare con HRESULT

Ma puoi chiedere a Delphi di nasconderti quell'impianto idraulico, così puoi procedere con la programmazione:

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

Dietro le quinte, il compilatore controllerà comunque il ritorno HRESULT e lancerà un file EOleSysError eccezione se HRESULT indica un errore (ad es.era negativo).Il file generato dal compilatore safecall la versione è funzionalmente equivalente a:

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

Ma ti rende libero di chiamare semplicemente:

bAdded: WordBool;
thingy: IThingy;

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

tl;dr:Puoi utilizzare:

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

Ma il primo richiede di gestire ogni volta gli HRESULT.

Chiacchiere bonus

Non vorrai quasi mai gestire tu stesso gli HRESULT;ingombra il programma con rumore che non aggiunge nulla.Ma a volte potresti voler controllare tu stesso l'HRESULT (ad es.vuoi gestire un fallimento che non sia del tutto eccezionale).Mai le versioni di Delphi hanno iniziato a includere interfacce di intestazione di Windows tradotte dichiarate in entrambi i modi:

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;

o dalla 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;

IL SC il suffisso sta per safecall.Entrambe le interfacce sono equivalenti e puoi scegliere come dichiarare la variabile COM in base alle tue esigenze:

//thingy: IThingy;
thingy: IThingySC;

Puoi anche trasmettere tra di loro:

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;

Chiacchiere bonus extra - C#

C# per impostazione predefinita fa l'equivalente di Delphi safecall, tranne che in C#:

  • devi disattivare la mappatura delle chiamate sicure
  • piuttosto che opt-in

In C# dichiareresti la tua interfaccia COM come:

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

Noterai che COM HRESULT ti è nascosto.Il compilatore C#, come il compilatore Delphi, controllerà automaticamente l'HRESULT restituito e genererà un'eccezione per te.

E in C#, come in Delphi, puoi scegliere di gestire tu stesso gli 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);
}

IL [Preserva Sig] dice al compilatore di preservare la firma del metodo esattamente così com'è:

Indica se i metodi non gestiti che hanno RISULTATO O retval i valori restituiti vengono tradotti direttamente o se RISULTATO O retval i valori restituiti vengono automaticamente convertiti in eccezioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top