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