我在Windows Tcl 8.5应用程序中拦截Ctrl-C时遇到问题。我在我开发的扩展库中添加了一个控制台处理程序,但它并不总是有效。
如果某些Tcl代码正在执行,那么一切正常。但是如果应用程序正在等待用户输入,则按Ctrl-C会终止它。调用我的处理程序,但同时(在不同的线程中?)Tcl REPL调用Tcl_Exit
。这真的搞砸了一切。
据我所知,REPL调用Tcl_Exit
被调用,因为它错误地认为stdin
遇到EOF
。反过来,这是由于当按下Ctrl-C时,读取例程返回,并返回读取的字节数,即零。 REPL将此条件解释为EOF。
有没有简单的方法来解决这个问题?我知道我可以放弃Tcl内置通道并提供我自己的通道,但这对于这个简单的问题来说似乎有点过头了。
我尝试了twapi::set_console_control_handler
,但它似乎根本不起作用。按Ctrl-C始终终止应用程序,永远不会调用处理程序。
答案 0 :(得分:2)
SetConsoleCtrlHandler
的MSDN文档指出单独处理CTRL_C处理,但可以通过将控制台模式设置为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
的位置。希望这可以帮助您寻找解决方案。
答案 1 :(得分:2)
关于twapi :: set_console_control_handler,它要求事件循环运行才能生效。如果Tcl线程在100ms内没有响应,则处理Ctrl-C的线程将继续使用默认的OS提供的处理程序。如果没有收到响应,也许这应该更改为默认为不传递给OS处理程序。