【问题标题】:How to prevent a system shutdown without having a window?如何在没有窗口的情况下防止系统关闭?
【发布时间】:2017-12-12 08:02:15
【问题描述】:

我有这个源代码的windows应用程序

#include <Windows.h>
#include <thread>
#include <chrono>

int WinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       cmdShow)
{
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch (Message)
    {
        case WM_QUERYENDSESSION:
            MessageBox(NULL, "Triggered?", "Message", 0);
            AbortSystemShutdown(NULL);
            return 0;

        default:
            return DefWindowProc(hWnd, Message, wParam, lParam);
    }

    return 0;
}

我需要知道系统何时关闭并阻止它,或者至少向用户发送消息。

我的应用程序似乎没有收到WM_QUERYENDSESSION 消息。

我也尝试使用ShutdownBlockReasonCreate(),但我没有HWND 作为窗口。

我应该怎么做?

【问题讨论】:

  • 习惯上在尝试为它处理消息之前创建一个窗口。
  • 一个“窗口过程”(或“winproc”)连接到一个窗口类。如果您没有窗口或窗口类,那么事件循环就没有什么可做的了。名为WinProc 的函数没有什么特别之处,Windows 不会自动调用它。
  • WM_QUERYENDSESSION 的文档不清楚该消息是发送到所有顶级窗口(这很荒谬,但并非不可想象)还是任务消息。也就是说,如果 Microsoft 的文档是可靠的,那么可以从“由 windows proc 处理”推断它是发送到窗口的。但是文档中充满了此类技术作家的许可声明,其中大部分都是错误的(就像许多高度赞成的 SO 答案一样)。如果它是一个任务消息,那么只需将处理移至您的消息循环。但如果它被发送到顶级寡妇,然后创建一个顶级窗口。
  • 您为什么要这样做?这听起来像是个坏主意™。
  • @Cheersandhth.-Alf 你大概是指 thread 消息?实际上,如果您考虑WM_QUERYENDSESSION 具有返回值这一事实,则并不清楚。 A thread message can only be posted,因此它无法将值返回给它的发送者。这只给我们留下了始终与窗口相关联的SendMessage* 系列函数。

标签: c++ winapi shutdown


【解决方案1】:

正如WM_QUERYENDSESSION 的参考文献中所述:

窗口通过其 WindowProc 函数接收此消息。

您有一个 WindowProc,但缺少一个窗口。 WindowProc 必须与窗口相关联,否则 Windows 不知道它。要将 WindowProc 与窗口关联,您可以调用 RegisterClassEx,然后调用 CreateWindowEx。在对CreateWindowEx 的调用中指定新创建的窗口类的名称。

窗口必须是顶级窗口。它可以是不可见的,但在这种情况下适用以下情况(来自Application Shutdown Changes in Windows Vista):

还要注意,如果您的应用程序没有可见的顶级窗口, 如果需要成功阻止关机,它必须使用此 API [ShutdownBlockReasonCreate()]。这样的 如果阻止关闭,应用程序将自动终止 不使用 API。

注意message-only window will not receive WM_QUERYENDSESSION

工作示例:

#include <windows.h>

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

int APIENTRY wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                       LPWSTR lpCmdLine, int nCmdShow )
{
    WNDCLASSEXW wx = { sizeof(wx) }; // set cbSize member and zero-initialize all other
    wx.lpfnWndProc = WndProc;
    wx.hInstance = hInstance;
    wx.lpszClassName = L"MyWindowClass";

    if( ! RegisterClassExW( &wx ) )
        return 1;  // TODO: improve error handling

    HWND hWnd = CreateWindowExW( 0, wx.lpszClassName, L"My Application", 0, 0, 0, 0, 0, 
                                 NULL, NULL, NULL, NULL );
    if( ! hWnd )
        return 2;  // TODO: improve error handling

    MSG msg;
    while( GetMessage( &msg, nullptr, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }

    return static_cast<int>( msg.wParam );
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
        case WM_QUERYENDSESSION:
        {
            // Try to block shutdown.
            ShutdownBlockReasonCreate( hWnd, L"I don't want to sleep (yet)!" );
            return FALSE;
        }
        case WM_ENDSESSION:
        {
            // TODO: Always handle this message because shutdown can be forced
            // even if we return FALSE from WM_QUERYENDSESSION!
            return 0;
        }
        default:
        {
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
    }
    return 0;
}

进一步阅读:

【讨论】:

  • 啊,隐形窗口。像魅力一样工作
  • 关于“它可以是不可见的。”:如果一个人想要阻止关机,则不会。 MS 的文档说:“请注意,系统不允许控制台应用程序或没有可见窗口的应用程序取消关机。” (msdn.microsoft.com/en-us/library/windows/desktop/…)
  • @Cheersandhth.-Alf 至少在 Win 10 下,即使只有一个不可见的窗口,它也会显示 ShutdownBlockReasonCreate 消息。不过还没有测试过旧的操作系统。
  • 也许文档是错误的。这不会是第一次。但是如果在屏幕外有一个可见的窗口可能会更好?
  • @Cheersandhth.-Alf 我现在已经在 Win 7 和 Win 8.1 下成功测试了上面的代码。 this page 也确认了这种行为:“但是,如果没有可见顶级窗口的应用程序使用新 API 主动指示它需要阻止关闭,Windows Vista 不会自动终止它,而是会而是将其视为具有可见顶级窗口的应用程序。”。在页面下方,“新 API”定义为 ShutdownBlockReasonCreate()
猜你喜欢
  • 1970-01-01
  • 2016-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多