【问题标题】:Tcl and Ctrl-C in Windows consoleWindows 控制台中的 Tcl 和 Ctrl-C
【发布时间】:2015-01-12 13:34:27
【问题描述】:

在我的 Windows Tcl 8.5 应用程序中拦截 Ctrl-C 时遇到问题。我在我开发的扩展库中添加了一个控制台处理程序,但它并不总是有效。

如果某些 Tcl 代码正在执行,那么一切正常。但如果应用程序正在等待用户输入,按 Ctrl-C 会终止它。我的处理程序被调用,但同时(在不同的线程中?)Tcl REPL 调用Tcl_Exit。这真的把一切都搞砸了。

据我所知,REPL 调用Tcl_Exit 是因为它错误地认为stdin 遇到了EOF。反过来,这是由于当按下 Ctrl-C 时,读取例程返回,它返回读取的字节数,即 0。 REPL 将此条件解释为 EOF。

有没有简单的方法来解决这个问题?我知道我可以放弃 Tcl 内置通道并提供自己的通道,但这对于这个简单的问题来说似乎有点过头了。

我已经尝试过twapi::set_console_control_handler,但它似乎根本不起作用。按 Ctrl-C 总是会终止应用程序,并且永远不会调用处理程序。

【问题讨论】:

  • 我尝试了一个快速的 critcl 内置扩展来调用 SetConsoleCtrlHandler 并获得相同的效果。处理函数被调用,但 tclsh 仍然退出。我怀疑在 twapi 版本中它会引发一个事件来运行脚本并在处理事件之前退出,因此无法调用脚本。 C 处理程序可能正在被调用。

标签: windows tcl interrupt windows-console


【解决方案1】:

SetConsoleCtrlHandler 的 MSDN 文档指出 CTRL_C 处理是单独处理的,但可以通过将控制台模式设置为 ENABLE_PROCESSED_INPUT 来禁用它。然后将 Ctrl-C 事件报告为键盘输入。

以下 critcl 代码加载到解释器中(使用 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 的位置。希望这可以帮助您寻找解决方案。

【讨论】:

  • 看起来设置 ENABLE_PROCESSED_INPUT 就像在 Unix 终端上设置 raw/noecho 模式。使用计时器事件在几微秒内重置它可能就足够了吗?我真的不知道……
【解决方案2】:

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

【讨论】:

  • 添加了一个 RFE 来改变 Tcl core.tcl.tk/tcl/tktview/…
  • 更新了 Tcl 主干(8.6.4 之后),不将 Ctrl-C 视为控制台上的 EOF。
  • 非常感谢。我刚刚下载了最新版本,Ctrl-C 不再立即杀死我的程序。
猜你喜欢
  • 2020-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
  • 2017-02-03
  • 2011-05-22
  • 1970-01-01
相关资源
最近更新 更多