/*++ Copyright (c) 1994-2001 Microsoft Corporation Module Name : shutdown.cpp Abstract: IIS Shutdown/restart dialog Author: Ronald Meijer (ronaldm) Sergei Antonov (sergeia) Project: Internet Services Manager Revision History: --*/ #include "stdafx.h" #include "common.h" #include "InetMgrApp.h" #include "iisobj.h" #include "shutdown.h" // // Shutdown in milliseconds // #define IIS_SHUTDOWN_TIMEOUT 300000L #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CRITICAL_SECTION gcs; UINT __cdecl StopIISServices( IN LPVOID pParam ) /*++ Routine Description: Worker thread to perform IIS Service Control Command Arguments: LPVOID * pParam : Casts to IISCOMMAND (see above) Return Value: UINT --*/ { IISCOMMAND * pCmd = (IISCOMMAND *)pParam; // // This thread needs its own CoInitialize // CError err(CoInitialize(NULL)); ASSERT_PTR(pCmd->pMachine); CIISSvcControl isc(pCmd->pMachine->QueryAuthInfo()); err = isc.QueryResult(); // // Block access to pCmd since the main thread will try to // delete it. // EnterCriticalSection(&gcs); if (err.Succeeded()) { err = isc.Stop(IIS_SHUTDOWN_TIMEOUT, TRUE); } // // Clean Up, returning the error code // EnterCriticalSection(&pCmd->cs); pCmd->fFinished = TRUE; pCmd->hReturn = err; LeaveCriticalSection(&pCmd->cs); LeaveCriticalSection(&gcs); return 0; } // // Shutdown progress dialog // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // // Timer ID and values // #define ID_TIMER (1) #define A_SECOND (1000L) CShutProgressDlg::CShutProgressDlg(IISCOMMAND * pCommand, BOOL bShowCancel) /*++ Routine Description: Constructor for shutdown progress dialog Arguments: IISCOMMAND * pCommand : Command structure Return Value: N/A --*/ : CDialog(CShutProgressDlg::IDD, pCommand->pParent), m_pCommand(pCommand), m_bShowCancel(bShowCancel), m_uTimeoutSec(pCommand->dwMilliseconds / A_SECOND) { //{{AFX_DATA_INIT(CShutProgressDlg) //}}AFX_DATA_INIT } void CShutProgressDlg::DoDataExchange( IN CDataExchange * pDX ) /*++ Routine Description: Initialise/Store control data Arguments: CDataExchange * pDX - DDX/DDV control structure Return Value: None --*/ { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CShutProgressDlg) DDX_Control(pDX, IDC_STATIC_PROGRESS, m_static_ProgressMsg); DDX_Control(pDX, IDC_PROGRESS_SHUTDOWN, m_prgShutdown); //}}AFX_DATA_MAP } // // Message Map // BEGIN_MESSAGE_MAP(CShutProgressDlg, CDialog) //{{AFX_MSG_MAP(CShutProgressDlg) ON_WM_TIMER() ON_WM_DESTROY() //}}AFX_MSG_MAP END_MESSAGE_MAP() // // Message Handlers // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< BOOL CShutProgressDlg::OnInitDialog() /*++ Routine Description: WM_INITDIALOG handler. Initialize the dialog. Arguments: None. Return Value: TRUE if no focus is to be set automatically, FALSE if the focus is already set. --*/ { CDialog::OnInitDialog(); VERIFY(m_strProgress.LoadString(IDS_SHUTDOWN_PROGRESS)); m_nProgress = 0; m_prgShutdown.SetRange32(0, m_uTimeoutSec); m_prgShutdown.SetPos(m_nProgress); m_prgShutdown.SetStep(1); if (!m_bShowCancel) { GetDlgItem(IDOK)->EnableWindow(FALSE); GetDlgItem(IDOK)->ShowWindow(SW_HIDE); GetDlgItem(IDC_STATIC_STATUS)->EnableWindow(FALSE); GetDlgItem(IDC_STATIC_STATUS)->ShowWindow(SW_HIDE); } // // Start the progress bar ticking, once per second. // UINT_PTR nID = SetTimer(ID_TIMER, A_SECOND, NULL); if (nID != ID_TIMER) { // // Failed to create the timer. Pop up an error, and // cancel the dialog. // CError err(ERROR_NO_SYSTEM_RESOURCES); err.MessageBox(m_hWnd); EndDialog(IDCANCEL); } return TRUE; } void CShutProgressDlg::OnTimer( IN UINT nIDEvent ) /*++ Routine Description: Timer handler. Upgrade the progressbar with another second on the clock Arguments: UINT nIDEvent : Timer id Return Value: None --*/ { ASSERT(nIDEvent == ID_TIMER); m_prgShutdown.SetPos(++m_nProgress); // // Display progress on the tick marker // CString str; str.Format(m_strProgress, m_uTimeoutSec - (UINT)m_nProgress + 1); m_static_ProgressMsg.SetWindowText(str); // // Check to see if the stop thread has finished its action already // BOOL fFinished; EnterCriticalSection(&m_pCommand->cs); fFinished = m_pCommand->fFinished; LeaveCriticalSection(&m_pCommand->cs); if (fFinished) { // // The worker thread has finished, so there's no reason to // keep the user in suspense -- dismiss the dialog // EndDialog(IDCANCEL); } if ((UINT)m_nProgress > m_uTimeoutSec) { // // We've timed out -- tell the main thread to Kill!() // OnOK(); } // // I doubt there's any default processing here, but anyway... // CDialog::OnTimer(nIDEvent); } void CShutProgressDlg::OnDestroy() /*++ Routine Description: Handle dialog destruction, kill the timer. Arguments: None Return Value: None --*/ { CDialog::OnDestroy(); ::KillTimer(m_hWnd, (UINT_PTR)ID_TIMER); } void CShutProgressDlg::OnOK() /*++ Routine Description: OK handler -- ok button maps to "Kill Now!" Arguments: None Return Value: None --*/ { // // Kill! // EndDialog(IDOK); } void CShutProgressDlg::OnCancel() /*++ Routine Description: Cancel button handler. This dialog cannot be cancelled, so the cancel notification is eaten. Arguments: None Return Value: None --*/ { // // Eat cancel command (user pressed escape) // } // // Shutdown dialog // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< CIISShutdownDlg::CIISShutdownDlg( IN CIISMachine * pMachine, IN CWnd * pParent OPTIONAL ) /*++ Routine Description: Constructor Arguments: CIISMachine * pMachine : Machine object CWnd * pParent : Optional parent window Return Value: N/A --*/ : CDialog(CIISShutdownDlg::IDD, pParent), m_fServicesRestarted(FALSE), m_pMachine(pMachine) { //{{AFX_DATA_INIT(CIISShutdownDlg) //}}AFX_DATA_INIT ASSERT_PTR(m_pMachine); } void CIISShutdownDlg::DoDataExchange( IN CDataExchange * pDX ) /*++ Routine Description: Initialise/Store control data Arguments: CDataExchange * pDX - DDX/DDV control structure Return Value: None --*/ { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CIISShutdownDlg) DDX_Control(pDX, IDC_COMBO_RESTART, m_combo_Restart); DDX_Control(pDX, IDC_STATIC_DETAILS, m_static_Details); //}}AFX_DATA_MAP } void CIISShutdownDlg::SetDetailsText() /*++ Routine Description: Set the details text to correspond to what's in the combo box Arguments: None Return Value: None --*/ { UINT nSel = m_combo_Restart.GetCurSel(); // ASSERT(nSel >= 0 && nSel < NUM_ISC_ITEMS); m_static_Details.SetWindowText(m_strDetails[nSel]); } // // Message Map // BEGIN_MESSAGE_MAP(CIISShutdownDlg, CDialog) //{{AFX_MSG_MAP(CIISShutdownDlg) ON_CBN_SELCHANGE(IDC_COMBO_RESTART, OnSelchangeComboRestart) ON_CBN_DBLCLK(IDC_COMBO_RESTART, OnDblclkComboRestart) ON_BN_CLICKED(ID_HELP, OnHelp) //}}AFX_MSG_MAP END_MESSAGE_MAP() HRESULT CIISShutdownDlg::PerformCommand(int iCmd, BOOL bShowCancel) /*++ Routine Description: Perform restart command Arguments: int iCmd - One of the following commands: ISC_START ISC_STOP ISC_SHUTDOWN ISC_RESTART Return Value: HRESULT --*/ { // // Make sure the service is supported // ASSERT_PTR(m_pMachine); BeginWaitCursor(); CIISSvcControl isc(m_pMachine->QueryAuthInfo()); EndWaitCursor(); CError err(isc.QueryResult()); if (err.Failed()) { return err; } // // Create command structure to hand off to // worker thread // IISCOMMAND * pCommand = new IISCOMMAND; if (!pCommand) { err = ERROR_NOT_ENOUGH_MEMORY; return err; } ::ZeroMemory(pCommand, sizeof(IISCOMMAND)); pCommand->pMachine = m_pMachine; pCommand->dwMilliseconds = IIS_SHUTDOWN_TIMEOUT; pCommand->pParent = this; InitializeCriticalSection(&pCommand->cs); InitializeCriticalSection(&gcs); CShutProgressDlg dlg(pCommand, bShowCancel); CWinThread * pStopThread = NULL; BOOL fStartServices = FALSE; INT_PTR nReturn = IDCANCEL; // // Fire off the thread that does the actual work, while we // put up the progress UI // switch(iCmd) { case ISC_RESTART: ++fStartServices; // // Fall through... // case ISC_STOP: // // Stop the services in the workerthread // pStopThread = AfxBeginThread(&StopIISServices, pCommand); nReturn = dlg.DoModal(); break; case ISC_START: ++fStartServices; break; case ISC_SHUTDOWN: BeginWaitCursor(); err = isc.Reboot(IIS_SHUTDOWN_TIMEOUT, m_pMachine->IsLocal()); EndWaitCursor(); break; default: // // Internal error! // ASSERT_MSG("Invalid command code!"); err = ERROR_INVALID_FUNCTION; } // // Determine if a kill is necessary (timed-out or user // pressed 'Kill') // BeginWaitCursor(); if (nReturn == IDOK) { TRACEEOLID("Killing now!"); err = isc.Kill(); Sleep(1000L); } else { // // Waiting for the thread to finish // if (pStopThread != NULL) { BOOL fDone = FALSE; while(!fDone) { TRACEEOLID("Checking to see if thread has finished"); EnterCriticalSection(&pCommand->cs); if (pCommand->fFinished) { err = pCommand->hReturn; ++fDone; } LeaveCriticalSection(&pCommand->cs); // // Pause a bit to catch our breath. // if (!fDone) { Sleep(500); } } } } // // Everything should be stopped, start it up again // if necessary. // if (err.Succeeded() && fStartServices) { err = isc.Start(IIS_SHUTDOWN_TIMEOUT); m_fServicesRestarted = err.Succeeded(); } EndWaitCursor(); // // Clean up when the worker thread says we can. // EnterCriticalSection(&gcs); DeleteCriticalSection(&pCommand->cs); delete pCommand; LeaveCriticalSection(&gcs); DeleteCriticalSection(&gcs); return err; } // // Message Handlers // // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< BOOL CIISShutdownDlg::OnInitDialog() /*++ Routine Description: WM_INITDIALOG handler. Initialize the dialog. Arguments: None. Return Value: TRUE if no focus is to be set automatically, FALSE if the focus is already set. --*/ { CDialog::OnInitDialog(); // // Load combobox text and details // CString strFmt, str; // // This may take a second or two... // BeginWaitCursor(); CIISSvcControl isc(m_pMachine->QueryAuthInfo()); EndWaitCursor(); CError err(isc.QueryResult()); if (err.Failed()) { // // Failed to obtain interface -- quit now. // if (err.HResult() == REGDB_E_CLASSNOTREG || err.HResult() == CS_E_PACKAGE_NOTFOUND ) { // // Friendly message about the interface not being supported. // DoHelpMessageBox(m_hWnd,IDS_ERR_NO_SHUTDOWN, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0); } else { m_pMachine->DisplayError(err, m_hWnd); } EndDialog(IDCANCEL); } UINT nOption = IDS_IIS_START; UINT nDetails = IDS_IIS_START_DETAILS; for (int i = ISC_START; i <= ISC_RESTART; ++i) { VERIFY(strFmt.LoadString(nOption++)); str.Format(strFmt, m_pMachine->QueryServerName()); VERIFY(m_strDetails[i].LoadString(nDetails++)); m_combo_Restart.AddString(str); } m_combo_Restart.SetCurSel(ISC_RESTART); m_combo_Restart.SetFocus(); SetDetailsText(); return FALSE; } void CIISShutdownDlg::OnSelchangeComboRestart() /*++ Routine Description: Selection change notification handler. Change the text in the details static text to reflect the new selection in the combo box Arguments: None Return Value: None --*/ { SetDetailsText(); } void CIISShutdownDlg::OnDblclkComboRestart() /*++ Routine Description: Double-click notification handler. Maps to OK Arguments: None Return Value: None --*/ { // // Go with the current selection // OnOK(); } void CIISShutdownDlg::OnOK() /*++ Routine Description: "OK" button has been pressed, and perform the selected action. Arguments: None Return Value: None --*/ { int iCmd = m_combo_Restart.GetCurSel(); CError err = PerformCommand(iCmd); if (err.Failed()) { m_pMachine->DisplayError(err, m_hWnd); // // Failed -- do not dismiss the dialog // return; } // // No error, dismiss the dialog // CDialog::OnOK(); }