2010-08-04 4 views
6

Обновление: Это похоже на D2007. Он работает в D2010, как и в старой версии.Как вернуть код ошибки с помощью Halt (n) из блока Exception с D2007?

Я хотел бы вернуть код выхода в зависимости от типа Exception пойманного в блоке Eception Handler как:

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. 

К сожалению, в D2007, вызывающего Halt (п) от блока исключений всегда возвращаюсь Код выхода 1, независимо от того, что вы передадите, чтобы остановить().

Видимо, потому что выход из обработчика исключений не вызывает финализац, который очищает незавершенные (не являющихся Abort) Исключения, вызывая SysUtils.ExceptHandler:

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

И независимо от того, что код выхода я хотел я понимаю, что Halt(1)!

Так что вопрос:
Как я могу просто вернуть нужный код Выход в зависимости от того, который был поднят Exception?

+0

Основываясь на комментарий ниже Майк, это верно, это действительно делает возвращение правильный ERRORCODE. Я подозреваю, что это метод, который вы используете для получения ErrorCode, который может работать не так, как ожидалось. – zz1433

+0

@ Aldo. Нет, это D2007. То же самое происходит по-разному с D2007 и D2010, где он вернулся, как я ожидал, и сообщил Майк. –

+0

Пожалуйста, напишите большой отчет в QC (http://qc.embarcadero.com/); хотя, вероятно, не будет обновления D2007, приятно видеть, какие ошибки «известны». –

ответ

5

Будет ли это работать?

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

Или это?

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

В любом случае: it's a bug in D2007, which was fixed in D2010.

+0

Спасибо! +1 для 'AcquireExceptionObject' делает трюк для меня как обходной путь в D2007. Это была хорошая ошибка ... –

2

... На самом деле, кажется, работает как задумано ....

Я использовал свой код ...

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. 

Составитель в в Delphi 5, а затем побежал в поле DOS под XP ...

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

C:\>echo %errorlevel% 
111 

C:\> 

Обратите внимание, что DOS Уровни ошибок ограничены в диапазоне от 0 до 65535. Вторя% ERRORLEVEL% это самый быстрый способ, чтобы увидеть уровень ошибок.

Не забывайте, что чтение уровня ошибки очищает его.

+1

Больше не работает в D2007! Но спасибо за подтверждение, что он работал! Я был почти уверен, что сделал это так раньше. ;-) –

+1

И он также работает в D2010. Тот же самый точный код, тот же самый точный способ тестирования errorlevel дает мне то, что я хочу в D2010, как для вас с D5, но с D2007 я всегда получаю 1! –

2

Если вы хотите немедленно прекратить выполнение программы без какой-либо очистки и вернуть код выхода, попробуйте ExitProcess. Тем не менее, см. Статью для нескольких предостережений об использовании ExitProcess.

+0

+1 для ExitProcess. Это слишком жестко для моего настоящего случая, но стоит помнить. –

-1

Если встроенная функция обработки исключений не делать то, что вам нравится, а затем заменить его своим:

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

Assign, что к глобальной переменной System.ExceptProc при запуске программы:

ExceptProc := @ExitCodeExceptHandler; 

Я использовал его для использования глобальной переменной ExitCode. Если значение по умолчанию равно 0, функция возвращается к исходному поведению Delphi при выходе с 1, но если код выхода уже установлен на что-то еще, тогда это остановится с этим значением. Первое, что Halt делает, устанавливает глобальную переменную ExitCode, поэтому ваш код не нуждается в дополнительных изменениях (хотя я бы выбрал другое имя для переменной Exitcode). В вашем вызове Halt будет установлена ​​глобальная переменная ExitCode, а затем приступить к завершению работы программы. Обработчик исключений заметит, что ExitCode уже установлен и перезвон Halt с этим значением вместо 1.

+5

К сожалению, это не работает. Во-первых, это должна быть процедура, а не функция, но более важная, когда вы приходите в «DoneExceptions», код сбрасывает «ExceptProc: = nil», а затем вызывает непосредственно 'if (ExceptObject <> nil), а не (ExceptObject - EAbort), тогда ExceptHandler (ExceptObject, ExceptAddr); ' –

0

Использование Halt (I) создает утечку памяти (вы можете увидеть, что если вы позволили MemoryLeaks FastMM с ReportMemoryLeaksOnShutdown: = True;)

Это гораздо лучше использовать «чистый» Выход и установить ExitCode перед выходом ,

В основной части консольного приложения, например:

ExitCode:=I; 
exit;