Windows開機和關機慢,很多時候慢得令人抓狂。特別是做嵌入式開發時(如XPE和WinCE),任誰都無法忍受開發出來的裝置開關機的蝸牛速度。所以我們得為她加速。採用HORM是不錯的方案,因為是直接從休眠檔案中恢複系統現場,開機速度快了不少。採用HORM方案後,Windows預設的關機過程中,很多步驟對我們的裝置來說,是完全不需要的,我們需要直接斷電關機。本文將分析Windows的關機過程,然後介紹如何使用Windows未公開的API實現直接斷電關機。
一 Windows關機過程
簡單地說,在Windows關機時,系統做了以下工作:
1. 軟保護
首先先結束登入使用者開啟的所有程式,儲存使用者的設定和系統設定,然後停止系統服務和作業系統大部分進程。
2. 硬保護 複位硬體,如複位磁碟的磁頭、停止硬體驅動程式等。
3. 斷電 斷開主板給各硬體裝置的電源。當然這步需要主板的電源管理模組支援,一般來說,ATX電源和主板都支援軟斷電。
在整個關機過程中,軟保護是最耗時的,少則五六秒,多則上分鐘。剛安裝的作業系統,因為未安裝驅動和開啟額外的系統服務,關機非常快。開啟服務一多,關機就慢下來了,特別是安裝了有Bug的驅動,問題可能更糟。
二 軟保護
為了保證資料的完整性,軟保護是必須的,不管是作業系統本身還是第三方的應用程式。
軟保護的步驟有:
1. 使用者發起關機指令以後,發起關機指令的程式會通知Windows子系統CSRSS.EXE,CSRSS.EXE收到通知以後會和Winlogon.EXE做一個資料交換,接著由Winlogon.EXE通知CSRSS.EXE開始關閉系統的流程 。
2. CSRSS.EXE收到Winlogon.EXE的通知以後,會依次查詢擁有頂層視窗的使用者進程,讓這些使用者進程退出。如果某一個使用者進程在一個預設的逾時時間5000毫秒(可以通過修改註冊表索引值HKEY_CURRENT_USER/Cont rol Panel/Desktop/ HungAppTimeout設定逾時時間)內沒有退出的話,Windows會顯示一個結束任務對話方塊用於詢問使用者是否結束這個任務。預設情況下將顯示這個對話方塊並一直保持而不會自動關閉。對於控制台程式來說,基本情況類似,只不過Windows使用HK EY_CURRENT_USER/Control Panel/Desktop/ WaitToKillAppTimeout值來設定逾時時間。
3. 接著是輪到終止系統進程了。系統進程包括SMSS.EXE、Winlogon.EXE、Lsass.EXE等。Windows在終止系統進程的時候並不像終止使用者進程那樣如果無法在規定時間內終止則提示使用者,而是跳過這個進程,去執行下一個系統 進程的終止操作。使用的逾時時間和第2步使用的時間相同。
三 硬保護和斷電
在完成軟保護過程後,Winlogon.EXE調用一個原生API函數ZwShutdownSystem()或NtShutdownSystem()來命令系統執行後面的掃尾工作,包括上面提到的硬保護和ATX斷電。
在ZwShutdownSystem函數調用過程中,Windows執行子系統會完成最後的關機操作,例如:裝置驅動在這個階段裡面完成一些驅動設定的特殊操作;也是在這個階段,組態管理系統將被修改過的註冊表資料會寫道磁碟裡面。等除了電源管理以後的全部子系統完成退出以後,電源管理完成最後的操作,如重啟、關機等。
四 響應關機事件
不管是直接按機箱上的Power按鈕,還是點擊開始菜單->關閉電腦(登出、關機、重啟),我們的應用程式都可以響應這類事件,那就是視窗訊息WM_QUERYENDSESSION和WM_ENDSESSION。
系統提供了一個常用的API實現系統的登出、關機和重啟,它的聲明為:
- BOOL ExitWindowsEx(
- UINT uFlags,
- DWORD dwReason
- );
參數uFlags可分為兩類,之間可以用“|”合并:
1. 關閉動作,有以下標誌:EWX_LOGOFF(登出)、EWX_SHUTDOWN(關閉系統後不切斷電源,即使主板支援ATX電源管理)、EWX_POWEROFF(關機,關閉系統後切斷電源,需主板支援)、EWX_REBOOT(重啟)。
2. 關閉強度,有以下標誌:數值0(安全關閉,不使用該類標誌時預設為該項)、EWX_FORCEIFHUNG(應用程式掛起一段時間後強行關閉)、EWX_FORCE(強行關閉,不管應用程式有沒有掛起)。
如果不使用關閉強度標誌(EWX_FORCE或EWX_FORCEIFHUNG),關機是安全的,也就是說,在關機的軟保護時,系統將給每個以案頭為頂級的視窗進程發送WM_QUERYENDSESSION訊息。如果超過5000毫秒(可以通過修改註冊表索引值HKEY_CURRENT_USER/Cont rol Panel/Desktop/ HungAppTimeout設定逾時時間)仍未有WM_QUERYENDSESSION訊息的返回,則彈出結束任務對話方塊用於詢問使用者是否結束這個任務。預設情況下將顯示這個對話方塊並一直保持而不會自動關閉;如果設定了自動結束任務(HKEY_CURRENT_USER/Cont rol Panel/Desktop/AutoEndTasks索引值改為1),那麼逾時(HungAppTimeout)後仍未有WM_QUERYENDSESSION訊息的傳回值時,不再顯示結束任務對話方塊而直接結束這個掛起的任務。如果多個進程響應了WM_QUERYENDSESSION並掛起(如記事本彈出詢問是否儲存的訊息框),那麼系統對於每個進程是串列處理的,即等待第一個掛起的進程響應WM_QUERYENDSESSION並返回後(立即發送WM_ENDSESSION通知同一視窗使用者的選擇<是否確認關閉>),再給下一個進程發送WM_QUERYENDSESSION並等待掛起逾時。
需要注意的是,不管是點擊系統彈出的結束任務對話方塊上的確定按鈕,還是系統逾時自動結束所有任務(已設定AutoEndTasks),那麼掛起後的WM_QUERYENDSESSION和WM_ENDSESSION響應代碼不會被執行。
五 直接斷電關機
如上分析,如果您不在乎應用程式的資料丟失和作業系統的系統檔案破壞(可能進不了系統),您完全可以把關機的軟保護過程省略,來加快關機的速度。網上已經有許多快速關機軟體,就是直接調用ntdll.dll中的ZwShutdownSystem()實現的。當然Windows系統本身也提供了這樣的功能讓您有選擇地快速關機:開啟工作管理員,按住鍵盤的Ctrl鍵,同時點擊菜單“關機”-“關閉(或重啟)”,即可在一兩秒內立即斷電關機。
程式實現斷電關機的代碼如下:
- const int SE_SHUTDOWN_PRIVILEGE = 0x13;
- typedef int (__stdcall *PFN_RtlAdjustPrivilege)( INT, BOOL, BOOL, INT*);
- typedef int (__stdcall *PFN_ZwShutdownSystem)(INT);
- HMODULE hModule = ::LoadLibrary(_T("ntdll.dll"));
- if( hModule != NULL)
- {
- PFN_RtlAdjustPrivilege pfnRtl = (PFN_RtlAdjustPrivilege)GetProcAddress( hModule, "RtlAdjustPrivilege");
- PFN_ZwShutdownSystem pfnShutdown = (PFN_ZwShutdownSystem)GetProcAddress( hModule,"ZwShutdownSystem");
- if( pfnRtl != NULL &
- pfnShutdown != NULL )
- {
- int en = 0;
- int nRet= pfnRtl( SE_SHUTDOWN_PRIVILEGE, TRUE, TRUE, &en);
- if( nRet == 0x0C000007C )
- nRet = pfnRtl(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &en);
- //SH_SHUTDOWN = 0;
- //SH_RESTART = 1;
- //SH_POWEROFF = 2;
- const int SH_POWEROFF = 2;
- nRet = pfnShutdown(POWEROFF);
- }
- }