2010-08-04 13 views
6

Aggiornamento: Sembra essere specifico per D2007. Funziona in D2010 come ha funzionato nella versione precedente.Come restituire un codice di errore con Halt (n) da un blocco Exception con D2007?

Vorrei restituire un codice di uscita a seconda del tipo di eccezione catturato nel blocco Eception Handler come:

program test; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

var 
    Exitcode: Integer; 
begin 
    Writeln('Enter error code:'); 
    Readln(Exitcode); 
    try 
    raise EExternal.Create('sdsdkfjh'); 
    except 
    on E:EExternal do 
    begin 
     Writeln(E.Classname, ': ', E.Message); 
     Halt(Exitcode); 
    end; 
    end; 
end. 

Purtroppo in D2007, chiamando Halt (n) da un blocco di eccezione restituisce sempre un codice di uscita 1, indipendentemente da ciò che si passa a Halt().

apparentemente perché uscendo da un gestore di eccezioni chiamate Finalizza, che cancella le eccezioni in sospeso (non rinunciare), chiamando SysUtils.ExceptHandler:

procedure ExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); far; 
begin 
    ShowException(ExceptObject, ExceptAddr); 
    Halt(1); // <= @#$##@#$! 
end; 

E non importa quello che il codice di uscita che volevo l'ho capito Halt(1)!

Quindi la domanda è:
Come posso semplicemente restituire il codice di uscita desiderato a seconda di quale eccezione è stata sollevata?

+0

Sulla base del commento qui sotto da Mike, che è vero, anzi fa ritorno l'errorcode corretta. Sospetto che sia il metodo che usi per ottenere il Codice errore che potrebbe non funzionare come previsto. – zz1433

+0

@Aldo. No, è D2007. La stessa identica cosa si comporta diversamente con D2007 e D2010 dove è tornata come mi aspettavo e segnalata da Mike. –

+0

Si prega di presentare un grande report in QC (http://qc.embarcadero.com/); anche se probabilmente non ci sarà un aggiornamento D2007, è bello poter vedere lì quali bug sono "conosciuti". –

risposta

5

Questo lavoro funzionerà?

NeedHalt := False; 
try 
    raise EExternal.Create('sdsdkfjh'); 
except 
    on E:EExternal do 
    begin 
    Writeln(E.Classname, ': ', E.Message); 
    NeedHalt := True; 
    end; 
end; 
if NeedHalt then 
    Halt(Exitcode); 

O questo?

try 
    raise EExternal.Create('sdsdkfjh'); 
except 
    on E:EExternal do 
    begin 
    Writeln(E.Classname, ': ', E.Message); 
    AcquireExceptionObject; 
    Halt(Exitcode); 
    end; 
end; 

In ogni caso: it's a bug in D2007, which was fixed in D2010.

+0

Grazie! +1 per 'AcquireExceptionObject' sta facendo il trucco per me come soluzione alternativa in D2007. E 'stato davvero un baco ... –

2

In realtà ... sembra funzionare come previsto ....

Ho usato il codice ...

program test1; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

var 
    Exitcode: Integer; 
begin 
    Writeln('Enter error code:'); 
    Readln(Exitcode); 
    try 
    raise EExternal.Create('sdsdkfjh'); 
    except 
    on E:EExternal do 
    begin 
     Writeln(E.Classname, ': ', E.Message); 
     Halt(Exitcode); 
    end; 
    end; 
end. 

compilata in in Delphi 5, poi corse in una finestra DOS sotto XP ...

C:\>test1 
Enter error code: 
111 
EExternal: sdsdkfjh 

C:\>echo %errorlevel% 
111 

C:\> 

Nota che DOS livelli di errore sono limitati alla gamma da 0 a 65535. Facendo eco% errorlevel% è il modo più veloce per vedere il livello di errore.

Non dimenticare che la lettura del errorlevel lo cancella.

+1

Non funziona più in D2007! Ma grazie per aver confermato che funzionava! Ero praticamente sicuro di averlo fatto in quel modo prima .. ;-) –

+1

E funziona anche in D2010. Lo stesso codice esatto, lo stesso esatto modo di test errorlevel mi dà quello che voglio in D2010 come per te con D5, ma con D2007, ottengo sempre 1! –

2

Se si desidera interrompere immediatamente il programma senza alcuna pulizia e restituire un codice di uscita, provare a ExitProcess. Vedere l'articolo per alcuni avvertimenti sull'utilizzo di ExitProcess, però.

+0

+1 per ExitProcess. È un po 'troppo duro per il mio caso attuale, ma vale la pena di essere ricordato. –

-1

Se la funzione di gestione delle eccezioni built-in non fa quello che ti piace, allora sostituirlo con il proprio:

function ExitCodeExceptHandler(ExceptObject: TObject; ExceptAddr: Pointer); 
begin 
    ShowException(ExceptObject, ExceptAddr); 
    if ExitCode = 0 then 
    ExitCode := 1; 
    Halt(ExitCode); 
end; 

Assegna che alla variabile globale System.ExceptProc quando il programma si avvia:

ExceptProc := @ExitCodeExceptHandler; 

L'ho implementato per utilizzare la variabile globale ExitCode. Se è ancora al suo valore predefinito di 0, allora la funzione ritorna al comportamento originale di Delphi di uscire con 1, ma se il codice di uscita è già stato impostato su qualcos'altro, allora questo si fermerà con quel valore. La prima cosa che fa Halt è la variabile globale ExitCode, quindi il tuo codice non dovrebbe richiedere ulteriori modifiche (anche se sceglierei un nome diverso per la variabile Exitcode). La vostra chiamata a Halt imposterà la variabile globale ExitCode e quindi procederà a chiudere il programma. Il gestore delle eccezioni noterà che ExitCode è già impostato e richiamare Halt con quel valore anziché 1.

+5

Sfortunatamente, non funziona. Innanzitutto, dovrebbe essere una procedura, non una funzione, ma più importante quando si arriva in 'DoneExceptions', il codice reimposta' ExceptProc: = nil' quindi chiama direttamente 'if (ExceptObject <> nil) e non (ExceptObject è EAbort) quindi ExceptHandler (ExceptObject, ExceptAddr); ' –

0

Utilizzando battuta d'arresto (I) genera perdite di memoria (si può vedere che se è stata attivata la MemoryLeaks FastMM con ReportMemoryLeaksOnShutdown: = true;)

è molto meglio usare un Exit "Clean" e impostare ExitCode prima di uscire .

In una sezione principale di una console app per esempio:

ExitCode:=I; 
exit;