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

C++ 如何在控制台应用程序中处理WM_ENDSESSION,c++,winapi,mouseevent,C++,Winapi,Mouseevent,我编写了一个程序来注册鼠标事件,我希望它在计算机关闭时终止(然后执行刷新和最终打印) 我试过使用CtrlHandler,但它仅在系统关闭时使用Ctrl-C,因为我使用的是Win32库,根据MSDN: 如果控制台应用程序加载gdi32.dll或user32.dll库,则在调用SetConsoletrlHandler时指定的HandlerRoutine函数不会在CTRL\u LOGOFF\u事件和CTRL\u SHUTDOWN\u事件中被调用。操作系统将加载gdi32.dll或user32.dll的

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

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

如果控制台应用程序加载gdi32.dll或user32.dll库,则在调用SetConsoletrlHandler时指定的HandlerRoutine函数不会在CTRL\u LOGOFF\u事件和CTRL\u SHUTDOWN\u事件中被调用。操作系统将加载gdi32.dll或user32.dll的进程识别为Windows应用程序,而不是控制台应用程序。对于不直接调用gdi32.dll或user32.dll中的函数,而是调用函数(如Shell函数)的控制台应用程序,也会发生此行为,而Shell函数则反过来调用gdi32.dll或user32.dll中的函数

要在用户注销或设备在这些情况下关闭时接收事件,请在控制台应用程序中创建一个隐藏窗口,然后处理隐藏窗口接收的WM_QUERYENDSESSION和WM_ENDSESSION窗口消息。通过调用CreateWindowEx方法,将dwExStyle参数设置为0,可以创建隐藏窗口

因此,首先我必须创建一个隐藏窗口,然后我必须截获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;
}
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
{
开关(fdwCtrlType)
{
//处理CTRL-C信号。
/*案例控制事件:
printf(“Ctrl-C事件\n\n”);
嘟嘟声(750300);
返回FALSE;//TRUE
//CTRL-CLOSE:确认用户想要退出。
案例控制关闭事件:
嘟嘟声(600200);
printf(“Ctrl-Close事件\n\n”);
返回FALSE;//TRUE
//将其他信号传递给下一个处理程序。
案例控制中断事件:
嘟嘟声(900200);
printf(“Ctrl-Break事件\n\n”);
返回FALSE;
*/案例控制\注销\事件:
嘟嘟声(1000200);
printf(“Ctrl注销事件\n\n”);

myfile您只需在控制台应用程序中创建一个隐藏的GUI窗口,并在窗口过程中处理
WM_ENDSESSION
,如下所示

#包括
HWND g_hidden_window=nullptr;
LRESULT回调wnd_过程(HWND、UINT、WPARAM、LPARAM);
//应用程序的主要入口点
int main(){
hmodulecurrent_instance=::GetModuleHandle(L“”);
//注册窗口类
WNDCLASSEX窗口_类_ex={0};
window_class_ex.cbSize=sizeof(WNDCLASSEX);
窗口类前lpfnWndProc=wnd程序;
window_class_ex.lpszClassName=L“Foo”;
window\u class\u ex.hInstance=当前\u实例;
if(!::注册表类(&window\u class\u ex)){
返回1;
}
//创建重叠窗口
g_hidden_window=::CreateWindow(
L“Foo”,
L“,
W_重叠,
0, 0, 0, 0,
nullptr,
nullptr,
当前_实例,
0);
如果(!g_隐藏窗口){
返回1;
}
消息;
//主消息循环
while(::GetMessage(&message,nullptr,0,0)){
::DispatchMessage(&message);
}
}
现在,在主窗口过程中,您应该处理
WM\u ENDSESSION
。在您的情况下,我认为没有理由处理
WM\u QUERYENDSESSION
。您还应该处理
WM\u CLOSE
和/或
WM\u DESTROY
以退出主消息循环:

//主窗口过程
LRESULT回调wnd_proc(HWND window_句柄、UINT window_消息、WPARAM WPARAM、LPARAM LPARAM){
开关(窗口信息){
案例WM_ENDSESSION:
if(wparam){
//根据MSDN,当系统即将关闭时,该值将为1:https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-endsession
//在这里调用你的函数
CtrlHandler(CTRL\u关闭\u事件);
}
打破
案例WM_结束:
车窗(车窗把手);
打破
案例WM_销毁:
::PostQuitMessage(0);
打破
违约:
return::DefWindowProc(窗口句柄、窗口消息、wparam、LPRAM);
}
返回0;
}
要优雅地关闭应用程序,您必须中断该消息循环。为此,您必须发送
WM\u CLOSE
消息:

SendMessage(g_隐藏窗口,WM_关闭,0,0);
或者,通过调用以下命令显式销毁窗口:

销毁窗口(g_隐藏窗口);

让我知道它是否有效。我还没有测试它,因为我现在在Mac上,但它应该可以工作。

对于隐藏的消息窗口,不需要处理
WM\u CLOSE
。默认情况下,
DefWindowProc()
将调用
DestroyWindow()
回复
WM\u CLOSE
@RemyLebeau谢谢你!但我有另一个问题..如何运行消息循环和我的程序(检查鼠标位置是一个无限for循环)同时?如果我将for循环放在消息部分上方,它将不会运行,因为它位于while循环中。希望您理解我的问题,顺便告诉我是否应该发布代码。感谢将消息窗口创建和消息循环移动到单独的工作线程中。或者,将消息调度移到主
while
循环,并将
GetMessage()
更改为
PeekMessage()
,这样就不会阻止循环代码。只需确保主循环定期检查挂起的消息。