как устранить посторонний консольный вывод после ctrl-c в Windows?

#windows #delphi #winapi #windows-console

#Windows #delphi #winapi #windows-консоль

Вопрос:

При нажатии Ctrl-c почти всегда появляется дополнительный вывод. Я хотел бы убедиться, что после получения Ctrl-c программа не показывает ничего, кроме, возможно, «^ C».

Я обнаружил, что в основном это тот же вопрос, но это было для Linux, и мои попытки «перенести» решение из Linux в Windows не увенчались успехом.

На данный момент у меня закончились попытки, и я могу воспользоваться некоторой помощью, которую я определенно ценю. Спасибо.

Приведенный ниже краткий пример программы страдает от этой проблемы.

 {$APPTYPE        CONSOLE}

program _SetConsoleCtrlHandler;

uses
  Windows,
  SysUtils
  ;

function CtrlHandler(CtrlType : DWORD) : BOOL; stdcall;
begin
  result := FALSE;

  case CtrlType of
    CTRL_C_EVENT,
    CTRL_BREAK_EVENT:
    begin
      result := TRUE;

      ExitProcess(7);
    end;
  end;
end;

var
  s : shortstring;

begin
  SetConsoleCtrlHandler(@CtrlHandler, TRUE);

  while TRUE do
  begin
    write('press <ctrl-c> to end this program : ');
    readln(s);
  end;
end.
  

Комментарии:

1. Помимо моего ответа ниже, вы не должны выполнять цикл после readln. Вы либо создаете однопоточное приложение, которое выполняется сверху вниз, пока вы не обнаружите Ctrl-C и немедленно завершите работу, либо приложение, в котором вся остальная работа выполняется в потоке, и вы ожидаете Ctrl-C в главном потоке приложения.

2. CtrlHandler запускается в другом потоке

3. Ваша идентификация проблемы, похоже, неверна. Например. очистите свой цикл ( while true do; ), затем нажмите ctrl c и посмотрите, есть ли какой-либо посторонний вывод.

4. @ScienceAmatuer Это не совсем надежный подход, потому что вы не можете гарантировать синхронизацию сигнала о том, что была нажата Ctrl-C. Как я сказал ниже, вам действительно нужно структурировать процесс выхода так, чтобы он ожидал нажатия ctrl-c.

5. @ Allen, приложение не может ожидать ctrl-c. Он должен обрабатывать пользовательский ввод, и для этого он должен отображать соответствующее приглашение и читать все, что пользователь ввел.

Ответ №1:

Обычно я делаю это так, чтобы у меня был отдельный блок, который сигнализируется, и простое ожидание, как показано ниже. В главном консольном проекте вы вызываете WaitForCtrlC вместо Readln(). Вы также могли бы использовать TEvent и ожидать события вместо цикла, как я показываю в этом примере:

 uses
  {$IFDEF LINUX}
  Posix.Signal,
  {$ENDIF}
  {$IFDEF MSWINDOWS}
  Windows,
  {$ENDIF}
  SysUtils;

procedure WaitForCtrlC;

implementation

var
  Control_C: Boolean = False;

{$IFDEF MSWINDOWS}
function ConsoleCtrlHandler(dwCtrlType: DWORD): BOOL; stdcall;
begin
  if (dwCtrlType = CTRL_C_EVENT) then
    Control_C := True;
  Result := True;
end;
{$ENDIF}

{$IFDEF LINUX}
var
  sigIntHandler: sigaction_t;

procedure SigHandler(SigNum: Integer); cdecl;
begin
  Control_C := True;
end;
{$ENDIF}

procedure WaitForCtrlC;
begin
  while not Control_C do
    Sleep(25);
end;

initialization
  {$IFDEF MSWINDOWS}
  Windows.SetConsoleCtrlHandler(@ConsoleCtrlHandler, True);
  {$ENDIF}
  {$IFDEF LINUX}
  sigIntHandler._u.sa_handler := @SigHandler;
  sigemptyset(sigIntHandler.sa_mask);
  sigIntHandler.sa_flags := 0;
  sigaction(SIGINT, @sigIntHandler, nil);
  {$ENDIF}
  

Комментарии:

1. @ Allen, спасибо за вашу помощь. Проблема не в readln, добавление Sleep(5) — или даже Sleep (1) — после readln решает проблему в примере кода, который я опубликовал. Кажется, что после нажатия ctrl-c возникает состояние гонки. Без режима ожидания (xx) цикл имеет достаточно времени для повторного выполнения до того, как будет выполнен вызов ExitProcess . К вашему сведению, я проголосовал за ваш ответ, но я не буду проверять его как ответ, потому что реальный ответ заключается в том, чтобы каким-то образом (обычно с вызовом sleep) дать обработчику управления достаточно времени для выполнения вызова ExitProcess / TerminateProcess ..

2. Я думаю, что главное здесь в том, что вам не следует использовать Readln (), если вы намерены завершить работу своего приложения с помощью Ctrl-C. Если вы сделаете это, дождавшись Ctrl-C, проблема, которую вы описываете, исчезнет.

3. @ Allen, readln необходим. В типичном интерпретаторе командной строки есть комбинация write <приглашение>, за которой следует readln <команда>. Нет ничего плохого в использовании writeln и readln. Проблема заключается в том, чтобы гарантировать, что обработчик ctrl-c всегда выигрывает гонку выполнения, вводя задержку в цикл.

4. @ScienceAmateur Ни в вашем вопросе, ни в вашем примере это намерение вообще не обсуждается. Ваш пример показывает, что вы хотите выйти из своей программы только при нажатии Ctlr-C.

5. @ Allen, я сделал пример максимально простым в иллюстративных целях. Возможно, я сделал это слишком просто.