Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1072 lines
31 KiB

  1. /////////////////////////////////////////////////////////////////////
  2. //
  3. // Progress.cpp
  4. //
  5. // Progress dialog to Start, Stop, Pause, Resume and Restart a service.
  6. //
  7. // IMPLEMENTATION
  8. // Since an operation (ie, Start, Stop, Pause, Resume or Restart) may take
  9. // a while, a thread is created to do the actual work while a dialog
  10. // is displayed to the user.
  11. // 0. Allocate CServiceControlProgress object on the heap
  12. // 1. Create a thread in suspended mode.
  13. // 2. Create the dialog.
  14. // 3. Dialog creates a timer to update the progress bar.
  15. // 4. Dialog resumes the thread.
  16. // 5. Thread opens the service and perform the requested operation(s).
  17. // 6. Dialog updates UI using its timer.
  18. // 7. Thread updates dialog UI as well.
  19. // 8. Thread waits until the dialog is dismissed. Dialog can be
  20. // dismissed for any of the following event:
  21. // a) Operation completed successfully.
  22. // b) User hit cancel button.
  23. // c) An unexpected error occured.
  24. // d) Operation times out.
  25. // 9 Thread deletes CServiceControlProgress object.
  26. //
  27. // HISTORY
  28. // 03-Oct-95 t-danmo Creation of (sysmgmt\dsui\services\progress.cxx)
  29. // 30-Sep-96 t-danmo Renamed and adapted to MMC.
  30. // 14-May-97 t-danm Fully implemented the "Restart" feature.
  31. //
  32. #include "stdafx.h"
  33. #include "progress.h"
  34. /////////////////////////////////////////////////////////////////////////////
  35. // This array represent the expected state of the service
  36. // after carrying action, start, stop, pause or resume.
  37. //
  38. // To be compared SERVICE_STATUS.dwCurrentState.
  39. //
  40. const DWORD rgdwExpectedServiceStatus[4] =
  41. {
  42. SERVICE_RUNNING, // Service should be running after a 'start'
  43. SERVICE_STOPPED, // Service should be stopped after a 'stop'
  44. SERVICE_PAUSED, // Service should be paused after a 'pause'
  45. SERVICE_RUNNING, // Service should be running after a 'resume'
  46. };
  47. /////////////////////////////////////////////////////////////////////////////
  48. CServiceControlProgress::CServiceControlProgress()
  49. {
  50. // Using ZeroMemory() is safe as long as CServiceControlProgress
  51. // is not derived from any other object and does not contains
  52. // any objects with constructors.
  53. ::ZeroMemory(this, sizeof(*this));
  54. }
  55. /////////////////////////////////////////////////////////////////////////////
  56. CServiceControlProgress::~CServiceControlProgress()
  57. {
  58. delete m_pargDependentServicesT;
  59. delete m_pargServiceStop;
  60. }
  61. /////////////////////////////////////////////////////////////////////////////
  62. // M_FInit()
  63. //
  64. // Initialize the object.
  65. // - Copy all input parameters
  66. // - Load the clock bitmap(s)
  67. // Return TRUE if successful, otherwise FALSE
  68. //
  69. BOOL
  70. CServiceControlProgress::M_FInit(
  71. HWND hwndParent, // IN: Parent of the dialog
  72. SC_HANDLE hScManager, // IN: Handle to service control manager database
  73. LPCTSTR pszMachineName, // IN: Machine name to display to the user
  74. LPCTSTR pszServiceName, // IN: Name of the service
  75. LPCTSTR pszServiceDisplayName) // IN: Display name of the service
  76. {
  77. Assert(IsWindow(hwndParent));
  78. Assert(hScManager != NULL);
  79. Assert(pszServiceName != NULL);
  80. Assert(pszServiceDisplayName != NULL);
  81. m_hWndParent = hwndParent;
  82. m_hScManager = hScManager;
  83. lstrcpy(OUT m_szUiMachineName, (pszMachineName && pszMachineName[0])
  84. ? pszMachineName : (LPCTSTR)g_strLocalMachine);
  85. lstrcpy(OUT m_szServiceName, pszServiceName);
  86. lstrcpy(OUT m_szServiceDisplayName, pszServiceDisplayName);
  87. return TRUE;
  88. } // M_FInit()
  89. /////////////////////////////////////////////////////////////////////
  90. // M_FDlgStopDependentServices()
  91. //
  92. // Check if the services has dependent services that must be stopped
  93. // before stopping the current service.
  94. //
  95. // If there are any dependent services, the function will display
  96. // a dialog asking the user to confirm he/she wants also to stop
  97. // all the dependent services.
  98. //
  99. // This function return FALSE ONLY IF the user click on the cancel button
  100. // otherwise TRUE. If there are no dependent services, or an error occurs
  101. // while reading dependent services, the function will return TRUE.
  102. //
  103. BOOL
  104. CServiceControlProgress::M_FDlgStopDependentServices()
  105. {
  106. Assert(m_hScManager != NULL);
  107. SC_HANDLE hService = NULL;
  108. BOOL fSuccess = TRUE;
  109. DWORD cbBytesNeeded = 0;
  110. DWORD dwServicesReturned = 0;
  111. m_cDependentServices = 0; // So far we have no dependent services
  112. delete m_pargServiceStop;
  113. m_pargServiceStop = NULL;
  114. {
  115. AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // required for CWaitCursor
  116. CWaitCursor wait;
  117. hService = ::OpenService(m_hScManager, m_szServiceName, SERVICE_ENUMERATE_DEPENDENTS);
  118. }
  119. if (hService == NULL)
  120. {
  121. TRACE1("M_FDlgStopDependentServices() - Unable to enumerate service dependencies for service %s.\n",
  122. m_szServiceName);
  123. goto End;
  124. }
  125. // Find out how many bytes are needed to enumerate the dependent services
  126. fSuccess = ::EnumDependentServices(
  127. hService,
  128. SERVICE_ACTIVE, // Enumerate only the active services
  129. NULL,
  130. 0,
  131. OUT &cbBytesNeeded,
  132. OUT &dwServicesReturned);
  133. if (cbBytesNeeded == 0)
  134. {
  135. // Service does not have any dependencies
  136. goto End;
  137. }
  138. Assert(fSuccess == FALSE);
  139. Report(GetLastError() == ERROR_MORE_DATA); // Error should be 'more data'
  140. Assert(dwServicesReturned == 0);
  141. cbBytesNeeded += 1000; // Add extra bytes (just in case)
  142. delete m_pargDependentServicesT; // Free previously allocated memory (if any)
  143. m_pargDependentServicesT = (LPENUM_SERVICE_STATUS) new BYTE[cbBytesNeeded];
  144. // Query the database for the dependent services
  145. fSuccess = ::EnumDependentServices(
  146. hService,
  147. SERVICE_ACTIVE, // Enumerate only the active services
  148. OUT m_pargDependentServicesT,
  149. cbBytesNeeded,
  150. OUT IGNORED &cbBytesNeeded,
  151. OUT &dwServicesReturned);
  152. Report(fSuccess != FALSE);
  153. Report(dwServicesReturned > 0);
  154. m_cDependentServices = dwServicesReturned;
  155. if (m_cDependentServices > 0)
  156. {
  157. // Allocate an array to hold all the dependent services
  158. m_pargServiceStop = new ENUM_SERVICE_STATUS[m_cDependentServices + 1];
  159. memcpy(OUT m_pargServiceStop, m_pargDependentServicesT,
  160. m_cDependentServices * sizeof(ENUM_SERVICE_STATUS));
  161. m_pargServiceStop[m_cDependentServices].lpServiceName = m_szServiceName;
  162. m_pargServiceStop[m_cDependentServices].lpDisplayName = m_szServiceDisplayName;
  163. INT_PTR nReturn = ::DialogBoxParam(
  164. g_hInstanceSave,
  165. MAKEINTRESOURCE(IDD_SERVICE_STOP_DEPENDENCIES),
  166. m_hWndParent,
  167. (DLGPROC)&S_DlgProcDependentServices,
  168. reinterpret_cast<LPARAM>(this));
  169. Report(nReturn != -1);
  170. if (0 == nReturn) // user chose Cancel
  171. fSuccess = FALSE;
  172. } // if
  173. End:
  174. if (NULL != hService)
  175. {
  176. VERIFY(::CloseServiceHandle(hService));
  177. }
  178. return fSuccess;
  179. } // M_FDlgStopDependentServices()
  180. /////////////////////////////////////////////////////////////////////
  181. // M_DoExecuteServiceThread()
  182. //
  183. // Run a background thread while a foreground dialog is
  184. // displayed to the user.
  185. // This routine synchronizes the background thread with the main thread.
  186. //
  187. // If an error occurs, the routine will display a message box of the
  188. // error encountered.
  189. //
  190. // Return the error code from GetLastError() if an error occured.
  191. //
  192. APIERR
  193. CServiceControlProgress::M_EDoExecuteServiceThread(void * pThreadProc)
  194. {
  195. Assert(pThreadProc != NULL);
  196. Assert(m_hService == NULL);
  197. Assert(m_hThread == NULL);
  198. m_hEvent = ::CreateEvent(
  199. NULL,
  200. FALSE,
  201. FALSE,
  202. NULL);
  203. Report(m_hEvent != NULL);
  204. // Create a thread in suspended mode
  205. m_hThread = ::CreateThread(
  206. NULL,
  207. 0,
  208. (LPTHREAD_START_ROUTINE)pThreadProc,
  209. this,
  210. CREATE_SUSPENDED,
  211. NULL);
  212. Report(m_hThread != NULL);
  213. // Display the dialog box, the dialog will resume the suspended thread
  214. (void)::DialogBoxParam(
  215. g_hInstanceSave,
  216. MAKEINTRESOURCE(IDD),
  217. m_hWndParent,
  218. (DLGPROC)&S_DlgProcControlService,
  219. reinterpret_cast<LPARAM>(this));
  220. // Display an error message to the user (if an error occured);
  221. M_ProcessErrorCode();
  222. // Make a copy of the last error code
  223. APIERR dwLastError = m_dwLastError;
  224. // Indicate the thread is allowed to terminate and delete the 'this' pointer
  225. VERIFY(SetEvent(m_hEvent));
  226. // 'this' pointer cannot longer be assumed to be valid
  227. return dwLastError;
  228. } // M_EDoExecuteServiceThread()
  229. /////////////////////////////////////////////////////////////////////
  230. // M_ProcessErrorCode()
  231. //
  232. // Query the service status one last time to get its exit code,
  233. // examine the content of member m_dwLastError and display an
  234. // error message if an error occured.
  235. //
  236. void
  237. CServiceControlProgress::M_ProcessErrorCode()
  238. {
  239. SERVICE_STATUS ss;
  240. ::ZeroMemory( &ss, sizeof(ss) );
  241. if (m_hService != NULL)
  242. {
  243. // Query the service status again to get its Win32ExitCode
  244. if (!::QueryServiceStatus(m_hService, OUT &ss))
  245. {
  246. TRACE3("QueryServiceStatus(%s [hService=%p]) failed. err=%u.\n",
  247. m_szServiceName, m_hService, GetLastError());
  248. m_dwLastError = GetLastError();
  249. }
  250. else
  251. {
  252. if (ss.dwWin32ExitCode != ERROR_SUCCESS)
  253. m_dwLastError = ss.dwWin32ExitCode;
  254. }
  255. } // if
  256. APIERR dwLastError = m_dwLastError;
  257. UINT uIdString = IDS_MSG_sss_UNABLE_TO_START_SERVICE;
  258. TCHAR szMessageExtra[512];
  259. szMessageExtra[0] = _T('\0');
  260. switch (dwLastError)
  261. {
  262. case ERROR_SUCCESS:
  263. if (ss.dwCurrentState == rgdwExpectedServiceStatus[m_iServiceAction])
  264. {
  265. // The service status is consistent with the expected service status
  266. uIdString = 0;
  267. }
  268. else
  269. {
  270. // We got a problem here, the service did not return an error
  271. // but did not behave as expected
  272. //
  273. // JonN 12/3/99 418111 If the service stopped automatically,
  274. // don't make such a fuss
  275. //
  276. if (SERVICE_RUNNING == rgdwExpectedServiceStatus[m_iServiceAction]
  277. && ( ss.dwCurrentState == SERVICE_STOPPED
  278. || ss.dwCurrentState == SERVICE_STOP_PENDING))
  279. {
  280. uIdString = IDS_MSG_sss_SERVICE_STOPPED_AUTOMATICALLY;
  281. break;
  282. }
  283. ::LoadString(g_hInstanceSave, IDS_MSG_INTERNAL_ERROR,
  284. OUT szMessageExtra, LENGTH(szMessageExtra));
  285. Assert(lstrlen(szMessageExtra) > 0);
  286. }
  287. break;
  288. case errUserCancelStopDependentServices:
  289. case errUserAbort:
  290. // Do not report this 'error' to the user
  291. uIdString = 0;
  292. break;
  293. case ERROR_SERVICE_SPECIFIC_ERROR:
  294. dwLastError = ss.dwServiceSpecificExitCode;
  295. uIdString = IDS_MSG_ssd_SERVSPECIFIC_START_SERVICE;
  296. // 341363 JonN 6/1/99: no point in loading this string as if it were
  297. // a Win32 error
  298. //::LoadString(g_hInstanceSave, IDS_MSG_SPECIFIC_ERROR,
  299. // OUT szMessageExtra, LENGTH(szMessageExtra));
  300. // Assert(lstrlen(szMessageExtra) > 0);
  301. break;
  302. } // switch
  303. if (uIdString != 0)
  304. {
  305. if (uIdString == IDS_MSG_ssd_SERVSPECIFIC_START_SERVICE)
  306. {
  307. DoServicesErrMsgBox(
  308. ::GetActiveWindow(),
  309. MB_OK | MB_ICONEXCLAMATION,
  310. 0,
  311. uIdString + m_iServiceAction,
  312. m_szServiceDisplayName,
  313. m_szUiMachineName,
  314. dwLastError);
  315. }
  316. else
  317. {
  318. DoServicesErrMsgBox(
  319. ::GetActiveWindow(),
  320. MB_OK | MB_ICONEXCLAMATION,
  321. dwLastError,
  322. uIdString + m_iServiceAction,
  323. m_szServiceDisplayName,
  324. m_szUiMachineName,
  325. szMessageExtra);
  326. }
  327. }
  328. } // M_ProcessErrorCode()
  329. /////////////////////////////////////////////////////////////////////
  330. // M_DoThreadCleanup()
  331. //
  332. // Routine that synchronize the background thread with the dialog and
  333. // perform cleanup tasks.
  334. // This routine delete the 'this' pointer when done.
  335. //
  336. void
  337. CServiceControlProgress::M_DoThreadCleanup()
  338. {
  339. TRACE1("CServiceControlProgress::M_DoThreadCleanup() - Waiting for event 0x%p...\n", m_hEvent);
  340. Assert(m_hEvent != NULL);
  341. // Wait for the the dialog box to be gone
  342. ::WaitForSingleObject(m_hEvent, INFINITE);
  343. VERIFY(::CloseHandle(m_hEvent));
  344. // Close the service handle opened by the thread
  345. if (m_hService != NULL)
  346. {
  347. if (!::CloseServiceHandle(m_hService))
  348. {
  349. TRACE3("CloseServiceHandle(%s [hService=%p]) failed. err=%u.\n",
  350. m_szServiceName, m_hService, GetLastError());
  351. }
  352. } // if
  353. VERIFY(::CloseHandle(m_hThread));
  354. delete this; // We are done with the object
  355. } // M_DoThreadCleanup()
  356. /////////////////////////////////////////////////////////////////////
  357. // M_EControlService()
  358. //
  359. // This function is just there to initialize variables to
  360. // perform a stop, pause, resume or restart operation.
  361. //
  362. APIERR
  363. CServiceControlProgress::M_EControlService(DWORD dwControlCode)
  364. {
  365. Assert(m_fRestartService == FALSE);
  366. APIERR err = 0;
  367. m_dwControlCode = dwControlCode;
  368. switch (dwControlCode)
  369. {
  370. default:
  371. Assert(FALSE && "CServiceControlProgress::M_EControlService() - Unknown control code.");
  372. break;
  373. case SERVICE_CONTROL_RESTART:
  374. m_dwControlCode = SERVICE_CONTROL_STOP;
  375. m_fRestartService = TRUE;
  376. // Fall Through //
  377. case SERVICE_CONTROL_STOP:
  378. m_dwDesiredAccess = SERVICE_STOP | SERVICE_QUERY_STATUS;
  379. m_dwQueryState = SERVICE_STOP_PENDING;
  380. m_iServiceAction = iServiceActionStop;
  381. if (!M_FDlgStopDependentServices())
  382. {
  383. // User changed its mind by pressing the 'Cancel' button
  384. err = errUserCancelStopDependentServices;
  385. }
  386. else
  387. {
  388. // Stop the services (including dependent services)
  389. err = M_EDoExecuteServiceThread(S_ThreadProcStopService);
  390. }
  391. break;
  392. case SERVICE_CONTROL_PAUSE:
  393. m_dwDesiredAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS;
  394. m_dwQueryState = SERVICE_PAUSE_PENDING;
  395. m_iServiceAction = iServiceActionPause;
  396. err = M_EDoExecuteServiceThread(S_ThreadProcPauseResumeService);
  397. break;
  398. case SERVICE_CONTROL_CONTINUE:
  399. m_dwDesiredAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS;
  400. m_dwQueryState = SERVICE_CONTINUE_PENDING;
  401. m_iServiceAction = iServiceActionResume;
  402. err = M_EDoExecuteServiceThread(S_ThreadProcPauseResumeService);
  403. break;
  404. } // switch
  405. return err;
  406. } // M_EControlService()
  407. /////////////////////////////////////////////////////////////////////
  408. // M_QueryCurrentServiceState()
  409. //
  410. // Simply call the API ::QueryServiceStatus() and return dwCurrentState
  411. // of the SERVICE_STATUS structure.
  412. //
  413. // RETURNS
  414. // Function return the current state of a service:
  415. // SERVICE_STOPPED The service is not running.
  416. // SERVICE_START_PENDING The service is starting.
  417. // SERVICE_STOP_PENDING The service is stopping.
  418. // SERVICE_RUNNING The service is running.
  419. // SERVICE_CONTINUE_PENDING The service continue is pending.
  420. // SERVICE_PAUSE_PENDING The service pause is pending.
  421. // SERVICE_PAUSED The service is paused.
  422. //
  423. // If an error occurs, the function will return SERVICE_STOPPED.
  424. //
  425. DWORD
  426. CServiceControlProgress::M_QueryCurrentServiceState()
  427. {
  428. BOOL fRet;
  429. SERVICE_STATUS ss;
  430. Assert(m_hService != NULL);
  431. if (m_hService == NULL) // Just in case
  432. {
  433. return SERVICE_STOPPED;
  434. }
  435. // Query the service status
  436. fRet = ::QueryServiceStatus(m_hService, OUT &ss);
  437. if (!fRet)
  438. {
  439. TRACE2("CServiceControlProgress::M_QueryCurrentServiceState() - ::QueryServiceStatus(%s) failed. err=%u\n",
  440. m_szServiceName, GetLastError());
  441. Assert(GetLastError() != ERROR_SUCCESS);
  442. m_dwLastError = GetLastError();
  443. return SERVICE_STOPPED;
  444. }
  445. return ss.dwCurrentState;
  446. } // M_QueryCurrentServiceState()
  447. /////////////////////////////////////////////////////////////////////
  448. // Query the service database to get the friendly name and
  449. // display it into the dialog.
  450. //
  451. void CServiceControlProgress::M_UpdateDialogUI(LPCTSTR pszDisplayName)
  452. {
  453. Assert(pszDisplayName != NULL);
  454. if (m_hctlActionMsg == NULL || m_hctlServiceNameMsg == NULL)
  455. return;
  456. Assert(IsWindow(m_hctlActionMsg) && IsWindow(m_hctlServiceNameMsg));
  457. SetWindowTextPrintf(
  458. m_hctlActionMsg,
  459. IDS_SVC_ss_SERVICE_STARTING + m_iServiceAction,
  460. pszDisplayName,
  461. m_szUiMachineName);
  462. SetWindowText( m_hctlServiceNameMsg, pszDisplayName );
  463. } // M_UpdateDialogUI()
  464. /////////////////////////////////////////////////////////////////////
  465. // Routine to iterate through the dependent services to stop.
  466. //
  467. // Return the the service name to stop, increment the 'pointer' to the
  468. // next service. The routine returns FALSE if there are no remaining
  469. // services to stop.
  470. //
  471. // REMARKS
  472. // This routine is also used to restart dependent services.
  473. //
  474. BOOL
  475. CServiceControlProgress::M_FGetNextService(
  476. OUT LPCTSTR * ppszServiceName,
  477. OUT LPCTSTR * ppszDisplayName)
  478. {
  479. Assert(ppszServiceName != NULL);
  480. Assert(ppszDisplayName != NULL);
  481. Assert(m_iServiceAction == iServiceActionStop || m_iServiceAction == iServiceActionStart);
  482. int iDependentService = m_iDependentServiceIter;
  483. if (m_iServiceAction == iServiceActionStop)
  484. m_iDependentServiceIter++;
  485. else
  486. m_iDependentServiceIter--;
  487. if (m_pargServiceStop != NULL
  488. && iDependentService >= 0
  489. && iDependentService <= m_cDependentServices)
  490. {
  491. *ppszServiceName = m_pargServiceStop[iDependentService].lpServiceName;
  492. *ppszDisplayName = m_pargServiceStop[iDependentService].lpDisplayName;
  493. }
  494. else
  495. {
  496. *ppszServiceName = m_szServiceName;
  497. *ppszDisplayName = m_szServiceDisplayName;
  498. }
  499. return (m_iDependentServiceIter >= 0 && m_iDependentServiceIter <= m_cDependentServices);
  500. } // M_FGetNextService()
  501. /////////////////////////////////////////////////////////////////////
  502. // Thread to start one (or more) services.
  503. DWORD
  504. CServiceControlProgress::S_ThreadProcStartService(CServiceControlProgress * pThis)
  505. {
  506. BOOL fSuccess = FALSE;
  507. SC_HANDLE hService;
  508. Assert(pThis != NULL);
  509. Assert(pThis->m_hScManager != NULL);
  510. Assert(pThis->m_hService == NULL);
  511. Endorse(pThis->m_fPulseEvent == TRUE); // We are starting multiple services
  512. Endorse(pThis->m_fPulseEvent == FALSE); // Wa are starting only one service
  513. if (pThis->m_dwLastError != ERROR_SUCCESS)
  514. {
  515. // If there is already an error, it is because we attempted to previously stop a service
  516. Assert(pThis->m_fRestartService == TRUE);
  517. goto Done;
  518. }
  519. while (TRUE)
  520. {
  521. LPCTSTR pszServiceName;
  522. LPCTSTR pszServiceDisplayName;
  523. UINT cServicesRemaining = pThis->M_FGetNextService(
  524. OUT &pszServiceName,
  525. OUT &pszServiceDisplayName);
  526. pThis->M_UpdateDialogUI(pszServiceDisplayName);
  527. // Sleep(5000); // Debug
  528. // Open service to allow a 'start' operation
  529. hService = ::OpenService(
  530. pThis->m_hScManager,
  531. pszServiceName,
  532. SERVICE_START | SERVICE_QUERY_STATUS);
  533. if (hService == NULL)
  534. {
  535. pThis->m_dwLastError = GetLastError();
  536. TRACE2("ERR: S_ThreadProcStartService(): Unable to open service %s to start. err=%u.\n",
  537. pszServiceName, pThis->m_dwLastError);
  538. break;
  539. }
  540. fSuccess = ::StartService(
  541. hService,
  542. pThis->m_dwNumServiceArgs,
  543. pThis->m_lpServiceArgVectors);
  544. if (!fSuccess)
  545. {
  546. APIERR err = GetLastError();
  547. if (ERROR_SERVICE_ALREADY_RUNNING != err)
  548. {
  549. pThis->m_dwLastError = err;
  550. TRACE2("ERR: S_ThreadProcStartService(): StartService(%s) returned err=%u.\n",
  551. pszServiceName, pThis->m_dwLastError);
  552. break;
  553. }
  554. }
  555. Assert(pThis->m_hService == NULL);
  556. if (cServicesRemaining == 0)
  557. {
  558. // This was our last service to start
  559. pThis->m_fPulseEvent = FALSE;
  560. pThis->m_hService = hService;
  561. break;
  562. }
  563. Assert(pThis->m_fPulseEvent == TRUE);
  564. pThis->m_hService = hService;
  565. // Wait until the service was actually 'started'
  566. WaitForSingleObject(pThis->m_hEvent, INFINITE);
  567. pThis->m_hService = NULL;
  568. Assert(hService != NULL);
  569. VERIFY(::CloseServiceHandle(hService));
  570. } // while
  571. Done:
  572. pThis->M_DoThreadCleanup();
  573. return 0;
  574. } // S_ThreadProcStartService()
  575. /////////////////////////////////////////////////////////////////////
  576. // Thread to stop one (or more) services.
  577. DWORD
  578. CServiceControlProgress::S_ThreadProcStopService(CServiceControlProgress * pThis)
  579. {
  580. BOOL fSuccess;
  581. SC_HANDLE hService;
  582. Assert(pThis != NULL);
  583. Assert(pThis->m_hScManager != NULL);
  584. Assert(pThis->m_dwDesiredAccess & SERVICE_STOP);
  585. Assert(pThis->m_hService == NULL);
  586. Assert(pThis->m_fPulseEvent == FALSE);
  587. //
  588. // Stop the dependent services
  589. //
  590. while (TRUE)
  591. {
  592. LPCTSTR pszServiceName;
  593. LPCTSTR pszServiceDisplayName;
  594. UINT cServicesRemaining = pThis->M_FGetNextService(
  595. OUT &pszServiceName,
  596. OUT &pszServiceDisplayName);
  597. pThis->M_UpdateDialogUI(pszServiceDisplayName);
  598. // Sleep(5000); // Debug
  599. hService = ::OpenService(
  600. pThis->m_hScManager,
  601. pszServiceName,
  602. pThis->m_dwDesiredAccess);
  603. if (hService == NULL)
  604. {
  605. pThis->m_dwLastError = GetLastError();
  606. TRACE2("ERR: S_ThreadProcStopService(): Unable to open dependent service %s to stop. err=%u.\n",
  607. pszServiceName, pThis->m_dwLastError);
  608. break;
  609. }
  610. SERVICE_STATUS ss; // Ignored
  611. fSuccess = ::ControlService(
  612. hService,
  613. pThis->m_dwControlCode,
  614. OUT IGNORED &ss);
  615. if (!fSuccess)
  616. {
  617. APIERR err = GetLastError();
  618. if (ERROR_SERVICE_NOT_ACTIVE != err)
  619. {
  620. TRACE2("ERR: S_ThreadProcStopService(): ControlService(%s) returned err=%u.\n",
  621. pszServiceName, pThis->m_dwLastError);
  622. break;
  623. }
  624. }
  625. Assert(pThis->m_hService == NULL);
  626. if (cServicesRemaining == 0 && !pThis->m_fRestartService)
  627. {
  628. // This was our last service to stop
  629. pThis->m_fPulseEvent = FALSE;
  630. pThis->m_hService = hService;
  631. break; // We are done
  632. }
  633. else
  634. {
  635. pThis->m_fPulseEvent = TRUE;
  636. pThis->m_hService = hService;
  637. }
  638. // Wait until the service was actually 'stopped'
  639. WaitForSingleObject(pThis->m_hEvent, INFINITE);
  640. pThis->m_hService = NULL;
  641. Assert(hService != NULL);
  642. VERIFY(::CloseServiceHandle(hService));
  643. if (cServicesRemaining == 0)
  644. {
  645. Assert(pThis->m_fRestartService == TRUE);
  646. Assert(pThis->m_fPulseEvent == TRUE);
  647. // Start the service
  648. Assert(pThis->m_dwNumServiceArgs == 0);
  649. Assert(pThis->m_lpServiceArgVectors == NULL);
  650. pThis->m_iDependentServiceIter = pThis->m_cDependentServices; // Rewind the service iterator
  651. pThis->m_dwQueryState = SERVICE_START_PENDING;
  652. pThis->m_iServiceAction = iServiceActionStart;
  653. (void)S_ThreadProcStartService(pThis);
  654. return 0;
  655. }
  656. } // while
  657. pThis->M_DoThreadCleanup();
  658. return 0;
  659. } // S_ThreadProcStopService()
  660. /////////////////////////////////////////////////////////////////////
  661. // Thread to pause or resume the service
  662. DWORD CServiceControlProgress::S_ThreadProcPauseResumeService(CServiceControlProgress * pThis)
  663. {
  664. BOOL fSuccess;
  665. SC_HANDLE hService;
  666. SERVICE_STATUS ss;
  667. Assert(pThis != NULL);
  668. Assert(pThis->m_hScManager != NULL);
  669. Assert(pThis->m_dwDesiredAccess & SERVICE_PAUSE_CONTINUE);
  670. Assert(pThis->m_hService == NULL);
  671. Assert(pThis->m_fPulseEvent == FALSE);
  672. pThis->M_UpdateDialogUI(pThis->m_szServiceDisplayName);
  673. // Open service to allow a 'pause' or 'resume' operation
  674. hService = ::OpenService(
  675. pThis->m_hScManager,
  676. pThis->m_szServiceName,
  677. pThis->m_dwDesiredAccess);
  678. if (hService == NULL)
  679. {
  680. pThis->m_dwLastError = GetLastError();
  681. TRACE2("ERR: S_ThreadProcPauseResumeService(): Unable to open service %s. err=%u.\n",
  682. pThis->m_szServiceName, pThis->m_dwLastError);
  683. goto Done;
  684. }
  685. fSuccess = ::ControlService(
  686. hService,
  687. pThis->m_dwControlCode,
  688. OUT IGNORED &ss);
  689. if (!fSuccess)
  690. {
  691. pThis->m_dwLastError = GetLastError();
  692. TRACE2("ERR: S_ThreadProcPauseResumeService(): ControlService(%s) returned err=%u.\n",
  693. pThis->m_szServiceName, pThis->m_dwLastError);
  694. }
  695. Assert(pThis->m_hService == NULL);
  696. pThis->m_hService = hService;
  697. Done:
  698. pThis->M_DoThreadCleanup();
  699. return 0;
  700. } // S_ThreadProcPauseResumeService()
  701. /////////////////////////////////////////////////////////////////////
  702. // S_DlgProcControlService()
  703. //
  704. // Dialog proc for the clock dialog
  705. // - Respond to a WM_TIMER message to update the clock bitmap while
  706. // waiting for the operation to complete.
  707. //
  708. BOOL CALLBACK
  709. CServiceControlProgress::S_DlgProcControlService(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  710. {
  711. CServiceControlProgress * pThis;
  712. pThis = (CServiceControlProgress*)GetWindowLongPtr(hdlg, DWLP_USER);
  713. switch (uMsg)
  714. {
  715. case WM_INITDIALOG:
  716. pThis = reinterpret_cast<CServiceControlProgress *>(lParam);
  717. Assert(pThis != NULL);
  718. pThis->M_OnInitDialog(hdlg);
  719. break;
  720. case WM_TIMER:
  721. Assert(pThis != NULL);
  722. Assert(wParam == pThis->m_uTimerId);
  723. pThis->M_OnTimer(hdlg);
  724. break;
  725. case WM_COMMAND:
  726. Assert(wParam == IDOK || wParam == IDCANCEL);
  727. if (wParam == IDCANCEL)
  728. {
  729. if ((HWND)lParam != NULL)
  730. {
  731. TRACE1("INFO: User cancelled dialog. dwLastError=%u.\n",
  732. pThis->m_dwLastError);
  733. pThis->m_dwLastError = errUserAbort;
  734. }
  735. EndDialog(hdlg, FALSE);
  736. }
  737. else
  738. {
  739. Assert(IsWindow(pThis->m_hctlProgress));
  740. SendMessage(pThis->m_hctlProgress, PBM_SETPOS, dwTimerProgressDone * 2, 0);
  741. Sleep(150);
  742. EndDialog (hdlg, TRUE);
  743. }
  744. break;
  745. case WM_DESTROY:
  746. Assert(IsWindow(pThis->m_hctlActionMsg) && IsWindow(pThis->m_hctlServiceNameMsg));
  747. pThis->m_hctlActionMsg = NULL;
  748. pThis->m_hctlServiceNameMsg = NULL;
  749. if (pThis->m_uTimerId != 0)
  750. {
  751. VERIFY(KillTimer(hdlg, pThis->m_uTimerId));
  752. }
  753. break;
  754. default:
  755. return FALSE;
  756. } // switch (uMsg)
  757. return (TRUE);
  758. } // S_DlgProcControlService()
  759. /////////////////////////////////////////////////////////////////////
  760. void
  761. CServiceControlProgress::M_OnInitDialog(HWND hdlg)
  762. {
  763. Assert(IsWindow(hdlg));
  764. SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(this));
  765. m_hctlActionMsg = HGetDlgItem(hdlg, IDC_STATIC_ACTION_MSG);
  766. m_hctlServiceNameMsg = HGetDlgItem(hdlg, IDC_STATIC_SERVICENAME_MSG);
  767. m_hctlProgress = HGetDlgItem(hdlg, IDC_PROGRESS);
  768. SendMessage(m_hctlProgress, PBM_SETRANGE, 0, MAKELPARAM(0, dwTimerProgressDone * 2));
  769. Assert(m_uTimerId == 0);
  770. Assert(m_dwTimerTicks == 0);
  771. m_uTimerId = SetTimer(hdlg, ID_TIMER, dwTimerTickIncrement, NULL);
  772. Assert(m_hThread != NULL);
  773. ::ResumeThread(m_hThread);
  774. if (m_uTimerId == 0)
  775. {
  776. Report(FALSE && "Unable to create timer. Dialog will be destroyed.");
  777. PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
  778. }
  779. } // M_OnInitDialog()
  780. /////////////////////////////////////////////////////////////////////
  781. void
  782. CServiceControlProgress::M_OnTimer(HWND hdlg)
  783. {
  784. Assert(IsWindow(m_hctlActionMsg) && IsWindow(m_hctlServiceNameMsg));
  785. m_dwTimerTicks += dwTimerTickIncrement;
  786. if (m_dwLastError != ERROR_SUCCESS)
  787. {
  788. TRACE1("CServiceControlProgress::M_OnTimer() - dwLastError=%u.\n", m_dwLastError);
  789. PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
  790. return;
  791. }
  792. if (m_dwTimerTicks > dwTimerTimeout)
  793. {
  794. VERIFY(KillTimer(hdlg, m_uTimerId));
  795. m_uTimerId = 0;
  796. m_dwLastError = ERROR_SERVICE_REQUEST_TIMEOUT;
  797. TRACE0("CServiceControlProgress::M_OnTimer() - Time out.\n");
  798. PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
  799. return;
  800. }
  801. if ((m_hService != NULL) && (m_dwTimerTicks >= 900))
  802. {
  803. // If the current state of the service changed (ie, operation completed)
  804. // we can dismiss the dialog
  805. if (m_dwQueryState != M_QueryCurrentServiceState())
  806. {
  807. if (m_fPulseEvent)
  808. {
  809. m_dwTimerTicks = 0; // Reset the time-out counter
  810. Assert(m_hEvent != NULL);
  811. PulseEvent(m_hEvent);
  812. SendMessage(m_hctlProgress, PBM_SETPOS, dwTimerProgressDone * 2, 0);
  813. Sleep(100);
  814. }
  815. else
  816. {
  817. PostMessage(hdlg, WM_COMMAND, IDOK, 0);
  818. }
  819. }
  820. } // if
  821. // Advance the current position of the progress bar by the increment.
  822. Assert(IsWindow(m_hctlProgress));
  823. DWORD dwPos = m_dwTimerTicks;
  824. if(dwPos > dwTimerProgressDone)
  825. {
  826. dwPos -= dwTimerProgressDone;
  827. dwPos = (dwPos * dwTimerProgressDone) / dwTimerTimeout;
  828. dwPos += dwTimerProgressDone;
  829. }
  830. SendMessage(m_hctlProgress, PBM_SETPOS, dwPos, 0);
  831. } // M_OnTimer()
  832. /////////////////////////////////////////////////////////////////////
  833. BOOL CALLBACK
  834. CServiceControlProgress::S_DlgProcDependentServices(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  835. {
  836. CServiceControlProgress * pThis;
  837. HWND hwndListbox;
  838. INT i;
  839. switch (uMsg)
  840. {
  841. case WM_INITDIALOG:
  842. pThis = reinterpret_cast<CServiceControlProgress *>(lParam);
  843. Assert(pThis != NULL);
  844. SetWindowTextPrintf( ::GetDlgItem(hdlg, IDC_STATIC_STOP_SERVICES),
  845. (pThis->m_fRestartService)
  846. ? IDS_SVC_s_RESTART_DEPENDENT_SERVICES
  847. : IDS_SVC_s_STOP_DEPENDENT_SERVICES,
  848. pThis->m_szServiceDisplayName);
  849. if (pThis->m_fRestartService)
  850. {
  851. // Set the window caption
  852. SetWindowTextPrintf(hdlg, IDS_SVC_RESTART_DEPENDENT_CAPTION);
  853. SetWindowTextPrintf(::GetDlgItem(hdlg, IDC_STATIC_STOP_SERVICES_QUERY),
  854. IDS_SVC_RESTART_DEPENDENT_QUERY);
  855. }
  856. // Fill in the listbox with dependent services
  857. hwndListbox = HGetDlgItem(hdlg, IDC_LIST_SERVICES);
  858. Assert(pThis->m_pargDependentServicesT != NULL);
  859. for (i = 0; i < pThis->m_cDependentServices; i++)
  860. {
  861. SendMessage(hwndListbox, LB_ADDSTRING, 0,
  862. (LPARAM)pThis->m_pargDependentServicesT[i].lpDisplayName);
  863. }
  864. break;
  865. case WM_COMMAND:
  866. switch (wParam)
  867. {
  868. case IDOK:
  869. EndDialog(hdlg, TRUE);
  870. break;
  871. case IDCANCEL:
  872. EndDialog(hdlg, FALSE);
  873. break;
  874. }
  875. break;
  876. case WM_CONTEXTMENU: // right mouse click
  877. DoContextHelp(wParam, HELP_DIALOG_TOPIC(IDD_SERVICE_STOP_DEPENDENCIES));
  878. break;
  879. case WM_HELP:
  880. DoHelp(lParam, HELP_DIALOG_TOPIC(IDD_SERVICE_STOP_DEPENDENCIES));
  881. break;
  882. default:
  883. return FALSE;
  884. } // switch (uMsg)
  885. return TRUE;
  886. } // S_DlgProcDependentServices()
  887. /////////////////////////////////////////////////////////////////////
  888. // S_EStartService()
  889. //
  890. // Starts the execution of a service synchronously. The function will wait
  891. // until the service is fully started and/or failed to start.
  892. //
  893. // A clock dialog will appear indicating the progress of the operation.
  894. //
  895. // Return ERROR_SUCCESS if syccessful, otherwise return the error code
  896. // from GetLastError().
  897. //
  898. APIERR
  899. CServiceControlProgress::S_EStartService(
  900. HWND hwndParent, // IN: Parent of the dialog
  901. SC_HANDLE hScManager, // IN: Handle to service control manager database
  902. LPCTSTR pszMachineName, // IN: Machine name to display to the user
  903. LPCTSTR pszServiceName, // IN: Name of the service to start
  904. LPCTSTR pszServiceDisplayName, // IN: Display name of the service to start
  905. DWORD dwNumServiceArgs, // IN: Number of arguments
  906. LPCTSTR * lpServiceArgVectors) // IN: Address of array of argument string pointers
  907. {
  908. CServiceControlProgress * pThis;
  909. pThis = new CServiceControlProgress;
  910. Assert(pThis->m_dwLastError == ERROR_SUCCESS); // No error yet
  911. if (!pThis->M_FInit(
  912. hwndParent,
  913. hScManager,
  914. pszMachineName,
  915. pszServiceName,
  916. pszServiceDisplayName))
  917. {
  918. delete pThis;
  919. return errCannotInitialize;
  920. }
  921. pThis->m_dwNumServiceArgs = dwNumServiceArgs;
  922. pThis->m_lpServiceArgVectors = lpServiceArgVectors;
  923. pThis->m_dwQueryState = SERVICE_START_PENDING;
  924. pThis->m_iServiceAction = iServiceActionStart;
  925. return pThis->M_EDoExecuteServiceThread(S_ThreadProcStartService);
  926. } // S_EStartService()
  927. /////////////////////////////////////////////////////////////////////
  928. // S_EControlService()
  929. //
  930. // Control the execution of a service synchronously. The function is similar
  931. // to EStartService() but use for Stop, Pause or Resume a service.
  932. //
  933. // A clock dialog will appear indicating the progress of the operation.
  934. //
  935. // Return ERROR_SUCCESS if syccessful, otherwise return the error code
  936. // from GetLastError().
  937. //
  938. APIERR
  939. CServiceControlProgress::S_EControlService(
  940. HWND hwndParent, // IN: Parent of the dialog
  941. SC_HANDLE hScManager, // IN: Handle to service control manager database
  942. LPCTSTR pszMachineName, // IN: Machine name to display to the user
  943. LPCTSTR pszServiceName, // IN: Name of the service to start
  944. LPCTSTR pszServiceDisplayName, // IN: Display name of the service to start
  945. DWORD dwControlCode) // IN: Control code. (SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE or SERVICE_CONTROL_CONTINUE)
  946. {
  947. CServiceControlProgress * pThis;
  948. pThis = new CServiceControlProgress;
  949. Assert(pThis->m_dwLastError == ERROR_SUCCESS); // No error yet
  950. if (!pThis->M_FInit(
  951. hwndParent,
  952. hScManager,
  953. pszMachineName,
  954. pszServiceName,
  955. pszServiceDisplayName))
  956. {
  957. delete pThis;
  958. return errCannotInitialize;
  959. }
  960. return pThis->M_EControlService(dwControlCode);
  961. } // S_EControlService()