I have a problem intercepting Ctrl-C in my Windows Tcl 8.5 application. I have added a console handler in an extension library I develop, but it's not always working.
If some Tcl code is executing, then everything is OK. But if the application is waiting for user input, pressing Ctrl-C terminates it. My handler is called, but at the same time (in a different thread?) the Tcl REPL calls Tcl_Exit
. This really messes up everything.
As far as I can tell, REPL calls Tcl_Exit
is called because it erroneously thinks that stdin
has encountered EOF
. This, in turn, is caused by the fact that when Ctrl-C is pressed, the read routine returns, and it returns the number of bytes read, which is zero. REPL interprets this condition as EOF.
Is there an easy way to fix this problem? I know I could ditch the Tcl built-in channel and supply my own, but this seems an overkill for this simple problem.
I have tried twapi::set_console_control_handler
but it doesn't seem to work at all. Pressing Ctrl-C always terminates the application, and the handler is never called.
The MSDN documentation for SetConsoleCtrlHandler
points out that CTRL_C handling is handled separately but this can be disabled by setting the console mode to ENABLE_PROCESSED_INPUT
. This then reports Ctrl-C events as keyboard input.
The following critcl code loaded into an interpreter (using load ctrl_c.dll ctrl_c; win32::SetCtrlHandler
lets me intercept the Control-C keyboard input without exiting:
package require critcl
namespace eval win32 {
critcl::ccode {
#define STRICT
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0502
#include <windows.h>
BOOL CtrlHandler(DWORD dwEvent)
{
switch (dwEvent)
{
case CTRL_C_EVENT:
fprintf(stderr, "ctrl_c\n");
return TRUE;
default:
return FALSE;
}
}
}
# Quick and dirty test CTRL_C interception in windows.
critcl::cproc SetCtrlHandler {} ok {
BOOL b = SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
if (b)
b = SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
return b ? TCL_OK : TCL_ERROR;
}
}
Compiled using critcl -lib ctrl_c.tcl
.
However! Once a Ctrl-C has been seen, the console input no longer echos anything typed by the user. It does read the input and acts upon it but does not echo that input. As an example session:
% load ctrl_c.dll ctrl_c
% win32::SetCtrlHandler
% ctrl_c
8.6.1
% -blocking 1 -buffering line -buffersize 4096 -encoding unicode -eofchar → -translation auto
% -blocking 1 -buffering line -buffersize 4096 -encoding unicode -eofchar {} -translation crlf
%
What is not shown is where I entered fconfigure stdin
and fconfigure stdout
. Hopefully this can assist your search for a solution.