Как вернуть код ошибки с Halt (n) из блока исключений с D2007? - PullRequest
6 голосов
/ 04 августа 2010

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

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

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

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

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

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

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

Ответы [ 5 ]

5 голосов
/ 05 августа 2010

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

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 .

2 голосов
/ 05 августа 2010

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

2 голосов
/ 05 августа 2010

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

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

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

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

0 голосов
/ 04 марта 2016

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

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

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

ExitCode:=I;
exit;
0 голосов
/ 05 августа 2010

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

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

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

ExceptProc := @ExitCodeExceptHandler;

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

...