我在Windows Tcl 8.5应用程序中拦截了Ctrl-C的问题。 我在我开发的扩展库中添加了一个控制台处理程序,但并不总是有效。
如果一些Tcl代码正在执行,那么一切正常。 但是,如果应用程序正在等待用户input,请按Ctrl-C终止它。 我的处理程序被调用,但在同一时间(在不同的线程?)Tcl REPL调用Tcl_Exit
。 这真的弄糟了一切。
据我所知,REPL调用Tcl_Exit
被调用,因为它错误地认为stdin
已经遇到EOF
。 这反过来又是由于当按下Ctrl-C时,读取例程返回,并返回读取的字节数为零。 REPL将此条件解释为EOF。
有没有简单的方法来解决这个问题? 我知道我可以抛弃Tcl内置通道并提供我自己的通道,但对于这个简单的问题,这似乎是一个矫枉过正。
我已经尝试twapi::set_console_control_handler
但它似乎并没有工作。 按Ctrl-C总是终止应用程序,处理程序永远不会被调用。
SetConsoleCtrlHandler
的MSDN文档指出,CTRL_C处理是分开处理的,但可以通过将控制台模式设置为ENABLE_PROCESSED_INPUT
来ENABLE_PROCESSED_INPUT
。 然后这将Ctrl-C事件报告为键盘输入。
下面的批处理代码加载到解释器(使用load ctrl_c.dll ctrl_c; win32::SetCtrlHandler
让我拦截Control-C键盘输入,而不退出:
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; } }
编译使用critcl -lib ctrl_c.tcl
。
然而! 一旦看到Ctrl-C,控制台输入不再回显用户输入的任何东西。 它确实读取输入并对其执行操作,但不会回应该输入。 作为例子会议:
% 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 %
没有显示的是我输入fconfigure stdin
和fconfigure stdout
。 希望这可以帮助您寻找解决方案。
关于twapi :: set_console_control_handler,它需要事件循环运行才能生效。 如果Tcl线程在100ms内没有响应,处理Ctrl-C的线程将继续使用默认的OS提供的处理程序。 如果没有收到响应,也许这应该改变为默认不传递给操作系统处理程序。