2

I have windows application with this source code

#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;
}

I need to know when the system is shutting down and prevent it, or at least send a message to the user.

It doesn't seem that my application is receiving the WM_QUERYENDSESSION message.

I also tried to use ShutdownBlockReasonCreate() but I don't have a HWND for a window.

How should I do this?

Melebius
  • 6,183
  • 4
  • 39
  • 52
Shawn Li
  • 419
  • 3
  • 17
  • 3
    It's customary to create a window before trying to handle messages for it. – Jonathan Potter Dec 12 '17 at 08:07
  • 1
    A "window procedure" (or "winproc") is connected to a window class. If you don't have a window or a window class, then there's nothing for the event loop to do really. There's nothing special with a function named `WinProc`, Windows will not call it automatically. – Some programmer dude Dec 12 '17 at 08:09
  • The documentation of `WM_QUERYENDSESSION` is unclear on whether this message is sent to all top level windows (that would be absurd, but not unthinkable) or is a task message. That is, if Microsoft's documentation was reliable, then one could infer from "processed by windows proc" that it's sent to a window. But the documentation is full of such tech writer's license statements, most of which are plain wrong (like many highly upvoted SO answers). If it's a task messqage, then just move the processing to your message loop. But if it's sent to top level widnows, then create a top level window. – Cheers and hth. - Alf Dec 12 '17 at 08:37
  • Why would you want to do that? This sounds like a Bad Idea™. – rioki Dec 12 '17 at 21:44
  • 1
    @Cheersandhth.-Alf You propably mean a *thread* message? Actually it is not that unclear if you consider the fact that `WM_QUERYENDSESSION` has a return value. [A thread message can only be *posted*](https://blogs.msdn.microsoft.com/oldnewthing/20081223-00/?p=19743) and thus it can't return a value to its sender. This only leaves us with the `SendMessage*` family of functions which are always associated with a window. – zett42 Dec 12 '17 at 23:38
  • @zett42: Good point, thanks. – Cheers and hth. - Alf Dec 13 '17 at 04:15

1 Answers1

5

As stated in the reference for WM_QUERYENDSESSION:

A window receives this message through its WindowProc function.

You have a WindowProc but you are missing a window. A WindowProc must be associated with a window, otherwise it is not known to Windows. To associate a WindowProc with a window, you can call RegisterClassEx followed by CreateWindowEx. Specify the name of your newly created window class in the call to CreateWindowEx.

The window must be a top-level window. It can be invisible, but in this case the following applies (from Application Shutdown Changes in Windows Vista):

Also note that if your application has no visible top-level windows, it must use this API [ShutdownBlockReasonCreate()] if it needs to successfully block shutdown. Such applications will automatically be terminated if they block shutdown without using the API.

Note that a message-only window will not receive WM_QUERYENDSESSION.

Working example:

#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;
}

Further read:

djvg
  • 11,722
  • 5
  • 72
  • 103
zett42
  • 25,437
  • 3
  • 35
  • 72
  • Ah, invisible window. Worked like a charm – Shawn Li Dec 12 '17 at 23:32
  • Re "It can be invisible.": not if one one wants to block shutdown. Says MS' docs: "Note that the system does not allow console applications or applications without a visible window to cancel shutdown." (https://msdn.microsoft.com/en-us/library/windows/desktop/bb394721(v=vs.85).aspx) – Cheers and hth. - Alf Dec 13 '17 at 04:16
  • @Cheersandhth.-Alf At least under Win 10 it shows the `ShutdownBlockReasonCreate` message even if there is only an invisible window. Haven't tested older OS though. – zett42 Dec 13 '17 at 08:52
  • Maybe the docs are wrong then. It would not be the first time. But better with a visible window off-screen, maybe? – Cheers and hth. - Alf Dec 13 '17 at 08:53
  • 2
    @Cheersandhth.-Alf I have now successfully tested above code under Win 7 and Win 8.1 too. This behavior is also confirmed on [this page](https://msdn.microsoft.com/en-us/library/windows/desktop/bb394721(v=vs.85).aspx): "_However, if an application with no visible top-level windows uses the new API to proactively indicate that it needs to block shutdown, Windows Vista will not automatically terminate it, and will instead treat it like an application that does have a visible top-level window._". Further down the page, the "new API" is defined as `ShutdownBlockReasonCreate()`. – zett42 Dec 13 '17 at 11:36