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.