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

时间:2021-06-09 20:43:30

Are there best practices and code snippets available which show how I can handle Ctrl+C in a Delphi console application?

是否有可用的最佳实践和代码片段,说明如何在Delphi控制台应用程序中处理Ctrl + C?

I have found some articles which give some information about possible problems with the debugger, with exception handling, unloading of DLLs, closing of stdin, and finalization for example this CodeGear forums thread.

我找到了一些文章,它们提供了一些关于调试器可能存在的问题的信息,包括异常处理,DLL的卸载,stdin的关闭,以及例如这个CodeGear论坛线程的最终化。

2 个解决方案

#1


From Windows API (MSDN):

从Windows API(MSDN):

BOOL WINAPI SetConsoleCtrlHandler(
    PHANDLER_ROUTINE HandlerRoutine,    // address of handler function  
    BOOL Add    // handler to add or remove 
   );   

A HandlerRoutine function is a function that a console process specifies to handle control signals received by the process. The function can have any name.

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

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

In the Delphi the handler routine should be like:

在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;

#2


I wrote a little program to show you how to stop properly a background task. Hopefully it is clearer.

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

Content of ThreadConsoleApplication.dpr file :

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.

#1


From Windows API (MSDN):

从Windows API(MSDN):

BOOL WINAPI SetConsoleCtrlHandler(
    PHANDLER_ROUTINE HandlerRoutine,    // address of handler function  
    BOOL Add    // handler to add or remove 
   );   

A HandlerRoutine function is a function that a console process specifies to handle control signals received by the process. The function can have any name.

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

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

In the Delphi the handler routine should be like:

在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;

#2


I wrote a little program to show you how to stop properly a background task. Hopefully it is clearer.

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

Content of ThreadConsoleApplication.dpr file :

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.