2012-01-21 12 views
22

Durante la navigazione System.Zip (Delphi XE2) per vedere come funziona, ho trovato questa funzione:Cosa significa `a ReturnAddress 'in Delphi?

procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer); 
begin 
    if Stream.Write(Buffer, Count) <> Count then 
    raise EZipException.CreateRes(@SZipErrorWrite) at ReturnAddress; 
end; 

E 'la parte at ReturnAddress quella sorta di mi lascia perplesso.

Non sapevo che at fosse una parola chiave valida (l'evidenziatore della sintassi non sembra riconoscerlo neanche).

Secondo l'IDE è dichiarato come System.ReturnAddress, ma posso trovarlo solo come etichetta da qualche parte nel codice (asm) di procedure _HandleAnyException;. L'unità di sistema è piena di riferimenti ad esso però.

Quindi quello che vorrei sapere è questo:

  1. Che cosa è ReturnAddress?
  2. Che cosa fa esattamente Raise Exception.Create ... at ReturnAddress?

Punti bonus se si può dare un esempio reale di dove questo sarebbe un costrutto utile, o se si può evitare di usarlo.

risposta

20

ReturnAddress è l'indirizzo a cui avrebbe restituito VerifyWrite al termine.

Raise Exception.Create... at ReturnAddress significa che quando viene visualizzata la finestra di dialogo delle eccezioni, indica l'indirizzo dell'eccezione allo ReturnAddress. In altre parole, il messaggio di eccezione leggere Exception <whatever> raised at <ReturnAddress>: <Exception Message>.

Ecco un estratto dal file di aiuto per Delphi 7. È quasi lo stesso di the online version.

Per generare un oggetto eccezione, utilizzare un'istanza dell'eccezione con un'istruzione raise. Ad esempio,

raise EMathError.Create; 

In generale, la forma di un'istruzione raise è

raise object at address 

cui oggetto e all'indirizzo sono entrambi opzionali; vedi Annullare nuovamente le eccezioni. Quando viene specificato un indirizzo, può essere qualsiasi espressione che restituisce un puntatore al tipo , ma di solito è un puntatore a una procedura o funzione. Ad esempio:

raise Exception.Create('Missing parameter') at @MyFunction; 

Utilizzare questa opzione per sollevare l'eccezione da un punto precedente nella pila quello in cui l'errore effettivamente verificato.

Nota l'ultima frase in particolare. È piuttosto specifico sull'uso di at <address>.

+0

@ain: Grazie per l'assistenza di formattazione. Non intendevo rimuovere il fatto che hai modificato, volevo solo sottolineare la frase finale del testo citato. :) –

+4

E l'uso del mondo reale per questo costrutto è generalmente se si usa una * funzione di supporto * per sollevare un'eccezione. Nella VCL, ad esempio, c'è 'TList.Error', da cui provengono tutti gli errori relativi a' TList'. Sapere che un'eccezione è stata sollevata in quella funzione non è utile per il debug, quindi usa la sintassi 'at' per riportare l'indirizzo di eccezione nella funzione che ha chiamato' Error', quindi quando cerchi l'indirizzo nel tuo map file, hai un'idea migliore di chi fosse il colpevole. (E perché usare un aiutante? Per uno, semplifica i codegen dei chiamanti.) –

+1

@RobKennedy: Lo stack di chiamate non rivelerebbe le stesse informazioni? – afrazier

8

ReturnAddr non era un puzzle con versioni Delphi precedenti.Considerare prossimo test (Delphi XE):

procedure RaiseTest1; 

    procedure RaiseException(ReturnAddr: Pointer); 
    begin 
    raise Exception.Create('OOPS!') at ReturnAddr; 
    end; 

asm 
     POP EAX 
     JMP RaiseException 
end; 

procedure RaiseTest2; 
begin 
    raise Exception.Create('OOPS!'); 
end; 


procedure TForm1.Button3Click(Sender: TObject); 
begin 
    RaiseTest1; 
end; 

procedure TForm1.Button4Click(Sender: TObject); 
begin 
    RaiseTest2; 
end; 

se si preme Button3 sotto debugger e premere il tasto 'pausa' in deroga messagebox, debugger si ferma a

procedure TForm1.Button3Click(Sender: TObject); 
begin 
    RaiseTest1; // <-- here 
end; 

se si preme Button4, debugger si ferma a

Come potete vedere, RaiseTest1 modifica il frame dello stack di eccezioni predefinito e rende il debug un po 'più semplice poiché l'unico scopo delle procedure RaiseTest1 (2) è di sollevare un exc eption.

Immagino che qualcosa sia cambiato in XE2 in modo che la sintassi ReturnAddr sia semplificata.

+0

Non capisco quale sintassi pensate sia cambiata. –

+1

@RobKennedy Penso che significhi da 'ReturnAddr' a' ReturnAddress', ma non ho trovato alcuna documentazione per questo. Ma sembra che 'ReturnAddr' fosse il nome di funzioni annidate in unità di sistema (si veda questo [bug] (http://qc.embarcadero.com/wc/qcmain.aspx?d=71294)). – ventiseis