///////////////////////////////////////////////////////////////////// // // 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(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(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(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(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(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()