之前写了一个软件用于实验室的打卡提醒,其中一个重要的功能是在关机之前提醒当天晚上是否已经打卡。之前我是在WM_ENDSESSION中弹出一个模态对话框来提醒,在XP中基本工作正常,在Win7中大多数时候工作正常,但是有时候会出现不提醒现象。我想这中间是不是有什么玄机,Windows的关机方案从XP到Win7到底发生了什么变化,如何进行有效的截获Windows关机消息。对此,我搜寻了MSDN和网上论坛结合自己的测评给出一个完善的描述和解决方案,如果你有类似的需求,可以参考这篇文章。

在MSDN中对于Windows关机行为的变化描述只有对比Vista和XP(这是链接),但是实际评测,显示这个描述文档对于大家有强的误导性,因为他只有部分是正确的,也不用奇怪,微软的文档错误多多,XX微软,请的实习生写的文档吗?如果你不想看这篇误导性文章,直接往下面看即可。

为了反映实际的关机行为,我写了一个小的截获软件,部分代码如下

  1. BOOL CEndSessionDlg::OnQueryEndSession()
  2. {
  3. //  if (!CDialog::OnQueryEndSession())
  4. //      return FALSE;
  5. //记录关机选项和时间
  6. CTime time = CTime::GetCurrentTime();
  7. CString csTemp;
  8. csTemp.Format(TEXT(".\\%d.txt"), m_hWnd);
  9. CFile f( csTemp, CFile::modeCreate | CFile::modeWrite );
  10. m_csOutput.Format(TEXT("%d-%d-%d\tWM_QUERYENDSESSION\tTIME:%d-%d-%d-%d-%d-%d\r\n"),
  11. m_queryBlock,
  12. m_returnTrue,
  13. m_endBlock,
  14. time.GetYear(),
  15. time.GetMonth(),
  16. time.GetDay(),
  17. time.GetHour(),
  18. time.GetMinute(),
  19. time.GetSecond());
  20. f.Write(m_csOutput.GetBuffer(256), m_csOutput.GetLength()*sizeof(TCHAR));
  21. if (m_queryBlock == TRUE)
  22. {
  23. MessageBox(TEXT("WM_QUERYENDSESSION中Block Shutdown"));
  24. }
  25. if (m_returnTrue == TRUE)
  26. {
  27. return TRUE;
  28. }
  29. else
  30. {
  31. return FALSE;
  32. }
  33. }
  34. void CEndSessionDlg::OnEndSession(BOOL bEnding)
  35. {
  36. //CDialog::OnEndSession(bEnding);
  37. //记录关机选项和时间
  38. CTime time = CTime::GetCurrentTime();
  39. CString csTemp;
  40. csTemp.Format(TEXT(".\\%d.txt"), m_hWnd);
  41. CFile f( csTemp, CFile::modeCreate | CFile::modeWrite );
  42. csTemp.Format(TEXT("%d-%d-%d\tWM_ENDSESSION\t\tTIME:%d-%d-%d-%d-%d-%d\r\n"),
  43. m_queryBlock,
  44. m_returnTrue,
  45. m_endBlock,
  46. time.GetYear(),
  47. time.GetMonth(),
  48. time.GetDay(),
  49. time.GetHour(),
  50. time.GetMinute(),
  51. time.GetSecond());
  52. m_csOutput += csTemp;
  53. f.Write(m_csOutput.GetBuffer(256), m_csOutput.GetLength()*sizeof(TCHAR));
  54. if (m_endBlock == TRUE)
  55. {
  56. csTemp.Format(TEXT("WM_ENDSESSION中Block Shutdown---bEnding=%d"), bEnding);
  57. MessageBox(csTemp);
  58. }
  59. }

软件界面如下

测试软件基本功能就是记录Winows关机时的WM_QUERYENDSESSION和WM_ENDSESSION消息的时间和关机选项到日志,根据测试软件界面上不同的选项在这两个消息中有不同的操作,阻塞关机采用MessageBox(不返回即阻塞)。

使用SPY++来捕获窗口消息并记录到日志

关于关机消息测试实例,我们参看之前那篇文章(这是链接),分别选择在WM_QUERYENDSESSION中测试Block Shutdown和Cancel Shutdown,在WM_ENDSESSION中测试Block Shutdown,穷举组合他们共有八组测试用例

XP测评

测试用例如下
图中的1表示Block或返回TRUE
先依次打开8个测试软件实例,按照上图编号对打开的软件按照打开顺序勾选上相应选项,然后再打开SPY++,设置对相应窗口的消息捕获,注意这个设置必须按照红字标明的顺序,至于为什么马上揭晓
按下关机后,观察发现最先打开的测试用例最先关闭,然后按照打开的顺序依次关闭,最后在第7组测试用例处停止,对,关机行为被阻止了,期间并没有MSDN描述的选择对话框弹出

合并整理日志,如下

  1. 1-1-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-4
  2. <00001> 00060260 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  3. 1-1-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-5
  4. <00001> 000102B0 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  5. 0-1-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-6
  6. 0-1-1   WM_ENDSESSION       TIME:2014-1-11-11-53-6
  7. <00001> 000402C4 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  8. <00002> 000402C4 R .WM_QUERYENDSESSION fShutdownIsOk:True
  9. <00003> 000402C4 S .WM_ENDSESSION fEndSession:True
  10. 0-1-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-7
  11. 0-1-0   WM_ENDSESSION       TIME:2014-1-11-11-53-7
  12. <00001> 00050150 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  13. <00002> 00050150 R .WM_QUERYENDSESSION fShutdownIsOk:True
  14. <00003> 00050150 S .WM_ENDSESSION fEndSession:True
  15. <00004> 00050150 R .WM_ENDSESSION
  16. 1-0-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-7
  17. <00001> 00030168 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  18. 1-0-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-8
  19. <00001> 00030151 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  20. 0-0-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-53-9
  21. 0-0-1   WM_ENDSESSION       TIME:2014-1-11-11-53-9
  22. <00001> 00030160 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  23. <00002> 00030160 R .WM_QUERYENDSESSION fShutdownIsOk:False
  24. <00003> 00030160 S .WM_ENDSESSION fEndSession:False
  25. <00004> 00030160 R .WM_ENDSESSION     //注意点击确定后才返回
  26. 0-0-0
  27. <00001> 000800BC S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  28. <00002> 000800BC R .WM_QUERYENDSESSION fShutdownIsOk:False
  29. <00003> 000800BC S .WM_ENDSESSION fEndSession:False
  30. <00004> 000800BC R .WM_ENDSESSION

分析可得如下结论:

1.XP关机的时候依次给窗口发送WM_QUERYENDSESSION和WM_ENDSESSION消息,前一个程序结束后才给第二个发送WM_QUERYENDSESSION并不是网上有些人说的先给每个程序发送WM_QUERYENDSESSION(事实上按照MSDN描述这是Windows 95的方式)。一般是先打开的程序先关闭。

2.在WM_QUERYENDSESSION中Block Shutdown1秒钟内不返回的话,XP会强制结束它,并向下一个待关闭程序继续发送WM_QUERYENDSESSION

3.在WM_ENDSESSION中Block Shutdown1秒钟内不返回的话,XP会强制结束它,并向下一个待关闭程序继续发送WM_QUERYENDSESSION

4.在WM_QUERYENDSESSION中立即返回FALSE(至少是Block不超过1秒就返回),WM_ENDSESSION接受到的关机参数是FALSE,XP立即停止关机行为,当前返回FALSE的程序依然收到WM_ENDSESSION消息,XP不会继续向下发送WM_QUERYENDSESSION。

Win7测评

同样,测试用例和上面一样

先打开SPY++,再依次打开8个测试软件实例,按照上图编号对打开的软件按照打开顺序勾选上相应选项,设置对相应窗口的消息捕获,注意这个设置必须按照红字标明的顺序,至于为什么马上揭晓
按下关机后,观察发现最后打开的测试用例最先关闭,然后按照打开的相反顺序依次关闭,过了一段时间后程序调到类似如下的界面(无法截图放的是一个示意图,具体请自行下载测试软件测试),达到如下界面后基本上等待5秒钟关闭一个测试用例直至系统关闭。

合并整理日志,如下

  1. 0-0-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-0-55
  2. 0-0-0   WM_ENDSESSION       TIME:2014-1-11-11-0-55
  3. <00001> 00020DBA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  4. <00002> 00020DBA R .WM_QUERYENDSESSION fShutdownIsOk:False
  5. <00003> 00020DBA S .WM_ENDSESSION fEndSession:True
  6. <00004> 00020DBA R .WM_ENDSESSION
  7. 0-0-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-0-55
  8. 0-0-1   WM_ENDSESSION       TIME:2014-1-11-11-0-55
  9. <00001> 00020B2C S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  10. <00002> 00020B2C R .WM_QUERYENDSESSION fShutdownIsOk:False
  11. <00003> 00020B2C S .WM_ENDSESSION fEndSession:True
  12. 1-0-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-0
  13. <00001> 00050CFA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  14. 1-0-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-5
  15. <00001> 00020BCA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  16. 0-1-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-10
  17. 0-1-0   WM_ENDSESSION       TIME:2014-1-11-11-1-10
  18. <00001> 00020BE2 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  19. <00002> 00020BE2 R .WM_QUERYENDSESSION fShutdownIsOk:True
  20. <00003> 00020BE2 S .WM_ENDSESSION fEndSession:True
  21. <00004> 00020BE2 R .WM_ENDSESSION
  22. 0-1-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-10
  23. 0-1-1   WM_ENDSESSION       TIME:2014-1-11-11-1-10
  24. <00001> 00020C4C S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  25. <00002> 00020C4C R .WM_QUERYENDSESSION fShutdownIsOk:True
  26. <00003> 00020C4C S .WM_ENDSESSION fEndSession:True
  27. 1-1-0   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-15
  28. <00001> 00040C5A S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
  29. 1-1-1   WM_QUERYENDSESSION  TIME:2014-1-11-11-1-20
  30. <00001> 00050C6C S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)

分析可得如下结论:

1.Win7关机的时候依次给窗口发送WM_QUERYENDSESSION和WM_ENDSESSION消息,前一个程序结束后才给第二个发送WM_QUERYENDSESSION并不是网上有些人说的先给每个程序发送WM_QUERYENDSESSION(事实上按照MSDN描述这是Windows 95的方式)。一般是先打开的程序后关闭。

2.在WM_QUERYENDSESSION中Block Shutdown5秒钟内不返回的话,Win7会强制结束它,并向下一个待关闭程序继续发送WM_QUERYENDSESSION

3.在WM_ENDSESSION中Block Shutdown5秒钟内不返回的话,Win7会强制结束它,并向下一个待关闭程序继续发送WM_QUERYENDSESSION

4.在WM_QUERYENDSESSION中返回FALSE和返回TURE的效果一样,WM_ENDSESSION接受到的关机参数都是TRUE,Windows继续向下发送WM_QUERYENDSESSION。

5.一旦在5秒内没有处理完所有的程序的WM_QUERYENDSESSION和WM_ENDSESSION,Win7就会切换到“强制关机或取消”界面。

XP到Win7的关机行为变化

对比测评,可以看到XP到Win7的如下改变

1.在XP中程序可以阻止关机,但是在Win7中程序无法阻止关机。对此微软MSDN给出的描述是尽量遵循用户的行为,假设用户按下关机,那么内心是希望完成关机的,如果程序能够阻止用户关机的话,那么就是不友好的程序了,所以在Win7中干脆不允许用户程序阻止系统关机。所以这里WM_QUERYENDSESSION的返回值TURE或FALSE在Win7中是没有意义的,这只是为了兼容以前的程序,事实上不管怎样,在Win7 WM_ENDSESSION中接受到的关机信息都是TRUE。

2.那么如果是用户误操作按了关机呢?XP对此不管,Win7有个缓冲的界面(如上),允许用户取消关机,总之是越来越人性化,苦的就是开发人员。

3.对于程序来说,从XP到Win7,微软将可Block的时间从1秒调到了5秒,这允许程序做更多的收尾工作,如保存数据到文件等等。但是要明白的是一旦你的收尾工作太长,Block了超过规定的时长没有返回,系统就会Teminate程序。对此微软MSDN给出的建议,如果你的程序要保存大量数据请设置定时保存,毕竟关机时刻的行为是不可依赖的。你只能把少部分数据保存工作放在此时处理。再如果你想依靠这个消息做数据备份,那么只能说你太天真了。

有效的截获Windows关机消息

基本上我们截获关机消息有如下需求和解决方案
1.阻止关机
前面已经说过了,这在Win7是不行的,如果你非要说我的程序就只给XP用户使,那么你要有这个功能也可以。你需要做的是在WM_QUERYENDSESSION中1秒内返回FALSE,最好是立即返回FALSE。如果你还要弹出一个对话框选择是否关机,那么默认行为是关机,在WM_QUERYENDSESSION返回FALSE前弹出模态的MessageBox给你的用户可怜的1秒钟选择是否关机。如果你取消关机了,可以在WM_ENDSESSION中通知用户。
2.写入参数到文件
微软MSDN建议是在WM_QUERYENDSESSION中立即返回,把所有的保存操作放到WM_ENDSESSION中处理,当然不要忘了XP 1秒和Win7 5秒的Block限制。
3.提示用户一些信息
如果你是要和我的软件功能一样提醒一下用户,那么把提示消息行为放在WM_QUERYENDSESSION中处理,但是Win7中如果在你的程序之前有程序阻塞超过5秒,那么就会切换到Win7独有的关机界面,这时候就看不到提示信息了,这样怎么办呢。我们上面说过了XP的Win7的程序关机顺序是不一样的,可以使用SetProcessShutdownParameters函数将当前程序的关机顺序提前并使用ShutdownBlockReasonCreate函数创建在关机界面上的提示消息。
 

博客完整测试代码和测试日志下载链接

笔者的实验室打卡精灵最新版本可执行文件和源代码链接,其中对关机时的提醒优化主要的方法就是提升程序的关机顺序和改在WM_QUERYENDSESSION消息中提醒

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

http://blog.csdn.net/wenzhou1219/article/details/18138885

深入windows的关机消息截获-从XP到Win7的变化的更多相关文章

  1. 深入windows的关机消息截获-从XP到Win7的变化(在XP中程序可以阻止关机,但是在Win7中程序无法阻止关机,可Block的时间从1秒调到了5秒) good

    之前写了一个软件用于实验室的打卡提醒,其中一个重要的功能是在关机之前提醒当天晚上是否已经打卡.之前我是在WM_ENDSESSION中弹出一个模态对话框来提醒,在XP中基本工作正常,在Win7中大多数时 ...

  2. 关于 OnCloseQuery: 顺序、不能关机等(所有的windows的广播消息都是逐窗口传递的)——如果一个窗体的OnCloseQuery事件中如果写了代码那么WM_QUERYENDSESSION消息就传不过去了msg.result会返回0,关机事件也就停止了

    系统关闭窗体的事件顺序为: OnCloseQuery ----> OnClose ----> OnDestroy 下面的代码说明问题: unit Unit3; interface uses ...

  3. 黑客编程教程(六)Windows的关机和重起

    第六节 Windows的关机和重起 很多木马都有远程关机功能,但这并不是一个很好的功能.不过对于入侵服务器,有时需要重起服务器.其实对于关机和重起,只需要调用几个 API函数即可实现. 对于WIN9X ...

  4. C#实现控制Windows系统关机、重启和注销的方法:

    shutdown命令的参数: shutdown.exe -s:关机shutdown.exe -r:关机并重启shutdown.exe -l:注销当前用户 shutdown.exe -s -t 时间:设 ...

  5. Delphi中window消息截获的实现方式(1)

    近来笔者在一个项目中需要实现一个功能:模仿弹出菜单的隐藏方式,即鼠标在窗口的非PanelA区域点击时,使得PanelA隐藏.   经过思考,笔者想到通过处理鼠标的点击事件来实现相应功能.但是,究竟由谁 ...

  6. windows远程关机重启

    windows远程关机 http://lsscto.blog.51cto.com/779396/245681 shutdown http://baike.baidu.com/view/596875.h ...

  7. Delphi中的消息截获(六种方法:Hook,SubClass,Override WndProc,Message Handler,RTTI,Form1.WindowProc:=@myfun)good

    Windows是一个基于消息驱动的系统,因此,在很多时候,我们需要截获一些消息然后自己进行处理.而VCL系统又有一些特定的消息.下面对我所了解的delphi环境中截获消息进行一些总结.      就个 ...

  8. C#实现控制Windows系统关机、重启和注销的方法

    shutdown命令的参数: shutdown.exe -s:关机shutdown.exe -r:关机并重启shutdown.exe -l:注销当前用户 shutdown.exe -s -t 时间:设 ...

  9. 再谈Delphi关机消息拦截 -- 之控制台程序 SetConsoleCtrlHandler(控制台使用回调函数拦截,比较有意思)

    这里补充一下第一篇文章中提到的拦截关机消息 Delphi消息拦截:http://blog.csdn.net/cwpoint/archive/2011/04/05/6302314.aspx 下面我再介绍 ...

随机推荐

  1. Jquery构建Form表单Post提交数据的简单方法

    $.extend({ PostSubmitForm: function (url, args) { var body = $(document.body), form = $("<fo ...

  2. Implement custom foreach function in C#

    http://msdn.microsoft.com/en-us/library/System.Collections.IEnumerator.aspx http://support.microsoft ...

  3. java结构与算法之选择排序

    一 .java结构与算法之选择排序(冒择路兮快归堆) 什么事选择排序:从一组无序数据中选择出中小的的值,将该值与无序区的最左边的的值进行交换. 简单的解释:假设有这样一组数据 12,4,23,5,找到 ...

  4. BZOJ 1863: [Zjoi2006]trouble 皇帝的烦恼( 二分答案 )

    二分答案..然后从头到尾推一下, 看最后一个能不能取0个和第一个人相同的勋章 ------------------------------------------------------------- ...

  5. js中设置setInterval的注意点

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  6. CSS小技巧-图片自动缩放

    css的一个重要属性:max-width min-width 示例: <div width="500" height="259"><p> ...

  7. win7 VMware Workstation Centos6.5虚机桥接上网设置 详解(靠谱)

    1.VMware Workstation 设置 2. vim /etc/sysconfig/network-scripts/ifcfg-eth0 NAME="System eth0" ...

  8. Python网络编程——修改套接字发送和接收的缓冲区大小

    很多情况下,默认的套接字缓冲区大小可能不够用.此时,可以将默认的套接字缓冲区大小改成一个更合适的值. 1. 代码 # ! /usr/bin/env python # -*- coding: utf-8 ...

  9. AutoCAD 2014简体中文版官方正式版x86 x64下载,带注册机,永久免费使用

    注册机使用说明:会有部分杀毒软件报病毒,请无视.操作步骤:1.安装Autodesk AutoCAD 20142.使用以下序列号666-69696969安装.3.产品密码为001F14.安装完成后,启动 ...

  10. A. Liserious战队

    Liserious战队是一支活跃在京沪地区的著名密室逃脱战队,在优秀的队长Liserious和芳姐的带领下,打遍天下大大小小的密室逃脱,攻无不克,战无不胜,上天入地无所不能. 世界密室逃脱委员会以IS ...