好長時間不更新博客了,今天終於把畢業設計答辯完了,雖然沒完全做到自己預先設定的目標,但是起碼算是效果出來了。雖然很長時間不更博客,但是訂閱的RSS倒也沒扔下,堅持讀一讀,今天這篇文章就是根據msdn博客的一篇博文寫成的,只是添加了一些代碼,有願意看英文的請移步到這里。
從Vista開始,想阻止系統關機就開始變麻煩了,不能只攔截WM_QUERYENDSESSION了,操作系統只給一個應用程序兩秒鍾的時間去保存自己的東西,兩秒鍾之后,不管做完了沒有,Game Over!可是如果你正在刻錄一張光盤呢?兩秒鍾是都不夠的,所以Vista之后也給提供了一種方式阻止系統關機,就是ShutdownBlockReasonCreate函數,只要調用這個函數,第一個參數傳遞創建主窗口的句柄,第二個參數傳遞一個字符串,這個字符串說明阻止關機的原因,將會顯示給用戶。而且msdn提示說這個API函數只能由創建主窗口的線程調用,否則將返回ERROR_ACCESS_DENIED。而且完成后還得調用ShutdownBlockReasonDestroy函數使系統關機能夠繼續進行,參數只傳遞上面那個窗口的句柄就可以了。
下面這段代碼就是完整實現,不能使用VS2005編譯,因為自帶的庫還沒有這倆函數,我是使用VS2010編譯的,工程文件和可執行文件的下載地址在文章最下方。
1 #include <windows.h> 2 #include <tchar.h> 3 #include "resource.h" 4 5 //標記是否已經阻止關機,默認為未阻止 6 BOOL blockedFlag = FALSE; 7 8 //這倆函數純粹是撒嬌,為了突出它們才拿出來改個名的 9 BOOL BlockShutdown(HWND hwnd) 10 { 11 if (ShutdownBlockReasonCreate(hwnd, _T("不准關機!除非你點強制關機 :-("))) 12 { 13 return TRUE; 14 } 15 return FALSE; 16 } 17 18 BOOL UnblockShutdown(HWND hwnd) 19 { 20 if (ShutdownBlockReasonDestroy(hwnd)) 21 { 22 return TRUE; 23 } 24 return FALSE; 25 } 26 27 INT_PTR CALLBACK MainDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 28 { 29 switch(uMsg) 30 { 31 case WM_INITDIALOG: 32 SetDlgItemText(hwndDlg, IDC_STATIC_STATUS, _T("未阻止關機")); 33 return TRUE; 34 case WM_CLOSE: 35 if (blockedFlag) 36 { 37 if (UnblockShutdown(hwndDlg)) 38 { 39 EndDialog(hwndDlg, 0); 40 return TRUE; 41 } 42 if (IDYES == MessageBox(hwndDlg, _T("解除阻止失敗……還要繼續關閉程序么(關閉程序后不影響關機)?"), _T("提示"), MB_YESNO)) 43 { 44 EndDialog(hwndDlg, 0); 45 return TRUE; 46 } 47 return FALSE; 48 } 49 EndDialog(hwndDlg, 0); 50 return TRUE; 51 case WM_QUERYENDSESSION: 52 if (blockedFlag) //不要以為只調用BlockShutdownReasonCreate就行了,WM_QUERYENDSESSION也得攔住 53 { 54 return TRUE; 55 } 56 return FALSE; 57 case WM_COMMAND: 58 switch(LOWORD(wParam)) 59 { 60 case IDC_BUTTON_BLOCK: 61 if (blockedFlag) 62 { 63 MessageBox(hwndDlg, _T("已經阻止關機,請點擊關機測試。"), _T("提示"), MB_OK); 64 } 65 else 66 { 67 if (BlockShutdown(hwndDlg)) 68 { 69 SetDlgItemText(hwndDlg, IDC_STATIC_STATUS, _T("已經阻止關機")); 70 blockedFlag = TRUE; 71 } 72 else 73 { 74 MessageBox(hwndDlg, _T("阻止關機失敗了……"), _T("提示"), MB_OK); 75 } 76 } 77 return TRUE; 78 case IDC_BUTTON_UNBLOCK: 79 if (!blockedFlag) 80 { 81 MessageBox(hwndDlg, _T("未阻止關機,無需解除。"), _T("提示"), MB_OK); 82 } 83 else 84 { 85 if (UnblockShutdown(hwndDlg)) 86 { 87 SetDlgItemText(hwndDlg, IDC_STATIC_STATUS, _T("未阻止關機")); 88 blockedFlag = FALSE; 89 } 90 else 91 { 92 MessageBox(hwndDlg, _T("解除阻止失敗了……"), _T("提示"), MB_OK); 93 } 94 } 95 return TRUE; 96 default: 97 return FALSE; 98 } 99 default: 100 return FALSE; 101 } 102 return FALSE; 103 } 104 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 105 { 106 DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, MainDialogProc); 107 return 0; 108 }
下圖就是運行效果。
上面這個圖應該很多人看到過,只是通常剛一顯示就消失了,這樣就不會消失了,直到程序調用了ShutdownBlockReasonDestroy。
還有一點需要注意的是,如果你的電腦安裝了360安全衛士orQQ電腦管家等優化軟件,通常都會一鍵優化給優化掉……你就看不到這個對話框了,程序直接被槍斃掉了,因為這樣可以加快關機速度 :-(
完整的C語言工程和可執行程序文件都在壓縮包里,為了方便沒裝VS2010的童鞋測試程序效果,msvcr100.dll我也放到release目錄下了,下載地址是http://files.cnblogs.com/pianoid/ShutdownBlocker.rar。