windows-nt/Source/XPSP1/NT/admin/snapin/filemgmt/progress.cpp

1073 lines
31 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/////////////////////////////////////////////////////////////////////
//
// Progress.cpp
//
// Progress dialog to Start, Stop, Pause, Resume and Restart a service.
//
// IMPLEMENTATION
// Since an operation (ie, Start, Stop, Pause, Resume or Restart) may take
// a while, a thread is created to do the actual work while a dialog
// is displayed to the user.
// 0. Allocate CServiceControlProgress object on the heap
// 1. Create a thread in suspended mode.
// 2. Create the dialog.
// 3. Dialog creates a timer to update the progress bar.
// 4. Dialog resumes the thread.
// 5. Thread opens the service and perform the requested operation(s).
// 6. Dialog updates UI using its timer.
// 7. Thread updates dialog UI as well.
// 8. Thread waits until the dialog is dismissed. Dialog can be
// dismissed for any of the following event:
// a) Operation completed successfully.
// b) User hit cancel button.
// c) An unexpected error occured.
// d) Operation times out.
// 9 Thread deletes CServiceControlProgress object.
//
// HISTORY
// 03-Oct-95 t-danmo Creation of (sysmgmt\dsui\services\progress.cxx)
// 30-Sep-96 t-danmo Renamed and adapted to MMC.
// 14-May-97 t-danm Fully implemented the "Restart" feature.
//
#include "stdafx.h"
#include "progress.h"
/////////////////////////////////////////////////////////////////////////////
// This array represent the expected state of the service
// after carrying action, start, stop, pause or resume.
//
// To be compared SERVICE_STATUS.dwCurrentState.
//
const DWORD rgdwExpectedServiceStatus[4] =
{
SERVICE_RUNNING, // Service should be running after a 'start'
SERVICE_STOPPED, // Service should be stopped after a 'stop'
SERVICE_PAUSED, // Service should be paused after a 'pause'
SERVICE_RUNNING, // Service should be running after a 'resume'
};
/////////////////////////////////////////////////////////////////////////////
CServiceControlProgress::CServiceControlProgress()
{
// Using ZeroMemory() is safe as long as CServiceControlProgress
// is not derived from any other object and does not contains
// any objects with constructors.
::ZeroMemory(this, sizeof(*this));
}
/////////////////////////////////////////////////////////////////////////////
CServiceControlProgress::~CServiceControlProgress()
{
delete m_pargDependentServicesT;
delete m_pargServiceStop;
}
/////////////////////////////////////////////////////////////////////////////
// M_FInit()
//
// Initialize the object.
// - Copy all input parameters
// - Load the clock bitmap(s)
// Return TRUE if successful, otherwise FALSE
//
BOOL
CServiceControlProgress::M_FInit(
HWND hwndParent, // IN: Parent of the dialog
SC_HANDLE hScManager, // IN: Handle to service control manager database
LPCTSTR pszMachineName, // IN: Machine name to display to the user
LPCTSTR pszServiceName, // IN: Name of the service
LPCTSTR pszServiceDisplayName) // IN: Display name of the service
{
Assert(IsWindow(hwndParent));
Assert(hScManager != NULL);
Assert(pszServiceName != NULL);
Assert(pszServiceDisplayName != NULL);
m_hWndParent = hwndParent;
m_hScManager = hScManager;
lstrcpy(OUT m_szUiMachineName, (pszMachineName && pszMachineName[0])
? pszMachineName : (LPCTSTR)g_strLocalMachine);
lstrcpy(OUT m_szServiceName, pszServiceName);
lstrcpy(OUT m_szServiceDisplayName, pszServiceDisplayName);
return TRUE;
} // M_FInit()
/////////////////////////////////////////////////////////////////////
// M_FDlgStopDependentServices()
//
// Check if the services has dependent services that must be stopped
// before stopping the current service.
//
// If there are any dependent services, the function will display
// a dialog asking the user to confirm he/she wants also to stop
// all the dependent services.
//
// This function return FALSE ONLY IF the user click on the cancel button
// otherwise TRUE. If there are no dependent services, or an error occurs
// while reading dependent services, the function will return TRUE.
//
BOOL
CServiceControlProgress::M_FDlgStopDependentServices()
{
Assert(m_hScManager != NULL);
SC_HANDLE hService = NULL;
BOOL fSuccess = TRUE;
DWORD cbBytesNeeded = 0;
DWORD dwServicesReturned = 0;
m_cDependentServices = 0; // So far we have no dependent services
delete m_pargServiceStop;
m_pargServiceStop = NULL;
{
AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // required for CWaitCursor
CWaitCursor wait;
hService = ::OpenService(m_hScManager, m_szServiceName, SERVICE_ENUMERATE_DEPENDENTS);
}
if (hService == NULL)
{
TRACE1("M_FDlgStopDependentServices() - Unable to enumerate service dependencies for service %s.\n",
m_szServiceName);
goto End;
}
// Find out how many bytes are needed to enumerate the dependent services
fSuccess = ::EnumDependentServices(
hService,
SERVICE_ACTIVE, // Enumerate only the active services
NULL,
0,
OUT &cbBytesNeeded,
OUT &dwServicesReturned);
if (cbBytesNeeded == 0)
{
// Service does not have any dependencies
goto End;
}
Assert(fSuccess == FALSE);
Report(GetLastError() == ERROR_MORE_DATA); // Error should be 'more data'
Assert(dwServicesReturned == 0);
cbBytesNeeded += 1000; // Add extra bytes (just in case)
delete m_pargDependentServicesT; // Free previously allocated memory (if any)
m_pargDependentServicesT = (LPENUM_SERVICE_STATUS) new BYTE[cbBytesNeeded];
// Query the database for the dependent services
fSuccess = ::EnumDependentServices(
hService,
SERVICE_ACTIVE, // Enumerate only the active services
OUT m_pargDependentServicesT,
cbBytesNeeded,
OUT IGNORED &cbBytesNeeded,
OUT &dwServicesReturned);
Report(fSuccess != FALSE);
Report(dwServicesReturned > 0);
m_cDependentServices = dwServicesReturned;
if (m_cDependentServices > 0)
{
// Allocate an array to hold all the dependent services
m_pargServiceStop = new ENUM_SERVICE_STATUS[m_cDependentServices + 1];
memcpy(OUT m_pargServiceStop, m_pargDependentServicesT,
m_cDependentServices * sizeof(ENUM_SERVICE_STATUS));
m_pargServiceStop[m_cDependentServices].lpServiceName = m_szServiceName;
m_pargServiceStop[m_cDependentServices].lpDisplayName = m_szServiceDisplayName;
INT_PTR nReturn = ::DialogBoxParam(
g_hInstanceSave,
MAKEINTRESOURCE(IDD_SERVICE_STOP_DEPENDENCIES),
m_hWndParent,
(DLGPROC)&S_DlgProcDependentServices,
reinterpret_cast<LPARAM>(this));
Report(nReturn != -1);
if (0 == nReturn) // user chose Cancel
fSuccess = FALSE;
} // if
End:
if (NULL != hService)
{
VERIFY(::CloseServiceHandle(hService));
}
return fSuccess;
} // M_FDlgStopDependentServices()
/////////////////////////////////////////////////////////////////////
// M_DoExecuteServiceThread()
//
// Run a background thread while a foreground dialog is
// displayed to the user.
// This routine synchronizes the background thread with the main thread.
//
// If an error occurs, the routine will display a message box of the
// error encountered.
//
// Return the error code from GetLastError() if an error occured.
//
APIERR
CServiceControlProgress::M_EDoExecuteServiceThread(void * pThreadProc)
{
Assert(pThreadProc != NULL);
Assert(m_hService == NULL);
Assert(m_hThread == NULL);
m_hEvent = ::CreateEvent(
NULL,
FALSE,
FALSE,
NULL);
Report(m_hEvent != NULL);
// Create a thread in suspended mode
m_hThread = ::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)pThreadProc,
this,
CREATE_SUSPENDED,
NULL);
Report(m_hThread != NULL);
// Display the dialog box, the dialog will resume the suspended thread
(void)::DialogBoxParam(
g_hInstanceSave,
MAKEINTRESOURCE(IDD),
m_hWndParent,
(DLGPROC)&S_DlgProcControlService,
reinterpret_cast<LPARAM>(this));
// Display an error message to the user (if an error occured);
M_ProcessErrorCode();
// Make a copy of the last error code
APIERR dwLastError = m_dwLastError;
// Indicate the thread is allowed to terminate and delete the 'this' pointer
VERIFY(SetEvent(m_hEvent));
// 'this' pointer cannot longer be assumed to be valid
return dwLastError;
} // M_EDoExecuteServiceThread()
/////////////////////////////////////////////////////////////////////
// M_ProcessErrorCode()
//
// Query the service status one last time to get its exit code,
// examine the content of member m_dwLastError and display an
// error message if an error occured.
//
void
CServiceControlProgress::M_ProcessErrorCode()
{
SERVICE_STATUS ss;
::ZeroMemory( &ss, sizeof(ss) );
if (m_hService != NULL)
{
// Query the service status again to get its Win32ExitCode
if (!::QueryServiceStatus(m_hService, OUT &ss))
{
TRACE3("QueryServiceStatus(%s [hService=%p]) failed. err=%u.\n",
m_szServiceName, m_hService, GetLastError());
m_dwLastError = GetLastError();
}
else
{
if (ss.dwWin32ExitCode != ERROR_SUCCESS)
m_dwLastError = ss.dwWin32ExitCode;
}
} // if
APIERR dwLastError = m_dwLastError;
UINT uIdString = IDS_MSG_sss_UNABLE_TO_START_SERVICE;
TCHAR szMessageExtra[512];
szMessageExtra[0] = _T('\0');
switch (dwLastError)
{
case ERROR_SUCCESS:
if (ss.dwCurrentState == rgdwExpectedServiceStatus[m_iServiceAction])
{
// The service status is consistent with the expected service status
uIdString = 0;
}
else
{
// We got a problem here, the service did not return an error
// but did not behave as expected
//
// JonN 12/3/99 418111 If the service stopped automatically,
// don't make such a fuss
//
if (SERVICE_RUNNING == rgdwExpectedServiceStatus[m_iServiceAction]
&& ( ss.dwCurrentState == SERVICE_STOPPED
|| ss.dwCurrentState == SERVICE_STOP_PENDING))
{
uIdString = IDS_MSG_sss_SERVICE_STOPPED_AUTOMATICALLY;
break;
}
::LoadString(g_hInstanceSave, IDS_MSG_INTERNAL_ERROR,
OUT szMessageExtra, LENGTH(szMessageExtra));
Assert(lstrlen(szMessageExtra) > 0);
}
break;
case errUserCancelStopDependentServices:
case errUserAbort:
// Do not report this 'error' to the user
uIdString = 0;
break;
case ERROR_SERVICE_SPECIFIC_ERROR:
dwLastError = ss.dwServiceSpecificExitCode;
uIdString = IDS_MSG_ssd_SERVSPECIFIC_START_SERVICE;
// 341363 JonN 6/1/99: no point in loading this string as if it were
// a Win32 error
//::LoadString(g_hInstanceSave, IDS_MSG_SPECIFIC_ERROR,
// OUT szMessageExtra, LENGTH(szMessageExtra));
// Assert(lstrlen(szMessageExtra) > 0);
break;
} // switch
if (uIdString != 0)
{
if (uIdString == IDS_MSG_ssd_SERVSPECIFIC_START_SERVICE)
{
DoServicesErrMsgBox(
::GetActiveWindow(),
MB_OK | MB_ICONEXCLAMATION,
0,
uIdString + m_iServiceAction,
m_szServiceDisplayName,
m_szUiMachineName,
dwLastError);
}
else
{
DoServicesErrMsgBox(
::GetActiveWindow(),
MB_OK | MB_ICONEXCLAMATION,
dwLastError,
uIdString + m_iServiceAction,
m_szServiceDisplayName,
m_szUiMachineName,
szMessageExtra);
}
}
} // M_ProcessErrorCode()
/////////////////////////////////////////////////////////////////////
// M_DoThreadCleanup()
//
// Routine that synchronize the background thread with the dialog and
// perform cleanup tasks.
// This routine delete the 'this' pointer when done.
//
void
CServiceControlProgress::M_DoThreadCleanup()
{
TRACE1("CServiceControlProgress::M_DoThreadCleanup() - Waiting for event 0x%p...\n", m_hEvent);
Assert(m_hEvent != NULL);
// Wait for the the dialog box to be gone
::WaitForSingleObject(m_hEvent, INFINITE);
VERIFY(::CloseHandle(m_hEvent));
// Close the service handle opened by the thread
if (m_hService != NULL)
{
if (!::CloseServiceHandle(m_hService))
{
TRACE3("CloseServiceHandle(%s [hService=%p]) failed. err=%u.\n",
m_szServiceName, m_hService, GetLastError());
}
} // if
VERIFY(::CloseHandle(m_hThread));
delete this; // We are done with the object
} // M_DoThreadCleanup()
/////////////////////////////////////////////////////////////////////
// M_EControlService()
//
// This function is just there to initialize variables to
// perform a stop, pause, resume or restart operation.
//
APIERR
CServiceControlProgress::M_EControlService(DWORD dwControlCode)
{
Assert(m_fRestartService == FALSE);
APIERR err = 0;
m_dwControlCode = dwControlCode;
switch (dwControlCode)
{
default:
Assert(FALSE && "CServiceControlProgress::M_EControlService() - Unknown control code.");
break;
case SERVICE_CONTROL_RESTART:
m_dwControlCode = SERVICE_CONTROL_STOP;
m_fRestartService = TRUE;
// Fall Through //
case SERVICE_CONTROL_STOP:
m_dwDesiredAccess = SERVICE_STOP | SERVICE_QUERY_STATUS;
m_dwQueryState = SERVICE_STOP_PENDING;
m_iServiceAction = iServiceActionStop;
if (!M_FDlgStopDependentServices())
{
// User changed its mind by pressing the 'Cancel' button
err = errUserCancelStopDependentServices;
}
else
{
// Stop the services (including dependent services)
err = M_EDoExecuteServiceThread(S_ThreadProcStopService);
}
break;
case SERVICE_CONTROL_PAUSE:
m_dwDesiredAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS;
m_dwQueryState = SERVICE_PAUSE_PENDING;
m_iServiceAction = iServiceActionPause;
err = M_EDoExecuteServiceThread(S_ThreadProcPauseResumeService);
break;
case SERVICE_CONTROL_CONTINUE:
m_dwDesiredAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS;
m_dwQueryState = SERVICE_CONTINUE_PENDING;
m_iServiceAction = iServiceActionResume;
err = M_EDoExecuteServiceThread(S_ThreadProcPauseResumeService);
break;
} // switch
return err;
} // M_EControlService()
/////////////////////////////////////////////////////////////////////
// M_QueryCurrentServiceState()
//
// Simply call the API ::QueryServiceStatus() and return dwCurrentState
// of the SERVICE_STATUS structure.
//
// RETURNS
// Function return the current state of a service:
// SERVICE_STOPPED The service is not running.
// SERVICE_START_PENDING The service is starting.
// SERVICE_STOP_PENDING The service is stopping.
// SERVICE_RUNNING The service is running.
// SERVICE_CONTINUE_PENDING The service continue is pending.
// SERVICE_PAUSE_PENDING The service pause is pending.
// SERVICE_PAUSED The service is paused.
//
// If an error occurs, the function will return SERVICE_STOPPED.
//
DWORD
CServiceControlProgress::M_QueryCurrentServiceState()
{
BOOL fRet;
SERVICE_STATUS ss;
Assert(m_hService != NULL);
if (m_hService == NULL) // Just in case
{
return SERVICE_STOPPED;
}
// Query the service status
fRet = ::QueryServiceStatus(m_hService, OUT &ss);
if (!fRet)
{
TRACE2("CServiceControlProgress::M_QueryCurrentServiceState() - ::QueryServiceStatus(%s) failed. err=%u\n",
m_szServiceName, GetLastError());
Assert(GetLastError() != ERROR_SUCCESS);
m_dwLastError = GetLastError();
return SERVICE_STOPPED;
}
return ss.dwCurrentState;
} // M_QueryCurrentServiceState()
/////////////////////////////////////////////////////////////////////
// Query the service database to get the friendly name and
// display it into the dialog.
//
void CServiceControlProgress::M_UpdateDialogUI(LPCTSTR pszDisplayName)
{
Assert(pszDisplayName != NULL);
if (m_hctlActionMsg == NULL || m_hctlServiceNameMsg == NULL)
return;
Assert(IsWindow(m_hctlActionMsg) && IsWindow(m_hctlServiceNameMsg));
SetWindowTextPrintf(
m_hctlActionMsg,
IDS_SVC_ss_SERVICE_STARTING + m_iServiceAction,
pszDisplayName,
m_szUiMachineName);
SetWindowText( m_hctlServiceNameMsg, pszDisplayName );
} // M_UpdateDialogUI()
/////////////////////////////////////////////////////////////////////
// Routine to iterate through the dependent services to stop.
//
// Return the the service name to stop, increment the 'pointer' to the
// next service. The routine returns FALSE if there are no remaining
// services to stop.
//
// REMARKS
// This routine is also used to restart dependent services.
//
BOOL
CServiceControlProgress::M_FGetNextService(
OUT LPCTSTR * ppszServiceName,
OUT LPCTSTR * ppszDisplayName)
{
Assert(ppszServiceName != NULL);
Assert(ppszDisplayName != NULL);
Assert(m_iServiceAction == iServiceActionStop || m_iServiceAction == iServiceActionStart);
int iDependentService = m_iDependentServiceIter;
if (m_iServiceAction == iServiceActionStop)
m_iDependentServiceIter++;
else
m_iDependentServiceIter--;
if (m_pargServiceStop != NULL
&& iDependentService >= 0
&& iDependentService <= m_cDependentServices)
{
*ppszServiceName = m_pargServiceStop[iDependentService].lpServiceName;
*ppszDisplayName = m_pargServiceStop[iDependentService].lpDisplayName;
}
else
{
*ppszServiceName = m_szServiceName;
*ppszDisplayName = m_szServiceDisplayName;
}
return (m_iDependentServiceIter >= 0 && m_iDependentServiceIter <= m_cDependentServices);
} // M_FGetNextService()
/////////////////////////////////////////////////////////////////////
// Thread to start one (or more) services.
DWORD
CServiceControlProgress::S_ThreadProcStartService(CServiceControlProgress * pThis)
{
BOOL fSuccess = FALSE;
SC_HANDLE hService;
Assert(pThis != NULL);
Assert(pThis->m_hScManager != NULL);
Assert(pThis->m_hService == NULL);
Endorse(pThis->m_fPulseEvent == TRUE); // We are starting multiple services
Endorse(pThis->m_fPulseEvent == FALSE); // Wa are starting only one service
if (pThis->m_dwLastError != ERROR_SUCCESS)
{
// If there is already an error, it is because we attempted to previously stop a service
Assert(pThis->m_fRestartService == TRUE);
goto Done;
}
while (TRUE)
{
LPCTSTR pszServiceName;
LPCTSTR pszServiceDisplayName;
UINT cServicesRemaining = pThis->M_FGetNextService(
OUT &pszServiceName,
OUT &pszServiceDisplayName);
pThis->M_UpdateDialogUI(pszServiceDisplayName);
// Sleep(5000); // Debug
// Open service to allow a 'start' operation
hService = ::OpenService(
pThis->m_hScManager,
pszServiceName,
SERVICE_START | SERVICE_QUERY_STATUS);
if (hService == NULL)
{
pThis->m_dwLastError = GetLastError();
TRACE2("ERR: S_ThreadProcStartService(): Unable to open service %s to start. err=%u.\n",
pszServiceName, pThis->m_dwLastError);
break;
}
fSuccess = ::StartService(
hService,
pThis->m_dwNumServiceArgs,
pThis->m_lpServiceArgVectors);
if (!fSuccess)
{
APIERR err = GetLastError();
if (ERROR_SERVICE_ALREADY_RUNNING != err)
{
pThis->m_dwLastError = err;
TRACE2("ERR: S_ThreadProcStartService(): StartService(%s) returned err=%u.\n",
pszServiceName, pThis->m_dwLastError);
break;
}
}
Assert(pThis->m_hService == NULL);
if (cServicesRemaining == 0)
{
// This was our last service to start
pThis->m_fPulseEvent = FALSE;
pThis->m_hService = hService;
break;
}
Assert(pThis->m_fPulseEvent == TRUE);
pThis->m_hService = hService;
// Wait until the service was actually 'started'
WaitForSingleObject(pThis->m_hEvent, INFINITE);
pThis->m_hService = NULL;
Assert(hService != NULL);
VERIFY(::CloseServiceHandle(hService));
} // while
Done:
pThis->M_DoThreadCleanup();
return 0;
} // S_ThreadProcStartService()
/////////////////////////////////////////////////////////////////////
// Thread to stop one (or more) services.
DWORD
CServiceControlProgress::S_ThreadProcStopService(CServiceControlProgress * pThis)
{
BOOL fSuccess;
SC_HANDLE hService;
Assert(pThis != NULL);
Assert(pThis->m_hScManager != NULL);
Assert(pThis->m_dwDesiredAccess & SERVICE_STOP);
Assert(pThis->m_hService == NULL);
Assert(pThis->m_fPulseEvent == FALSE);
//
// Stop the dependent services
//
while (TRUE)
{
LPCTSTR pszServiceName;
LPCTSTR pszServiceDisplayName;
UINT cServicesRemaining = pThis->M_FGetNextService(
OUT &pszServiceName,
OUT &pszServiceDisplayName);
pThis->M_UpdateDialogUI(pszServiceDisplayName);
// Sleep(5000); // Debug
hService = ::OpenService(
pThis->m_hScManager,
pszServiceName,
pThis->m_dwDesiredAccess);
if (hService == NULL)
{
pThis->m_dwLastError = GetLastError();
TRACE2("ERR: S_ThreadProcStopService(): Unable to open dependent service %s to stop. err=%u.\n",
pszServiceName, pThis->m_dwLastError);
break;
}
SERVICE_STATUS ss; // Ignored
fSuccess = ::ControlService(
hService,
pThis->m_dwControlCode,
OUT IGNORED &ss);
if (!fSuccess)
{
APIERR err = GetLastError();
if (ERROR_SERVICE_NOT_ACTIVE != err)
{
TRACE2("ERR: S_ThreadProcStopService(): ControlService(%s) returned err=%u.\n",
pszServiceName, pThis->m_dwLastError);
break;
}
}
Assert(pThis->m_hService == NULL);
if (cServicesRemaining == 0 && !pThis->m_fRestartService)
{
// This was our last service to stop
pThis->m_fPulseEvent = FALSE;
pThis->m_hService = hService;
break; // We are done
}
else
{
pThis->m_fPulseEvent = TRUE;
pThis->m_hService = hService;
}
// Wait until the service was actually 'stopped'
WaitForSingleObject(pThis->m_hEvent, INFINITE);
pThis->m_hService = NULL;
Assert(hService != NULL);
VERIFY(::CloseServiceHandle(hService));
if (cServicesRemaining == 0)
{
Assert(pThis->m_fRestartService == TRUE);
Assert(pThis->m_fPulseEvent == TRUE);
// Start the service
Assert(pThis->m_dwNumServiceArgs == 0);
Assert(pThis->m_lpServiceArgVectors == NULL);
pThis->m_iDependentServiceIter = pThis->m_cDependentServices; // Rewind the service iterator
pThis->m_dwQueryState = SERVICE_START_PENDING;
pThis->m_iServiceAction = iServiceActionStart;
(void)S_ThreadProcStartService(pThis);
return 0;
}
} // while
pThis->M_DoThreadCleanup();
return 0;
} // S_ThreadProcStopService()
/////////////////////////////////////////////////////////////////////
// Thread to pause or resume the service
DWORD CServiceControlProgress::S_ThreadProcPauseResumeService(CServiceControlProgress * pThis)
{
BOOL fSuccess;
SC_HANDLE hService;
SERVICE_STATUS ss;
Assert(pThis != NULL);
Assert(pThis->m_hScManager != NULL);
Assert(pThis->m_dwDesiredAccess & SERVICE_PAUSE_CONTINUE);
Assert(pThis->m_hService == NULL);
Assert(pThis->m_fPulseEvent == FALSE);
pThis->M_UpdateDialogUI(pThis->m_szServiceDisplayName);
// Open service to allow a 'pause' or 'resume' operation
hService = ::OpenService(
pThis->m_hScManager,
pThis->m_szServiceName,
pThis->m_dwDesiredAccess);
if (hService == NULL)
{
pThis->m_dwLastError = GetLastError();
TRACE2("ERR: S_ThreadProcPauseResumeService(): Unable to open service %s. err=%u.\n",
pThis->m_szServiceName, pThis->m_dwLastError);
goto Done;
}
fSuccess = ::ControlService(
hService,
pThis->m_dwControlCode,
OUT IGNORED &ss);
if (!fSuccess)
{
pThis->m_dwLastError = GetLastError();
TRACE2("ERR: S_ThreadProcPauseResumeService(): ControlService(%s) returned err=%u.\n",
pThis->m_szServiceName, pThis->m_dwLastError);
}
Assert(pThis->m_hService == NULL);
pThis->m_hService = hService;
Done:
pThis->M_DoThreadCleanup();
return 0;
} // S_ThreadProcPauseResumeService()
/////////////////////////////////////////////////////////////////////
// S_DlgProcControlService()
//
// Dialog proc for the clock dialog
// - Respond to a WM_TIMER message to update the clock bitmap while
// waiting for the operation to complete.
//
BOOL CALLBACK
CServiceControlProgress::S_DlgProcControlService(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CServiceControlProgress * pThis;
pThis = (CServiceControlProgress*)GetWindowLongPtr(hdlg, DWLP_USER);
switch (uMsg)
{
case WM_INITDIALOG:
pThis = reinterpret_cast<CServiceControlProgress *>(lParam);
Assert(pThis != NULL);
pThis->M_OnInitDialog(hdlg);
break;
case WM_TIMER:
Assert(pThis != NULL);
Assert(wParam == pThis->m_uTimerId);
pThis->M_OnTimer(hdlg);
break;
case WM_COMMAND:
Assert(wParam == IDOK || wParam == IDCANCEL);
if (wParam == IDCANCEL)
{
if ((HWND)lParam != NULL)
{
TRACE1("INFO: User cancelled dialog. dwLastError=%u.\n",
pThis->m_dwLastError);
pThis->m_dwLastError = errUserAbort;
}
EndDialog(hdlg, FALSE);
}
else
{
Assert(IsWindow(pThis->m_hctlProgress));
SendMessage(pThis->m_hctlProgress, PBM_SETPOS, dwTimerProgressDone * 2, 0);
Sleep(150);
EndDialog (hdlg, TRUE);
}
break;
case WM_DESTROY:
Assert(IsWindow(pThis->m_hctlActionMsg) && IsWindow(pThis->m_hctlServiceNameMsg));
pThis->m_hctlActionMsg = NULL;
pThis->m_hctlServiceNameMsg = NULL;
if (pThis->m_uTimerId != 0)
{
VERIFY(KillTimer(hdlg, pThis->m_uTimerId));
}
break;
default:
return FALSE;
} // switch (uMsg)
return (TRUE);
} // S_DlgProcControlService()
/////////////////////////////////////////////////////////////////////
void
CServiceControlProgress::M_OnInitDialog(HWND hdlg)
{
Assert(IsWindow(hdlg));
SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(this));
m_hctlActionMsg = HGetDlgItem(hdlg, IDC_STATIC_ACTION_MSG);
m_hctlServiceNameMsg = HGetDlgItem(hdlg, IDC_STATIC_SERVICENAME_MSG);
m_hctlProgress = HGetDlgItem(hdlg, IDC_PROGRESS);
SendMessage(m_hctlProgress, PBM_SETRANGE, 0, MAKELPARAM(0, dwTimerProgressDone * 2));
Assert(m_uTimerId == 0);
Assert(m_dwTimerTicks == 0);
m_uTimerId = SetTimer(hdlg, ID_TIMER, dwTimerTickIncrement, NULL);
Assert(m_hThread != NULL);
::ResumeThread(m_hThread);
if (m_uTimerId == 0)
{
Report(FALSE && "Unable to create timer. Dialog will be destroyed.");
PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
}
} // M_OnInitDialog()
/////////////////////////////////////////////////////////////////////
void
CServiceControlProgress::M_OnTimer(HWND hdlg)
{
Assert(IsWindow(m_hctlActionMsg) && IsWindow(m_hctlServiceNameMsg));
m_dwTimerTicks += dwTimerTickIncrement;
if (m_dwLastError != ERROR_SUCCESS)
{
TRACE1("CServiceControlProgress::M_OnTimer() - dwLastError=%u.\n", m_dwLastError);
PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
return;
}
if (m_dwTimerTicks > dwTimerTimeout)
{
VERIFY(KillTimer(hdlg, m_uTimerId));
m_uTimerId = 0;
m_dwLastError = ERROR_SERVICE_REQUEST_TIMEOUT;
TRACE0("CServiceControlProgress::M_OnTimer() - Time out.\n");
PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
return;
}
if ((m_hService != NULL) && (m_dwTimerTicks >= 900))
{
// If the current state of the service changed (ie, operation completed)
// we can dismiss the dialog
if (m_dwQueryState != M_QueryCurrentServiceState())
{
if (m_fPulseEvent)
{
m_dwTimerTicks = 0; // Reset the time-out counter
Assert(m_hEvent != NULL);
PulseEvent(m_hEvent);
SendMessage(m_hctlProgress, PBM_SETPOS, dwTimerProgressDone * 2, 0);
Sleep(100);
}
else
{
PostMessage(hdlg, WM_COMMAND, IDOK, 0);
}
}
} // if
// Advance the current position of the progress bar by the increment.
Assert(IsWindow(m_hctlProgress));
DWORD dwPos = m_dwTimerTicks;
if(dwPos > dwTimerProgressDone)
{
dwPos -= dwTimerProgressDone;
dwPos = (dwPos * dwTimerProgressDone) / dwTimerTimeout;
dwPos += dwTimerProgressDone;
}
SendMessage(m_hctlProgress, PBM_SETPOS, dwPos, 0);
} // M_OnTimer()
/////////////////////////////////////////////////////////////////////
BOOL CALLBACK
CServiceControlProgress::S_DlgProcDependentServices(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CServiceControlProgress * pThis;
HWND hwndListbox;
INT i;
switch (uMsg)
{
case WM_INITDIALOG:
pThis = reinterpret_cast<CServiceControlProgress *>(lParam);
Assert(pThis != NULL);
SetWindowTextPrintf( ::GetDlgItem(hdlg, IDC_STATIC_STOP_SERVICES),
(pThis->m_fRestartService)
? IDS_SVC_s_RESTART_DEPENDENT_SERVICES
: IDS_SVC_s_STOP_DEPENDENT_SERVICES,
pThis->m_szServiceDisplayName);
if (pThis->m_fRestartService)
{
// Set the window caption
SetWindowTextPrintf(hdlg, IDS_SVC_RESTART_DEPENDENT_CAPTION);
SetWindowTextPrintf(::GetDlgItem(hdlg, IDC_STATIC_STOP_SERVICES_QUERY),
IDS_SVC_RESTART_DEPENDENT_QUERY);
}
// Fill in the listbox with dependent services
hwndListbox = HGetDlgItem(hdlg, IDC_LIST_SERVICES);
Assert(pThis->m_pargDependentServicesT != NULL);
for (i = 0; i < pThis->m_cDependentServices; i++)
{
SendMessage(hwndListbox, LB_ADDSTRING, 0,
(LPARAM)pThis->m_pargDependentServicesT[i].lpDisplayName);
}
break;
case WM_COMMAND:
switch (wParam)
{
case IDOK:
EndDialog(hdlg, TRUE);
break;
case IDCANCEL:
EndDialog(hdlg, FALSE);
break;
}
break;
case WM_CONTEXTMENU: // right mouse click
DoContextHelp(wParam, HELP_DIALOG_TOPIC(IDD_SERVICE_STOP_DEPENDENCIES));
break;
case WM_HELP:
DoHelp(lParam, HELP_DIALOG_TOPIC(IDD_SERVICE_STOP_DEPENDENCIES));
break;
default:
return FALSE;
} // switch (uMsg)
return TRUE;
} // S_DlgProcDependentServices()
/////////////////////////////////////////////////////////////////////
// S_EStartService()
//
// Starts the execution of a service synchronously. The function will wait
// until the service is fully started and/or failed to start.
//
// A clock dialog will appear indicating the progress of the operation.
//
// Return ERROR_SUCCESS if syccessful, otherwise return the error code
// from GetLastError().
//
APIERR
CServiceControlProgress::S_EStartService(
HWND hwndParent, // IN: Parent of the dialog
SC_HANDLE hScManager, // IN: Handle to service control manager database
LPCTSTR pszMachineName, // IN: Machine name to display to the user
LPCTSTR pszServiceName, // IN: Name of the service to start
LPCTSTR pszServiceDisplayName, // IN: Display name of the service to start
DWORD dwNumServiceArgs, // IN: Number of arguments
LPCTSTR * lpServiceArgVectors) // IN: Address of array of argument string pointers
{
CServiceControlProgress * pThis;
pThis = new CServiceControlProgress;
Assert(pThis->m_dwLastError == ERROR_SUCCESS); // No error yet
if (!pThis->M_FInit(
hwndParent,
hScManager,
pszMachineName,
pszServiceName,
pszServiceDisplayName))
{
delete pThis;
return errCannotInitialize;
}
pThis->m_dwNumServiceArgs = dwNumServiceArgs;
pThis->m_lpServiceArgVectors = lpServiceArgVectors;
pThis->m_dwQueryState = SERVICE_START_PENDING;
pThis->m_iServiceAction = iServiceActionStart;
return pThis->M_EDoExecuteServiceThread(S_ThreadProcStartService);
} // S_EStartService()
/////////////////////////////////////////////////////////////////////
// S_EControlService()
//
// Control the execution of a service synchronously. The function is similar
// to EStartService() but use for Stop, Pause or Resume a service.
//
// A clock dialog will appear indicating the progress of the operation.
//
// Return ERROR_SUCCESS if syccessful, otherwise return the error code
// from GetLastError().
//
APIERR
CServiceControlProgress::S_EControlService(
HWND hwndParent, // IN: Parent of the dialog
SC_HANDLE hScManager, // IN: Handle to service control manager database
LPCTSTR pszMachineName, // IN: Machine name to display to the user
LPCTSTR pszServiceName, // IN: Name of the service to start
LPCTSTR pszServiceDisplayName, // IN: Display name of the service to start
DWORD dwControlCode) // IN: Control code. (SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE or SERVICE_CONTROL_CONTINUE)
{
CServiceControlProgress * pThis;
pThis = new CServiceControlProgress;
Assert(pThis->m_dwLastError == ERROR_SUCCESS); // No error yet
if (!pThis->M_FInit(
hwndParent,
hScManager,
pszMachineName,
pszServiceName,
pszServiceDisplayName))
{
delete pThis;
return errCannotInitialize;
}
return pThis->M_EControlService(dwControlCode);
} // S_EControlService()