如何让GenerateConsoleCtrlEvent与cmd.exe一起使用

Kev*_*yth 5 windows winapi cmd console-application windows-console

我正在努力去GenerateConsoleCtrlEvent上班。这是我想出的最小示例:

#include <Windows.h>

static BOOL WINAPI handler(DWORD CtrlType)
{
    return TRUE;
}

int main()
{
    if (!SetConsoleCtrlHandler(&handler, TRUE))
    {
        throw 1;
    }

    STARTUPINFO si = {0};
    DWORD dwStartupFlags = 0;
    PROCESS_INFORMATION pi;
    if (!CreateProcess(
        NULL,
        "cmd.exe",
        NULL,
        NULL,
        FALSE,
        dwStartupFlags,
        NULL, // environ
        NULL, // cwd
        &si,
        &pi))
    {
        throw 1;
    }
    CloseHandle(pi.hThread);

    DWORD exitCode;
    while(true)
    {
        switch (WaitForSingleObject(pi.hProcess, 1000 * 10))
        {
          case WAIT_TIMEOUT:
            GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
            //WaitForSingleObject(pi.hProcess, INFINITE);
            break;

          case WAIT_OBJECT_0:
            GetExitCodeProcess(pi.hProcess, &exitCode);
            return exitCode;
        }
    }
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

所以我启动它,输入一些内容(没有换行符),然后等待 10 秒钟。调试器显示 Ctrl-C 事件。控制台显示 Ctrl-C 没有效果。

我追踪到这个问题:主 cmd.exe 线程调用ReadConsoleW. 在测试程序下GenerateConsoleCtrlEvent,这不会返回。按 Ctrl-C 确实会使其返回,并带有*lpNumberOfCharsRead==0.

GenerateConsoleCtrlEvent使用 启动一个新线程kernel32!CtrlRoutine。如果使用调试器冻结此线程,cmd.exe 的行为仍然就像按下 Ctrl-C 一样。事实上,如果您在按下返回键后设置返回时间,*lpNumberOfCharsRead==NULL则可以欺骗 cmd.exe 使其相信按下了 Ctrl-C 。ReadConsoleW

cmd.exe这种行为并不独特:Python.exe 的读取在按下 Ctrl-C 时立即返回,但在GenerateConsoleCtrlEvent. Python.exe 使用ReadFile. (按下 Enter 后,Python.exe 会注意到键盘中断。)

所以问题是:为什么ReadConsoleW按下 Ctrl-C 时立即返回,而不是GenerateConsoleCtrlEvent调用时立即返回?据我所知,在 Windows 7 上,按 Ctrl-C 会发送由与conhost.exe通信、与csrss.exe调用的消息读取的消息。当您按 Ctrl-C 时,我没有看到它执行任何其他操作。但是什么原因导致回归呢?NtCreateThreadExkernel32!CtrlRoutineReadConsoleW