Describing Windows XP and Vista Shutdown in Application context
In this post, I’m talking about some internals of Windows Shutdown in the context of applications.
When the user or an application initiates shutdown request through ExitWindowsEx function, the Csrss.exe will be initiating the shutdown operation by sending a window message to the hidden window owned by Winlogon process. The subsystem process Csrss.exe contains handles, Console (text) windows, creation and deletion of processes and threads and other various supports like ExitWindowEx etc…
The Winlogon process calls ExitWindowsEx function with some internal flags to continue the shutdown based on the security access rights of the currently-logged in user. Since the ExitWindowEx call has been made again, it falls back in the hand of Csrss.exe.
Csrss sends the WM_QUERYENDSESSION message to each thread in the process that has a Windows message loop. This message is like warning from the system that, the application has to respond for the shutdown operation. The shutdown can be preceded when the thread returns TRUE. If the thread returns FALSE, in most cases, the shutdown operation will be cancelled. The shutdown should be cancelled only for the valid reasons like CD burning, live recording etc…
Windows Vista supports more interactive shutdown(When the screen like left appears and I wondered when try to shutdown while the office applications are running. Now got the nuts and bolts of it
). One or many applications can cancel the shutdown operation under windows XP. In windows vista, if either of the application cancels shutdown operation, windows shows the list of currently running application and also the blocking application on the top of the window. If the user wants to shut his computer down, he can forcefully do that by clicking on the “Shut down now” button(red button in the figure). In this case windows sends WM_QUERYENDSESSION again with ENDSESSION_FORCEFULSHUTDOWN flag. If an application responds FALSE, Windows will continue shutdown instead of canceling it.
Windows Vista provides a facility to pro-actively describe about the reason for cancelling the shutdown. There are three new APIs at our disposal to manage shutdown cancellation string.
BOOL ShutdownBlockReasonCreate(HWND hWnd, LPCWSTR pwszReason); BOOL ShutdownBlockReasonDestroy(HWND hWnd); BOOL ShutdownBlockReasonQuery(HWND hWnd, LPWSTR pwszBuff, DWORD *pcchBuff);
The above functions are used to create, destroy and query the cancellation string for the window. The best time to create the string is at WM_QUERYENDSESSION function and destroys it once the application finished using it. (See the sample below for the usage)
Once the Query end session function returns true or managed to get out from the above described scenarios, Csrss then sends the WM_ENDSESSION Windows message to the thread to request it to exit. The application will get 5 second as time out as default (it’s defined in HKCU\Control Panel\Desktop\HungAppTimeout). If an application times out responding to WM_QUERYENDSESSION or WM_ENDSESSION, Windows will terminate it.
The behavior of the application time out is slightly different in Windows Vista and XP as we see in the picture below. Please check MSDN (I don’t want to copy paste it here) to get the detailed table.
If the thread doesn’t exit before the timeout, Csrss displays the hung-program dialog box (End Now). You can disable this dialog box by changing the registry value HKCU\Control Panel\Desktop\AutoEndTasks to 1. This dialog box indicates that a program isn’t shutting down in a timely manner and gives the user a choice of either killing the process or aborting the shutdown. There is no timeout on this dialog box, which means that a shutdown request could wait forever at this point.
Once all the threads that own windows in the process have exited, Csrss terminates the process and goes on to the next process in the interactive session.
OK Now you’re capable of handling the shutdown message for your application. Here’s a sample snippet for doing the same (MFC)
/* Map the following functions manually in your application (Win32 style)
*/
BEGIN_MESSAGE_MAP(CShutDownTestDlg, CDialog)
ON_MESSAGE( WM_QUERYENDSESSION, QueryEndSession )
ON_MESSAGE( WM_ENDSESSION, EndSession )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
LRESULT CShutDownTestDlg::QueryEndSession(WPARAM,LPARAM)
{
// Block shut down
ShutdownBlockReasonCreate(m_hWnd, L"TV Show being recorded" );
return FALSE;
// return TRUE; // continue shutdown
}
LRESULT CShutDownTestDlg::EndSession(WPARAM,LPARAM)
{
// Destroy the shutdown string used earlier
ShutdownBlockReasonDestroy(m_hWnd);
return TRUE;
}
Here’s the pure MFC way to do the same
/* Add the function using class wizard or through property window (VS2003 or above)
*/
BEGIN_MESSAGE_MAP(CShutDownTestDlg, CDialog)
ON_WM_QUERYENDSESSION()
ON_WM_ENDSESSION()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BOOL CShutDownTestDlg::OnQueryEndSession()
{
// Block shut down
ShutdownBlockReasonCreate(m_hWnd, L"TV Show being recorded" );
return FALSE;
}
void CShutDownTestDlg::OnEndSession(BOOL bEnding)
{
// Destroy the shutdown string used earlier
ShutdownBlockReasonDestroy(m_hWnd);
CDialog::OnEndSession(bEnding);
}
Hope you enjoyed this lengthy post. Thanks for MSDN articleand Mark Russinovich for his book Windows Internals. Those were the light for my path.
