windows-nt/Source/XPSP1/NT/inetsrv/iis/admin/snapin/shutdown.cpp
2020-09-26 16:20:57 +08:00

855 lines
13 KiB
C++

/*++
Copyright (c) 1994-1999 Microsoft Corporation
Module Name :
shutdown.cpp
Abstract:
IIS Shutdown/restart dialog
Author:
Ronald Meijer (ronaldm)
Project:
Internet Services Manager
Revision History:
--*/
//
// Include Files
//
#include "stdafx.h"
#include "common.h"
#include "InetMgrApp.h"
#include "iisobj.h"
#include "shutdown.h"
//
// Shutdown in milliseconds
//
#define IIS_SHUTDOWN_TIMEOUT 30000L // 30 Ms
#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(
IN IISCOMMAND * pCommand
)
/*++
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_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);
//
// 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();
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(
IN int iCmd
)
/*++
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);
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.
//
::AfxMessageBox(IDS_ERR_NO_SHUTDOWN);
}
else
{
m_pMachine->DisplayError(err);
}
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);
//
// Failed -- do not dismiss the dialog
//
return;
}
//
// No error, dismiss the dialog
//
CDialog::OnOK();
}