如何处理Ctrl+;Delphi控制台应用程序中的C?

如何处理Ctrl+;Delphi控制台应用程序中的C?,delphi,console,Delphi,Console,是否有最佳实践和代码片段可以展示如何在Delphi控制台应用程序中处理Ctrl+C 我找到了一些文章,其中提供了有关调试器可能出现的问题的一些信息,包括异常处理、DLL卸载、stdin关闭和最终确定。来自Windows API(): HandlerRoutine函数是控制台进程指定用于处理进程接收的控制信号的函数。函数可以有任何名称 BOOL WINAPI HandlerRoutine( DWORD dwCtrlType // control signal type );

是否有最佳实践和代码片段可以展示如何在Delphi控制台应用程序中处理Ctrl+C

我找到了一些文章,其中提供了有关调试器可能出现的问题的一些信息,包括异常处理、DLL卸载、stdin关闭和最终确定。

来自Windows API():

HandlerRoutine函数是控制台进程指定用于处理进程接收的控制信号的函数。函数可以有任何名称

BOOL WINAPI HandlerRoutine(
    DWORD dwCtrlType    //  control signal type
   );   

在Delphi中,处理程序例程应该如下所示:

function console_handler( dwCtrlType: DWORD ): BOOL; stdcall;
begin
  // Avoid terminating with Ctrl+C
  if (  CTRL_C_EVENT = dwCtrlType  ) then
    result := TRUE
  else
    result := FALSE;
end;

我编写了一个小程序,向您展示如何正确停止后台任务。 希望它更清楚

ThreadConsoleApplication.dpr文件的内容:

program ThreadConsoleApplication;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  Classes;

type
  { **
    * Classe TQueueReaderTestApplication
    * }
  TThreadConsoleApplication = class(TThread)
  public
    procedure Execute; override;

    constructor Create; virtual;
    destructor Destroy; override;

    class function getInstance: TThreadConsoleApplication;
  end;

function TThreadConsoleApplication_consoleCtrlHandler(dwCtrlType: DWORD): BOOL;
  stdcall; forward;

{ **
  * Classe TQueueReaderTestApplication
  * }

constructor TThreadConsoleApplication.Create;
begin
  inherited Create(True { CreateSuspended } );
  Windows.setConsoleCtrlHandler(@TThreadConsoleApplication_consoleCtrlHandler,
    True { add } );
end;

var
  threadConsoleApplicationInstance: TThreadConsoleApplication = nil;

destructor TThreadConsoleApplication.Destroy;
begin
  threadConsoleApplicationInstance := nil;
  inherited;
end;

procedure TThreadConsoleApplication.Execute;
begin
  System.Writeln('[TThreadConsoleApplication.Execute] begin');
  try
    while not Terminated do
    begin
      System.Writeln('[TThreadConsoleApplication.Execute] running ...');
      Windows.Sleep(1000 { dwMilliseconds } );
    end;
  finally
    System.Writeln('[TThreadConsoleApplication.Execute] end');
  end;
end;

class function TThreadConsoleApplication.getInstance: TThreadConsoleApplication;
begin
  if nil = threadConsoleApplicationInstance then
  begin
    threadConsoleApplicationInstance := TThreadConsoleApplication.Create;
  end;
  Result := threadConsoleApplicationInstance;
end;

function TThreadConsoleApplication_consoleCtrlHandler(dwCtrlType: DWORD): BOOL;
begin
  Result := False;
  if Windows.CTRL_C_EVENT = dwCtrlType then
  begin
    TThreadConsoleApplication.getInstance.Terminate;
    Result := True;
  end;
end;

var
  thread: TThread;

begin
  System.Writeln('[program] begin');
  try
    thread := nil;
    try
      thread := TThreadConsoleApplication.getInstance;
      thread.Resume;
      System.Writeln('[program] press a CTRL+C to stop running');
      thread.WaitFor;
    finally
      thread.Free;
    end;
    System.Writeln('[program] end');
  except
    on E: Exception do
    begin
      System.Writeln(System.ErrOutput, '[program] end with error');
      System.Writeln(System.ErrOutput, E.ClassName, ': ', E.Message);
    end;
  end;
  System.Writeln('[program] press a key to quit');
  System.Readln;

end.

好的,这显示了如何禁用Ctrl+C-我应该澄清我的问题:在检测到Ctrl+C后,如何执行干净的应用程序关闭?哦,我明白了。如我的例子所示,如果您捕获Ctrl+C,您可以设置一种“标志”,并在需要时正常终止。我刚刚想起了“break:=false;”,我曾将其放入我的Turbo Pascal程序中以禁用Ctrl-break。啊,怀旧……要记住一件非常重要的事情,那就是HandlerRoutine在一个单独的线程中运行。如果您自己不启动任何其他线程,请将IsMultithread全局变量设置为True,以便您执行的任何内存操作都是安全的。调用约定也很重要。回调函数必须使用stdcall调用约定,否则程序将神秘崩溃。
program ThreadConsoleApplication;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  Classes;

type
  { **
    * Classe TQueueReaderTestApplication
    * }
  TThreadConsoleApplication = class(TThread)
  public
    procedure Execute; override;

    constructor Create; virtual;
    destructor Destroy; override;

    class function getInstance: TThreadConsoleApplication;
  end;

function TThreadConsoleApplication_consoleCtrlHandler(dwCtrlType: DWORD): BOOL;
  stdcall; forward;

{ **
  * Classe TQueueReaderTestApplication
  * }

constructor TThreadConsoleApplication.Create;
begin
  inherited Create(True { CreateSuspended } );
  Windows.setConsoleCtrlHandler(@TThreadConsoleApplication_consoleCtrlHandler,
    True { add } );
end;

var
  threadConsoleApplicationInstance: TThreadConsoleApplication = nil;

destructor TThreadConsoleApplication.Destroy;
begin
  threadConsoleApplicationInstance := nil;
  inherited;
end;

procedure TThreadConsoleApplication.Execute;
begin
  System.Writeln('[TThreadConsoleApplication.Execute] begin');
  try
    while not Terminated do
    begin
      System.Writeln('[TThreadConsoleApplication.Execute] running ...');
      Windows.Sleep(1000 { dwMilliseconds } );
    end;
  finally
    System.Writeln('[TThreadConsoleApplication.Execute] end');
  end;
end;

class function TThreadConsoleApplication.getInstance: TThreadConsoleApplication;
begin
  if nil = threadConsoleApplicationInstance then
  begin
    threadConsoleApplicationInstance := TThreadConsoleApplication.Create;
  end;
  Result := threadConsoleApplicationInstance;
end;

function TThreadConsoleApplication_consoleCtrlHandler(dwCtrlType: DWORD): BOOL;
begin
  Result := False;
  if Windows.CTRL_C_EVENT = dwCtrlType then
  begin
    TThreadConsoleApplication.getInstance.Terminate;
    Result := True;
  end;
end;

var
  thread: TThread;

begin
  System.Writeln('[program] begin');
  try
    thread := nil;
    try
      thread := TThreadConsoleApplication.getInstance;
      thread.Resume;
      System.Writeln('[program] press a CTRL+C to stop running');
      thread.WaitFor;
    finally
      thread.Free;
    end;
    System.Writeln('[program] end');
  except
    on E: Exception do
    begin
      System.Writeln(System.ErrOutput, '[program] end with error');
      System.Writeln(System.ErrOutput, E.ClassName, ': ', E.Message);
    end;
  end;
  System.Writeln('[program] press a key to quit');
  System.Readln;

end.