关于 CTRL_SHUTDOWN_EVENT 在 DLL 和 WM_QUERYENDSESSION 中处理的混淆

Confusion about CTRL_SHUTDOWN_EVENT handling in DLLs and WM_QUERYENDSESSION

我的 UI 在 DLL 中。现在,DLL 和使用它的 EXE 都被编译为控制台程序,因此我可以在开发过程中使用 stdout 和 stderr 进行调试和错误报告。其中一件事是我有一个 uninit() 函数来确保 DLL 没有泄漏内存。

因此,我通过 DLL 设置了一个控制处理程序,这样 CTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT 模拟用户从文件菜单:它 PostQuitMessage(0),清理代码发生在消息泵 returns.

之后

我知道通常 CTRL_SHUTDOWN_EVENT 不能被忽略,并且程序 在处理程序例程 return 之后终止,无论它是什么 returns。但是根据MSDN,

Note that a third-party library or DLL can install a console control handler for your application. If it does, this handler overrides the default handler, and can cause the application to exit when the user logs off.

如果我没看错的话,这表示 DLL 安装的控制处理程序覆盖了导致我的程序在处理程序函数 returns 时退出的处理程序。我错了吗?我的 DLL 的处理程序函数只是 returns TRUE,我认为这将进一步停止 运行 中的任何其他默认值,鉴于上面的简介。

为什么?我注意到奇怪的行为:

在 Windows Vista 上,无论我做什么,程序都会关闭。在这种情况下,我想知道 blurb 是否有误,终止进程的处理程序仍然是 运行。无论我是否调用 ShutdownBlockReasonCreate().

都会发生这种情况

在 Windows 7 上,但是,似乎我的程序的主要 window 得到一个 WM_QUERYENDSESSION,并且 Windows 相应地响应它。这意味着如果我在我的 Quit 函数中说 "no, don't quit yet (don't call PostQuitMessage(0))",Windows 会弹出 "an application is preventing shutdown" 屏幕,说我的主 window 正在阻止关机。在那种情况下,上面的简介似乎是正确的,因为程序没有从控制台处理程序 return 退出(如果它甚至被调用!)。

如果我改为说“是,调用 PostQuitMessage(0),程序会正常退出。但是,我在 stdout 和 stderr 上丢失了调试输出,所以我无法判断它是否真的正常退出. 将我的程序调用为

new.exe > out.txt 2> err.txt

on cmd.exe 产生两个空文件;我不知道为什么系统关闭时输出没有保存(谷歌搜索没有显示任何信息)。

所以有人可以帮助我解决困惑,以便我可以正确地实施这个(包括 ShutdownBlockReasonCreate())吗?谢谢。

当您从您注册的处理程序中 return TRUE 时,Windows 立即终止进程。当您 return FALSE 时,将调用前一个处理程序。最终这将是默认处理程序,它会立即终止进程。

所以你要做的不是return然后阻塞直到你开心为止。这需要与泵送消息循环的线程同步。您将使用一个事件,泵送线程可以在其消息循环之后调用 SetEvent(),并且您的处理程序可以在调用 PostQuitMessage() 之后调用 WaitForSingleEvent() 进行阻塞。

然而,这是一场线程竞赛,您的 UI 线程可能是由 main() 启动的,而 CRT 将在 main() returns 时终止程序。谁先到那是一个未知数。


感觉自己做错了什么?好吧,你是。控制台 window 并不是显示调试输出的好方法。不确定你为什么要这样做,但我知道你的工具链很不寻常,我永远无法编译你的任何代码片段 运行。正确的方法是 OutputDebugString()。该函数与您的调试器对话并让它显示文本。即使您的调试器无法显示此类文本,您仍然可以回退到 SysInternals 的 DebugView 实用程序。

您可能正在使用 printf() 并且不会喜欢修复所有调试语句,只需在 CRT 之前编写您自己的链接版本,使用 vprintf() 和 OutputDebugStringA()。