標籤:copyto 好的 時間 執行檔案 sof form toc dba ctime
之前寫了一個軟體用於實驗室的打卡提醒,其中一個重要的功能是在關機之前提醒當天晚上是否已經打卡。之前我是在WM_ENDSESSION中彈出一個模態對話方塊來提醒,在XP中基本工作正常,在Win7中大多數時候工作正常,但是有時候會出現不提醒現象。我想這中間是不是有什麼玄機,Windows的關機方案從XP到Win7到底發生了什麼變化,如何進行有效截獲Windows關機訊息。對此,我搜尋了MSDN和網上論壇結合自己的測評給出一個完善的描述和解決方案,如果你有類似的需求,可以參考這篇文章。
在MSDN中對於Windows關機行為的變化描述只有對比Vista和XP(這是連結),但是實際評測,顯示這個描述文檔對於大家有強的誤導性,因為他只有部分是正確的,也不用奇怪,微軟的文檔錯誤多多,XX微軟,請的實習生寫的文檔嗎?如果你不想看這篇誤導性文章,直接往下面看即可。
為了反映實際的關機行為,我寫了一個小的截獲軟體,部分代碼如下
[cpp] view plain copy print?
- BOOL CEndSessionDlg::OnQueryEndSession()
- {
- // if (!CDialog::OnQueryEndSession())
- // return FALSE;
-
- //記錄關機選項和時間
- CTime time = CTime::GetCurrentTime();
- CString csTemp;
- csTemp.Format(TEXT(".\\%d.txt"), m_hWnd);
- CFile f( csTemp, CFile::modeCreate | CFile::modeWrite );
- m_csOutput.Format(TEXT("%d-%d-%d\tWM_QUERYENDSESSION\tTIME:%d-%d-%d-%d-%d-%d\r\n"),
- m_queryBlock,
- m_returnTrue,
- m_endBlock,
- time.GetYear(),
- time.GetMonth(),
- time.GetDay(),
- time.GetHour(),
- time.GetMinute(),
- time.GetSecond());
- f.Write(m_csOutput.GetBuffer(256), m_csOutput.GetLength()*sizeof(TCHAR));
-
- if (m_queryBlock == TRUE)
- {
- MessageBox(TEXT("WM_QUERYENDSESSION中Block Shutdown"));
- }
-
- if (m_returnTrue == TRUE)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
-
- void CEndSessionDlg::OnEndSession(BOOL bEnding)
- {
- //CDialog::OnEndSession(bEnding);
-
- //記錄關機選項和時間
- CTime time = CTime::GetCurrentTime();
- CString csTemp;
- csTemp.Format(TEXT(".\\%d.txt"), m_hWnd);
- CFile f( csTemp, CFile::modeCreate | CFile::modeWrite );
- csTemp.Format(TEXT("%d-%d-%d\tWM_ENDSESSION\t\tTIME:%d-%d-%d-%d-%d-%d\r\n"),
- m_queryBlock,
- m_returnTrue,
- m_endBlock,
- time.GetYear(),
- time.GetMonth(),
- time.GetDay(),
- time.GetHour(),
- time.GetMinute(),
- time.GetSecond());
- m_csOutput += csTemp;
- f.Write(m_csOutput.GetBuffer(256), m_csOutput.GetLength()*sizeof(TCHAR));
-
- if (m_endBlock == TRUE)
- {
- csTemp.Format(TEXT("WM_ENDSESSION中Block Shutdown---bEnding=%d"), bEnding);
- MessageBox(csTemp);
- }
- }
軟體介面如下
測試軟體準系統就是記錄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描述的選擇對話方塊彈出
合并整理日誌,如下
[plain] view plain copy print?
- 1-1-1 WM_QUERYENDSESSION TIME:2014-1-11-11-53-4
- <00001> 00060260 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 1-1-0 WM_QUERYENDSESSION TIME:2014-1-11-11-53-5
- <00001> 000102B0 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 0-1-1 WM_QUERYENDSESSION TIME:2014-1-11-11-53-6
- 0-1-1 WM_ENDSESSION TIME:2014-1-11-11-53-6
- <00001> 000402C4 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 000402C4 R .WM_QUERYENDSESSION fShutdownIsOk:True
- <00003> 000402C4 S .WM_ENDSESSION fEndSession:True
-
- 0-1-0 WM_QUERYENDSESSION TIME:2014-1-11-11-53-7
- 0-1-0 WM_ENDSESSION TIME:2014-1-11-11-53-7
- <00001> 00050150 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00050150 R .WM_QUERYENDSESSION fShutdownIsOk:True
- <00003> 00050150 S .WM_ENDSESSION fEndSession:True
- <00004> 00050150 R .WM_ENDSESSION
-
- 1-0-1 WM_QUERYENDSESSION TIME:2014-1-11-11-53-7
- <00001> 00030168 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 1-0-0 WM_QUERYENDSESSION TIME:2014-1-11-11-53-8
- <00001> 00030151 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 0-0-1 WM_QUERYENDSESSION TIME:2014-1-11-11-53-9
- 0-0-1 WM_ENDSESSION TIME:2014-1-11-11-53-9
- <00001> 00030160 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00030160 R .WM_QUERYENDSESSION fShutdownIsOk:False
- <00003> 00030160 S .WM_ENDSESSION fEndSession:False
- <00004> 00030160 R .WM_ENDSESSION //注意點擊確定後才返回
-
- 0-0-0
- <00001> 000800BC S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 000800BC R .WM_QUERYENDSESSION fShutdownIsOk:False
- <00003> 000800BC S .WM_ENDSESSION fEndSession:False
- <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秒鐘關閉一個測試案例直至系統關閉。
合并整理日誌,如下
[plain] view plain copy print?
- 0-0-0 WM_QUERYENDSESSION TIME:2014-1-11-11-0-55
- 0-0-0 WM_ENDSESSION TIME:2014-1-11-11-0-55
- <00001> 00020DBA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00020DBA R .WM_QUERYENDSESSION fShutdownIsOk:False
- <00003> 00020DBA S .WM_ENDSESSION fEndSession:True
- <00004> 00020DBA R .WM_ENDSESSION
-
- 0-0-1 WM_QUERYENDSESSION TIME:2014-1-11-11-0-55
- 0-0-1 WM_ENDSESSION TIME:2014-1-11-11-0-55
- <00001> 00020B2C S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00020B2C R .WM_QUERYENDSESSION fShutdownIsOk:False
- <00003> 00020B2C S .WM_ENDSESSION fEndSession:True
-
- 1-0-0 WM_QUERYENDSESSION TIME:2014-1-11-11-1-0
- <00001> 00050CFA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 1-0-1 WM_QUERYENDSESSION TIME:2014-1-11-11-1-5
- <00001> 00020BCA S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 0-1-0 WM_QUERYENDSESSION TIME:2014-1-11-11-1-10
- 0-1-0 WM_ENDSESSION TIME:2014-1-11-11-1-10
- <00001> 00020BE2 S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00020BE2 R .WM_QUERYENDSESSION fShutdownIsOk:True
- <00003> 00020BE2 S .WM_ENDSESSION fEndSession:True
- <00004> 00020BE2 R .WM_ENDSESSION
-
- 0-1-1 WM_QUERYENDSESSION TIME:2014-1-11-11-1-10
- 0-1-1 WM_ENDSESSION TIME:2014-1-11-11-1-10
- <00001> 00020C4C S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
- <00002> 00020C4C R .WM_QUERYENDSESSION fShutdownIsOk:True
- <00003> 00020C4C S .WM_ENDSESSION fEndSession:True
-
-
- 1-1-0 WM_QUERYENDSESSION TIME:2014-1-11-11-1-15
- <00001> 00040C5A S .WM_QUERYENDSESSION nSource:0 (Logoff or Shutdown from Windows Security dialog)
-
- 1-1-1 WM_QUERYENDSESSION TIME:2014-1-11-11-1-20
- <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的變化(在XP中程式可以阻止關機,但是在Win7中程式無法阻止關機,可Block的時間從1秒調到了5秒)