如何在控制台应用程序中处理 WM_ENDSESSION

How to handle WM_ENDSESSION in a console app

我写了一个程序来注册鼠标事件,我希望它在计算机关闭时终止(然后执行刷新和最后打印)。

我尝试使用 CtrlHandler,但它仅适用于 Ctrl-C,而不是在系统关闭时,因为我使用的是 Win32 库,根据 MSDN:

If a console application loads the gdi32.dll or user32.dll library, the HandlerRoutine function that you specify when you call SetConsoleCtrlHandler does not get called for the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT events. The operating system recognizes processes that load gdi32.dll or user32.dll as Windows applications rather than console applications. This behavior also occurs for console applications that do not call functions in gdi32.dll or user32.dll directly, but do call functions such as Shell functions that do in turn call functions in gdi32.dll or user32.dll.

To receive events when a user signs out or the device shuts down in these circumstances, create a hidden window in your console application, and then handle the WM_QUERYENDSESSION and WM_ENDSESSION window messages that the hidden window receives. You can create a hidden window by calling the CreateWindowEx method with the dwExStyle parameter set to 0.

所以,首先我必须创建一个隐藏的 window,然后我必须拦截 WM_ENDSESSION 消息。但是怎么办?

我试着阅读了一些示例,但我不知道该怎么做。

这是我的代码:

BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
    switch (fdwCtrlType)
    {
        // Handle the CTRL-C signal.
    /*case CTRL_C_EVENT:
        printf("Ctrl-C event\n\n");
        Beep(750, 300);
        return FALSE;  //TRUE

        // CTRL-CLOSE: confirm that the user wants to exit.
    case CTRL_CLOSE_EVENT:
        Beep(600, 200);
        printf("Ctrl-Close event\n\n");
        return FALSE;  //TRUE

        // Pass other signals to the next handler.
    case CTRL_BREAK_EVENT:
        Beep(900, 200);
        printf("Ctrl-Break event\n\n");
        return FALSE;

    */case CTRL_LOGOFF_EVENT:
        Beep(1000, 200);
        printf("Ctrl-Logoff event\n\n");
        myfile << "totale :" << tot;
        myfile.flush();
        myfile.close();
        return TRUE;  //FALSE

    case CTRL_SHUTDOWN_EVENT:
        Beep(750, 500);
        printf("Ctrl-Shutdown event\n\n");
        myfile << "totale :" << tot;
        myfile.flush();
        myfile.close();
        return TRUE;  //FALSE

    default:
       return FALSE;
    }
     
}

int main(){

if (SetConsoleCtrlHandler(CtrlHandler, TRUE))
    {
        printf("\nThe Control Handler is installed.\n");     
   

for(;;)
    {
     code that print the mouse event(........)
    
    }

 }
    else
    {
        printf("\nERROR: Could not set control handler");
        return 1;
    }
    return 0;
}

您可以在控制台应用程序中创建一个隐藏的 GUI window 并在 window 过程中处理 WM_ENDSESSION,如下所示

#include <Windows.h>

HWND g_hidden_window = nullptr;

LRESULT CALLBACK wnd_proc(HWND, UINT, WPARAM, LPARAM);

// Main entry point of your app
int main() {

    HMODULE current_instance = ::GetModuleHandle(L"");

    // Register the window class
    WNDCLASSEX window_class_ex = { 0 };
    window_class_ex.cbSize = sizeof(WNDCLASSEX);
    window_class_ex.lpfnWndProc = wnd_proc;
    window_class_ex.lpszClassName = L"Foo";
    window_class_ex.hInstance = current_instance;
    if (!::RegisterClassEx(&window_class_ex)) {
        return 1;
    }

    // Create an overlapped window
    g_hidden_window = ::CreateWindow(
        L"Foo",
        L"",
        WS_OVERLAPPED,
        0, 0, 0, 0,
        nullptr,
        nullptr,
        current_instance,
        0);
    if (!g_hidden_window) {
        return 1;
    }

    MSG message;

    // Main message loop
    while (::GetMessage(&message, nullptr, 0, 0)) {
        ::DispatchMessage(&message);
    }
}

现在,在您的 window 主程序中,您应该处理 WM_ENDSESSION。对于您的情况,我认为没有理由处理 WM_QUERYENDSESSION。您还应该处理 WM_CLOSE and/or WM_DESTROY 以退出主消息循环:

// Main window procedure
LRESULT CALLBACK wnd_proc(HWND window_handle, UINT window_message, WPARAM wparam, LPARAM lparam) {
    switch (window_message) {

    case WM_ENDSESSION:
        if(wparam) {
            // According to MSDN this value will be 1 when the system is about to shut down: https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-endsession
            // Invoke your function here
            CtrlHandler(CTRL_SHUTDOWN_EVENT);
        }
        break;

    case WM_CLOSE:
        DestroyWindow(window_handle);
        break;

    case WM_DESTROY:
        ::PostQuitMessage(0);
        break;

    default:
        return ::DefWindowProc(window_handle, window_message, wparam, lparam);
    }

    return 0;
}

要正常关闭应用程序,您必须打破该消息循环。为此,您必须发送 WM_CLOSE 消息:

SendMessage(g_hidden_window, WM_CLOSE, 0, 0);

或者,通过调用显式销毁 window:

DestroyWindow(g_hidden_window);

如果有效请告诉我。我还没有测试它,因为我现在正在使用 Mac,但它应该可以工作。