Is it possible to detect that a log-off has been aborted on windows?

376 views Asked by At

I'm wondering if it's possible to programmatically restart an application that has been terminated in the beginning of a shutdown that will be canceled later.
On Windows, if an application calls ShutdownBlockReasonCreate on shutdown , it can know whether the user has cancelled it or not, by checking if the ENDSESSION_CRITICAL bit is set in the lParam.
In my case I don't want to block any shutdown .

Is this possible ? Thanks for any suggestion.

1

There are 1 answers

2
Osama On

I think I found a solution :

I created a hidden window using CreateWindowEx to handle the WM_QUERYENDSESSION and WM_ENDSESSION messages:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_QUERYENDSESSION:
        {
            //clean up ...

            ShutdownBlockReasonCreate(hwnd, L"      ");
            // What ? You said you don't want to block the shutdown ?
            // Yes I do. It seems to work different with a hidden window.

            return 1;
        }
        case WM_ENDSESSION:
        {
            // GetSystemMetrics(SM_SHUTTINGDOWN) return Nonzero if the current session is shutting down
            // This loop waits for the user to cancel the shutdown
            while(GetSystemMetrics(SM_SHUTTINGDOWN) != 0)
               Sleep(100);
            // The user has cancelled the shutdown here
            // I can run the process again using CreateProcess or what ever...
            // Once this line is executed this instance will be terminated

        return 0;
        }
        default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
}

The return values are very important (see this).
As I didn't expected, a process without a top level visible window, even if it calls ShutdownBlockReasonCreate (with a hidden window handle), it will not be shown in the list of applications blocking the shutdown. And if there's no other application blocking the shutdown, Windows will only wait for a few seconds, and then shutdown.

For the loop, I took the idea from here. I don't know if there's a better alternative.

That's it.

Please test it, and correct me if I'm wrong. Thanks.

[EDIT] Here's the program main for a ready test:

#include <string>
#include <Windows.h>

using namespace std;

string getExePath()
{
    TCHAR wpath[MAX_PATH];
    GetModuleFileName (NULL, wpath, MAX_PATH);
    size_t i;
    char path[MAX_PATH];
    wcstombs_s(&i, path, MAX_PATH, wpath, MAX_PATH);

    return path;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_QUERYENDSESSION:
        {
            // A shutdown has been initiated
            ShutdownBlockReasonCreate(hwnd, L"      ");

            return 1;
        }
        case WM_ENDSESSION:
        {
            // GetSystemMetrics(SM_SHUTTINGDOWN) return Nonzero if the current session is shutting down
            // This loop waits for the user to cancel the shutdown

            while(GetSystemMetrics(SM_SHUTTINGDOWN) != 0)
               Sleep(100);            

            // The user has cancelled the shutdown here
            // I can run the process again

            WinExec(string("\"" + getExePath() + "\"").c_str(), SW_HIDE);

            TerminateProcess(OpenProcess(PROCESS_TERMINATE, FALSE, GetCurrentProcessId()), 0);

        return 0;
        }
        default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;
    WNDCLASSEXA wc;

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszClassName = "TestShutDownAbortClass";
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if( RegisterClassExA(&wc) == 0 )
        return 1;

    hwnd = CreateWindowExA(0,"TestShutDownAbortClass", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
        return 1;

    MSG msg;
    while(GetMessageA(&msg, hwnd, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessageA(&msg);
    }


    return 0;
}