#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, я сделал пример максимально простым в иллюстративных целях. Возможно, я сделал это слишком просто.