Как вернуть код ошибки с Halt (n) из блока исключений с D2007?

обновление: похоже, это специфично для D2007. Он работает в D2010, как и в более старой версии.

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

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 (n) из блока исключений всегда возвращает код выхода 1 неважно, что вы проходите, чтобы остановить().

видимо, потому что выход из Обработчик исключений вызывает Finalize, который очищает ожидающие (не прерываемые) исключения, вызывая SysUtils.ExceptHandler:

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

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

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

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;

иначе: это ошибка в D2007, которая была исправлена в D2010.


на самом деле... кажется, это работает по назначению....

я использовал ваш код...

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% - самый быстрый способ увидеть уровень ошибки.

Не забывайте, что чтение errorlevel очищает его.


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


использование halt (I) создает утечки памяти (вы можете видеть, что если вы включили Fastmm MemoryLeaks с ReportMemoryLeaksOnShutdown:=true;)

гораздо лучше использовать "чистый" выход и установить ExitCode до выхода.

в главном разделе консольного приложения, например:

ExitCode:=I;
exit;

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

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

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

ExceptProc := @ExitCodeExceptHandler;

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