【问题标题】:Why does my Windows Console Close Event Handler time out?为什么我的 Windows 控制台关闭事件处理程序超时?
【发布时间】:2017-10-31 17:37:45
【问题描述】:

我在 VS2017/Windows 10 中构建了以下程序。当我运行它时,我点击关闭并按预期调用 ctrl_handler(),但在大约三秒后,该进程无论如何都会被强制终止。

这是一个问题,因为我的实际应用程序写入了大型日志文件,而三秒不足以将它们写入磁盘。

描述此行为的文档在哪里?它不在those for the CTRL+CLOSE signal 中。

超时设置在哪里?是否可以在应用程序级别进行修改?还是使用组策略?

#include <Windows.h>

bool mainThreadRunning;
bool mainThreadFinished;

BOOL ctrl_handler(DWORD event)
{
    if (event == CTRL_CLOSE_EVENT) {
        mainThreadRunning = false;
        while (!mainThreadFinished) {
            Sleep(100);
        }
        return TRUE;
    }
    return FALSE;
}

int main()
{
    mainThreadRunning = true;
    mainThreadFinished = false;

    SetConsoleCtrlHandler((PHANDLER_ROUTINE)(ctrl_handler), TRUE);  // make sure when the user hits the close button in the console we shut down cleanly

    while (true)
    {

    }

    return 0;
}

【问题讨论】:

  • 为了发送控制事件,控制台主机(conhost.exe)请求会话服务器(csrss.exe)在客户端创建一个控制线程。该线程从kernelbase!CtrlRoutine 开始并调用注册的处理程序,直到处理程序返回TRUE 或者它到达调用ExitProcess(STATUS_CONTROL_C_EXIT) 的默认处理程序(kernelbase!DefaultHandler)。 Vista 和更高版本不允许处理程序通过为关闭事件返回 true 来保持进程(和控制台)处于活动状态。相反,会话服务器等待 5 秒,如果进程没有退出,则终止进程。

标签: c++ windows winapi console-application


【解决方案1】:

我猜你在找this is the reference

不幸的是,这是由操作系统决定的。 HandlerRoutine Callback 文档中有描述该行为的文档:

" 在这种情况下,不会调用其他处理函数,系统会弹出一个对话框,询问用户是否终止进程。如果进程在一段时间内没有响应,系统也会显示此对话框。一定的超时时间(CTRL_CLOSE_EVENT 为 5 秒,CTRL_LOGOFF_EVENT 或 CTRL_SHUTDOWN_EVENT 为 20 秒)。"

没有(至少公开的、记录在案的)API 可以更改此超时。

注意:

进程可以使用SetProcessShutdownParameters 函数来防止系统在注销或关闭期间向用户显示对话框。在这种情况下,系统会在 HandlerRoutine 返回 TRUE 或超时时间结束时终止进程

如果操作系统认为处理程序花费了太多时间才能完成,它会故意强制终止。

以下来自 cmets 的重要说明:

... Ctrl+C 不受超时的影响(我已经测试过了,这就是我现在正在使用的)。

【讨论】:

  • 关闭事件的弹出对话框是 Vista 之前的行为,其中应用程序可以通过为关闭事件返回 true 来保持控制台处于活动状态。这在 Vista 中被消除了。当用户关闭窗口(或优雅地终止控制台报告为其有效所有者的进程,它将WM_CLOSE 发送到控制台窗口),此时连接到控制台的所有进程都可以退出或终止。没有人就此事获得投票权。进程甚至无法从控制台分离以保持活动状态,因为会话服务器已经将其标记为死亡。
  • 谢谢,这似乎是 MS 想要对这个主题说的所有内容!我喜欢(不)线程如何引用 MS 已从文档中删除的超时,就好像他们试图混淆它一样。 ~ 出于利益考虑,由于它写得模棱两可,CTRL_C 不受超时的影响(我已经测试过了,这就是我现在正在使用的)。
  • @sebf,通常也使用 Ctrl+Break,因为它不能被忽略,不像 Ctrl+C。例如,一个程序可以调用GenerateConsoleCtrlEvent 向连接到控制台的所有进程(包括它自己)或使用CreateProcess 标志CREATE_NEW_PROCESS_GROUP(CMD 的start /B 使用此标志)。这可以优雅地杀死一个进程组。但是组的领导进程是在忽略 Ctrl+C 的情况下创建的。它可以通过SetConsoleCtrlHandler(NULL, False) 手动重新启用它,但这不是调用者可以控制的。
猜你喜欢
  • 2014-12-26
  • 2015-05-27
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
  • 2015-08-29
  • 2013-10-11
  • 2012-07-02
  • 1970-01-01
相关资源
最近更新 更多