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 stdinfconfigure stdout的地方。希望这可以帮助您寻找解决方案。

关于twapi::set_console_control_handler,需要事件循环运行才有效。如果 Tcl 线程在 100 毫秒内没有响应,则处理 Ctrl-C 的线程将继续使用默认 OS 提供的处理程序。如果没有收到响应,也许这应该更改为默认不传递给 OS 处理程序。