MFC: 获得关机消息;阻止Windows关机

时间:2023-03-09 16:01:56
MFC: 获得关机消息;阻止Windows关机

WM_QUERYENDSESSION消息是Windows向你询问Windows能否关闭,WM_ENDSESSION消息表示提示你Windows即将关闭。故当应用程序退出时, WM_QUERYENDSESSION消息和WM_ENDSESSION消息均不响应。也就是说也就是说,你的程序在运行状态时,而系统由于某种原因需要注销或重启(或关机),此时操作系统会向你询问,但到你的同意(返回TRUE)后,系统才能做进一步的操作。


Windows 在注销、重起、关机之前会向每个当前正在运行的应用程序发送WM_QUERYENDSESSION消息,
--> 如果该应用程序方便关闭的话,则在此消息的消息响应函数OnQueryEndSession()中返回非0值(TRUE),否则返回0值(FALSE)。
--> 如果当前系统正在运行的应用程序全部返回非0值,则Windows的注销、重起、关机过程继续下去,并且向所有运行的应用程序发送WM_ENDSESSION消息,并且消息参数为TRUE;
--> 如果当前系统正在运行的应用程序其中之一返回0值,则Windows的注销、重起、关机过程被终止,并且不再继续对剩余的应用程序调用OnQueryEndSession()消息响应函数,而是向已经调用过了OnQueryEndSession()消息响应函数的应用程序发送WM_ENDSESSION消息,并且消息参数为FALSE。
所以一个应用程序只要响应WM_ENDSESSION消息,并在WM_ENDSESSION消息的消息响应函数OnEndSession(BOOL bEnding) 中根据参数bEnding来判断系统最后是否真的要注销、重起、关机了,然后做出适当的反应即可。
MSDN:

The framework calls this member function when the user chooses to end the Windows session or when an application calls theExitWindows Windows function. If any application returns 0, the Windows session is not ended. Windows stops callingOnQueryEndSession as soon as one application returns 0 and sends theWM_ENDSESSION message with a parameter value ofFALSE for any application that has already returned nonzero.

当用户选择关闭Windows或者应用程序调用Windows的ExitWindows函数时,框架调用这个成员函数。如果应用程序返回0,则Windows会话不会结束。只要有一个应用程序返回了0,Windows就停止调用OnQueryEndSession,并且向所有已经返回非零值的应用程序发送一个WM_ENDSESSION消息,参数值为FALSE。

目的:当用户注销系统的时候,注销前,让我的程序执行几行代码(释放资源/关闭端口之类的)。

思路:就跟把系统注销、重启和关机的消息都拦截下来,并销毁掉,不让其执行注销、重启和关机等操作原理是一样的。因为Windows在注销、重起、关机之前会向每个当前正在运行的应用程序广播一个消息WM_QUERYENDSESSION, 其lParam参数可以区分是关机还是注销用户(注销用户时lParam是ENDSESSION_LOGOFF)。然后Windows会等到所有的应用程序都对这个消息返回TRUE才会关机,因此,只要我们的应用程序对这个消息的处理返回FALSE,Windows就不会关机了。当系统注销的时候,所有非系统级的程序都会被退出,如果想让自己的程序在注销时仍然可以运行,就将自己的程序做成系统服务程序。

解决途径:拦截系统广播出来的WM_ENDSESSION和WM_QUERYENDSESSION消息。插一句:方法应该不止一种,比如系统钩子,但是参考网友的解决方案,程序一运行系统就奔溃了。没想到在MFC里面非常容易的通过重载WindowProc就可以实现,方法如下。

在MFC中的解决方法:

环境:VS 6.0, XP 32-bit

“查看”-->"建立类向导...",将WindowProc重载。

MFC: 获得关机消息;阻止Windows关机

其函数体如下:

[cpp] view plain copy

MFC: 获得关机消息;阻止Windows关机MFC: 获得关机消息;阻止Windows关机

  1. LRESULT CLabRelayDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3. // TODO: Add your specialized code here and/or call the base class
  4. // WM_QUERYENDSESSION消息是Windows向你询问Windows能否关闭
  5. // WM_ENDSESSION消息表示提示你Windows即将关闭。
  6. switch(message) {
  7. case WM_ENDSESSION:
  8. // 把消息销毁掉,不让其正确转发。
  9. //message = WM_USERMESSAGE_NULL;
  10. //turn off the relay
  11. if(relayState==RELAY_ON)
  12. turnOff();
  13. break ;
  14. case WM_QUERYENDSESSION: //注销/关机
  15. //turn off the relay
  16. if(relayState==RELAY_ON)
  17. turnOff();
  18. break ;
  19. }
  20. return CDialog::WindowProc(message, wParam, lParam);
  21. }

就这样,系统注销的时候我的函数turnOff();被执行了。

关于lParam参数是通知码有一些的值:

ENDSESSION_CLOSEAPP 
0x00000001

The application is using a file that must be replaced, the system is being serviced, or system resources are exhausted. For more information, see Guidelines for Applications.

ENDSESSION_LOGOFF 
0x80000000