Leaked source code of windows server 2003
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.

704 lines
24 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1999-2001 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // CService.cpp
  7. //
  8. // Description:
  9. // Contains the definition of the CService class.
  10. //
  11. // Maintained By:
  12. // David Potter (DavidP) 14-JU-2001
  13. // Vij Vasu (Vvasu) 08-MAR-2000
  14. //
  15. //////////////////////////////////////////////////////////////////////////////
  16. //////////////////////////////////////////////////////////////////////////////
  17. // Include Files
  18. //////////////////////////////////////////////////////////////////////////////
  19. // The precompiled header.
  20. #include "Pch.h"
  21. // The header file for this class.
  22. #include "CService.h"
  23. // For the CStr class.
  24. #include "CStr.h"
  25. // For the CStatusReport class
  26. #include "CStatusReport.h"
  27. //////////////////////////////////////////////////////////////////////////////
  28. // Macros
  29. //////////////////////////////////////////////////////////////////////////////
  30. // String added to end of service name to get INF file section for create
  31. #define SERVICE_CREATE_SECTION_SUFFIX L"_Create"
  32. // String added to end of service name to get INF file section for cleanup
  33. #define SERVICE_CLEANUP_SECTION_SUFFIX L"_Cleanup"
  34. //////////////////////////////////////////////////////////////////////////////
  35. //++
  36. //
  37. // CService::Create
  38. //
  39. // Description:
  40. // This function creates an entry for the service with the SCM. It does
  41. // this by using the SetupAPI to process service and registry related
  42. // entries in a section named <ServiceName>_Create in the INF file that
  43. // is passed in.
  44. //
  45. // For example, if this object represents the ClusSvc service, then,
  46. // when this function is called, the AddService and the AddReg entries
  47. // under the [ClusSvc_Create] section are processed in the INF file
  48. // whose handle is hInfHandleIn.
  49. //
  50. // Arguments:
  51. // hInfHandleIn
  52. // Handle to the INF file that contains the required sections
  53. //
  54. // Return Value:
  55. // None.
  56. //
  57. // Exceptions Thrown:
  58. // CRuntimeError
  59. // If any of the APIs fail.
  60. //
  61. //--
  62. //////////////////////////////////////////////////////////////////////////////
  63. void
  64. CService::Create(
  65. HINF hInfHandleIn
  66. )
  67. {
  68. TraceFunc1( "Service Name = '%s'", m_strName.PszData() );
  69. LogMsg( "[BC] Attempting to create the '%s' service.", m_strName.PszData() );
  70. DWORD sc = ERROR_SUCCESS;
  71. CStr strSectionName = m_strName + SERVICE_CREATE_SECTION_SUFFIX;
  72. // Process the service section
  73. if ( SetupInstallServicesFromInfSection(
  74. hInfHandleIn
  75. , strSectionName.PszData()
  76. , 0
  77. ) == FALSE
  78. )
  79. {
  80. sc = TW32( GetLastError() );
  81. goto Cleanup;
  82. } // if: SetupInstallServicesFromInfSection failed
  83. // Process the registry keys.
  84. if ( SetupInstallFromInfSection(
  85. NULL // optional, handle of a parent window
  86. , hInfHandleIn // handle to the INF file
  87. , strSectionName.PszData() // name of the Install section
  88. , SPINST_REGISTRY // which lines to install from section
  89. , NULL // optional, key for registry installs
  90. , NULL // optional, path for source files
  91. , NULL // optional, specifies copy behavior
  92. , NULL // optional, specifies callback routine
  93. , NULL // optional, callback routine context
  94. , NULL // optional, device information set
  95. , NULL // optional, device info structure
  96. ) == FALSE
  97. )
  98. {
  99. sc = TW32( GetLastError() );
  100. goto Cleanup;
  101. } // if: SetupInstallFromInfSection failed
  102. LogMsg( "[BC] The '%s' service has been successfully created.", m_strName.PszData() );
  103. Cleanup:
  104. if ( sc != ERROR_SUCCESS )
  105. {
  106. LogMsg( "[BC] Error %#08x returned from SetupInstallFromInfSection() while trying to create the '%s' service. Throwing an exception.", sc, m_strName.PszData() );
  107. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_SERVICE_CREATE );
  108. }
  109. TraceFuncExit();
  110. } //*** CService::Create
  111. //////////////////////////////////////////////////////////////////////////////
  112. //++
  113. //
  114. // CService::Cleanup
  115. //
  116. // Description:
  117. // This function cleans up a service by deregistering it with the SCM and by
  118. // deleting any required registry entries. It does this by using the
  119. // SetupAPI to process service and registry related entries in a
  120. // section named <ServiceName>_Cleanup in the INF file that is passed in.
  121. //
  122. // For example, if this object represents the ClusSvc service, then,
  123. // when this function is called, the DelService and the DelReg entries
  124. // under the [ClusSvc_Cleanup] section are processed in the INF file
  125. // whose handle is hInfHandleIn.
  126. //
  127. // Arguments:
  128. // hInfHandleIn
  129. // Handle to the INF file that contains the required sections
  130. //
  131. // Return Value:
  132. // None.
  133. //
  134. // Exceptions Thrown:
  135. // CRuntimeError
  136. // If any of the APIs fail.
  137. //
  138. //--
  139. //////////////////////////////////////////////////////////////////////////////
  140. void
  141. CService::Cleanup(
  142. HINF hInfHandleIn
  143. )
  144. {
  145. TraceFunc1( "Service Name = '%s'", m_strName.PszData() );
  146. LogMsg( "[BC] Attempting to clean up the '%s' service.", m_strName.PszData() );
  147. DWORD sc = ERROR_SUCCESS;
  148. CStr strSectionName = m_strName + SERVICE_CLEANUP_SECTION_SUFFIX;
  149. // Process the service section
  150. if ( SetupInstallServicesFromInfSection(
  151. hInfHandleIn
  152. , strSectionName.PszData()
  153. , 0
  154. ) == FALSE
  155. )
  156. {
  157. sc = TW32( GetLastError() );
  158. goto Cleanup;
  159. } // if: SetupInstallServicesFromInfSection failed
  160. // Process the registry keys.
  161. if ( SetupInstallFromInfSection(
  162. NULL // optional, handle of a parent window
  163. , hInfHandleIn // handle to the INF file
  164. , strSectionName.PszData() // name of the Install section
  165. , SPINST_REGISTRY // which lines to install from section
  166. , NULL // optional, key for registry installs
  167. , NULL // optional, path for source files
  168. , NULL // optional, specifies copy behavior
  169. , NULL // optional, specifies callback routine
  170. , NULL // optional, callback routine context
  171. , NULL // optional, device information set
  172. , NULL // optional, device info structure
  173. ) == FALSE
  174. )
  175. {
  176. sc = TW32( GetLastError() );
  177. goto Cleanup;
  178. } // if: SetupInstallFromInfSection failed
  179. LogMsg( "[BC] The '%s' service has been successfully cleaned up.", m_strName.PszData() );
  180. Cleanup:
  181. if ( sc != ERROR_SUCCESS )
  182. {
  183. LogMsg( "[BC] Error %#08x returned from SetupInstallFromInfSection() while trying to clean up the '%s' service. Throwing an exception.", sc, m_strName.PszData() );
  184. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_SERVICE_CLEANUP );
  185. }
  186. TraceFuncExit();
  187. } //*** CService::Cleanup
  188. //////////////////////////////////////////////////////////////////////////////
  189. //++
  190. //
  191. // CService::Start
  192. //
  193. // Description:
  194. // Instructs the SCM to start the service. If fWaitForServiceStartIn is
  195. // true, this function tests cQueryCountIn times to see if the service has
  196. // started, checking every ulQueryIntervalMilliSecIn milliseconds.
  197. //
  198. // fWaitForServiceStartIn is false, this function returns immediately.
  199. //
  200. // Arguments:
  201. // hServiceControlManagerIn
  202. // Handle to the service control manager.
  203. //
  204. // fWaitForServiceStartIn
  205. // If true, this function waits for the service to finish starting
  206. // before returning. The default value is true.
  207. //
  208. // ulQueryIntervalMilliSecIn
  209. // Number of milliseconds between checking to see if the service
  210. // has started. The default value is 500 milliseconds.
  211. // This argument is used only if fWaitForServiceStartIn is true.
  212. //
  213. // cQueryCountIn
  214. // The number of times this function will query the service to see
  215. // if it has started. An exception is thrown if the service is not
  216. // running even after querying it ulQueryCountIn times. The default
  217. // value is 10 times.
  218. // This argument is used only if fWaitForServiceStartIn is true.
  219. //
  220. // pStatusReportIn
  221. // A pointer to the status report that should be sent while waiting for
  222. // the service to start. This argument is NULL by default.
  223. //
  224. // Return Value:
  225. // None.
  226. //
  227. // Exceptions Thrown:
  228. // CRuntimeError
  229. // If any of the APIs fail.
  230. //
  231. // CConfigError
  232. // If the service is not running even after the timeout has expired.
  233. //
  234. //--
  235. //////////////////////////////////////////////////////////////////////////////
  236. void
  237. CService::Start(
  238. SC_HANDLE hServiceControlManagerIn
  239. , bool fWaitForServiceStartIn
  240. , ULONG ulQueryIntervalMilliSecIn
  241. , UINT cQueryCountIn
  242. , CStatusReport * pStatusReportIn
  243. )
  244. {
  245. TraceFunc1( "Service Name = '%s'", m_strName.PszData() );
  246. LogMsg( "[BC] Attempting to start the '%s' service.", m_strName.PszData() );
  247. DWORD sc = ERROR_SUCCESS;
  248. bool fStarted = false; // Has the service been started?
  249. UINT cNumberOfQueries; // The number of times we have queried the service.
  250. int cSeqMismatchRetries = 0; // The number of times service returned ERROR_CLUSTER_DATABASE_SEQMISMATCH.
  251. // Handle to the service.
  252. SmartSCMHandle sscmhServiceHandle(
  253. OpenService(
  254. hServiceControlManagerIn
  255. , m_strName.PszData()
  256. , SERVICE_START | SERVICE_QUERY_STATUS
  257. )
  258. );
  259. if ( sscmhServiceHandle.FIsInvalid() )
  260. {
  261. sc = TW32( GetLastError() );
  262. LogMsg( "[BC] Error %#08x occurred while trying to open the '%s' service. Throwing an exception.", sc, m_strName.PszData() );
  263. goto Cleanup;
  264. } // if: the handle to the service could not be opened.
  265. // Try and start the service.
  266. sc = TW32( ScStartService( sscmhServiceHandle.HHandle() ) );
  267. if ( sc != ERROR_SUCCESS )
  268. {
  269. goto Cleanup;
  270. } // if:
  271. // If we are here, then the service may not have started yet.
  272. // Has the caller requested that we wait for the service to start?
  273. if ( ! fWaitForServiceStartIn )
  274. {
  275. LogMsg( "[BC] Not waiting to see if the service has started or not." );
  276. goto Cleanup;
  277. } // if: no waiting is required.
  278. // We have to wait for the service to start.
  279. cNumberOfQueries = 0;
  280. do
  281. {
  282. SERVICE_STATUS ssStatus;
  283. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  284. // Query the service for its status.
  285. if ( QueryServiceStatus( sscmhServiceHandle.HHandle(), &ssStatus ) == FALSE )
  286. {
  287. sc = TW32( GetLastError() );
  288. LogMsg( "[BC] Error %#08x occurred while trying to query service status. Throwing an exception.", sc );
  289. break;
  290. } // if: we could not query the service for its status.
  291. //
  292. // Set the member Win32 and Service specific exit codes.
  293. //
  294. m_scWin32ExitCode = ssStatus.dwWin32ExitCode;
  295. m_scServiceExitCode = ssStatus.dwServiceSpecificExitCode;
  296. // Check if the service has posted an error.
  297. // If the error is ERROR_CLUSTER_DATABASE_SEQMISMATCH then we want to sleep for 5 seconds and retry. This
  298. // will only be returned by the cluster service.
  299. if ( ssStatus.dwServiceSpecificExitCode == ERROR_CLUSTER_DATABASE_SEQMISMATCH )
  300. {
  301. Sleep( 5000 );
  302. cSeqMismatchRetries++;
  303. if ( cSeqMismatchRetries > 3 )
  304. {
  305. sc = TW32( ssStatus.dwWin32ExitCode );
  306. LogMsg( "[BC] The service has returned error %#08x to query service status more than 3 times. Throwing an exception.", sc );
  307. break;
  308. } // if:
  309. // Try and start the service. Ignore error and let the QueryServiceStatus() tell us if it started or not.
  310. sc = TW32( ScStartService( sscmhServiceHandle.HHandle() ) );
  311. if ( sc != ERROR_SUCCESS )
  312. {
  313. break;
  314. } // if:
  315. continue;
  316. } // if: the service returned ERROR_CLUSTER_DATABASE_SEQMISMATCH
  317. else if ( ssStatus.dwWin32ExitCode != ERROR_SUCCESS )
  318. {
  319. sc = TW32( ssStatus.dwWin32ExitCode );
  320. if ( sc == ERROR_SERVICE_SPECIFIC_ERROR )
  321. {
  322. TraceFlow( "This is a service specific error code." );
  323. sc = TW32( ssStatus.dwServiceSpecificExitCode );
  324. }
  325. LogMsg( "[BC] The service has returned error %#08x to query service status. Throwing an exception.", sc );
  326. break;
  327. } // else if: the service itself has posted an error.
  328. if ( ssStatus.dwCurrentState == SERVICE_RUNNING )
  329. {
  330. fStarted = true;
  331. break;
  332. } // if: the service is running.
  333. ++cNumberOfQueries;
  334. // Send a progress report if the caller had passed in a valid pointer.
  335. if ( pStatusReportIn != NULL )
  336. {
  337. pStatusReportIn->SendNextStep( S_OK );
  338. } // if: a status report needs to be sent while we wait
  339. // Wait for the specified time.
  340. Sleep( ulQueryIntervalMilliSecIn );
  341. }
  342. while ( cNumberOfQueries < cQueryCountIn ); // while: loop for the required number of queries
  343. if ( sc != ERROR_SUCCESS )
  344. {
  345. goto Cleanup;
  346. } // if: something went wrong.
  347. if ( ! fStarted )
  348. {
  349. LogMsg( "[BC] The service could not be started. Throwing an exception." );
  350. THROW_CONFIG_ERROR( HRESULT_FROM_WIN32( TW32( ERROR_SERVICE_NOT_ACTIVE ) ), IDS_ERROR_SERVICE_START );
  351. } // if: the maximum number of queries have been made and the service is not running.
  352. Cleanup:
  353. if ( sc != ERROR_SUCCESS )
  354. {
  355. LogMsg( "[BC] Error %#08x occurred trying to start the '%s' service.", sc, m_strName.PszData() );
  356. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_SERVICE_START );
  357. } // if: something has gone wrong
  358. else
  359. {
  360. LogMsg( "[BC] The '%s' service has been successfully started.", m_strName.PszData() );
  361. } // else: nothing went wrong.
  362. TraceFuncExit();
  363. } //*** CService::Start
  364. //////////////////////////////////////////////////////////////////////////////
  365. //++
  366. //
  367. // CService::Stop
  368. //
  369. // Description:
  370. // Instructs the SCM to stop the service. If fWaitForServiceStop is
  371. // true, this function tests cQueryCountIn times to see if the service has
  372. // stopped, checking every ulQueryIntervalMilliSecIn milliseconds.
  373. //
  374. // Arguments:
  375. // hServiceControlManagerIn
  376. // Handle to the service control manager.
  377. //
  378. // ulQueryIntervalMilliSecIn
  379. // Number of milliseconds between checking to see if the service
  380. // has stopped. The default value is 500 milliseconds.
  381. //
  382. // cQueryCountIn
  383. // The number of times this function will query the service to see
  384. // if it has stopped. An exception is thrown if the service is not
  385. // running even after querying it ulQueryCountIn times. The default
  386. // value is 10 times.
  387. //
  388. // pStatusReportIn
  389. // A pointer to the status report that should be sent while waiting for
  390. // the service to stop. This argument is NULL by default.
  391. //
  392. // Return Value:
  393. // None.
  394. //
  395. // Exceptions Thrown:
  396. // CRuntimeError
  397. // If any of the APIs fail.
  398. //
  399. // CConfigError
  400. // If the service has not stopped even after the timeout has expired.
  401. //
  402. //--
  403. //////////////////////////////////////////////////////////////////////////////
  404. void
  405. CService::Stop(
  406. SC_HANDLE hServiceControlManagerIn
  407. , ULONG ulQueryIntervalMilliSecIn
  408. , UINT cQueryCountIn
  409. , CStatusReport * pStatusReportIn
  410. )
  411. {
  412. TraceFunc( "" );
  413. DWORD sc = ERROR_SUCCESS;
  414. SERVICE_STATUS ssStatus; // The service status structure.
  415. bool fStopped = false; // Has the service been stopped?
  416. UINT cNumberOfQueries = 0; // The number of times we have queried the service
  417. // (not including the initial state query).
  418. LogMsg( "[BC] Attempting to stop the '%s' service.", m_strName.PszData() );
  419. // Smart handle to the service being stopped.
  420. SmartSCMHandle sscmhServiceHandle(
  421. OpenService(
  422. hServiceControlManagerIn
  423. , m_strName.PszData()
  424. , SERVICE_STOP | SERVICE_QUERY_STATUS
  425. )
  426. );
  427. // Check if we could open a handle to the service.
  428. if ( sscmhServiceHandle.FIsInvalid() )
  429. {
  430. // We could not get a handle to the service.
  431. sc = GetLastError();
  432. // Check if the service exists.
  433. if ( sc == ERROR_SERVICE_DOES_NOT_EXIST )
  434. {
  435. // Nothing needs to be done here.
  436. LogMsg( "[BC] The '%s' service does not exist, so it is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  437. sc = ERROR_SUCCESS;
  438. } // if: the service does not exist
  439. else
  440. {
  441. // Something else has gone wrong.
  442. TW32( sc );
  443. LogMsg( "[BC] Error %#08x occurred trying to open the '%s' service.", sc, m_strName.PszData() );
  444. } // else: the service exists
  445. goto Cleanup;
  446. } // if: the handle to the service could not be opened.
  447. TraceFlow( "Querying the service for its initial state." );
  448. // Query the service for its initial state.
  449. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  450. if ( QueryServiceStatus( sscmhServiceHandle.HHandle(), &ssStatus ) == 0 )
  451. {
  452. sc = TW32( GetLastError() );
  453. LogMsg( "[BC] Error %#08x occurred while trying to query the initial state of the '%s' service.", sc, m_strName.PszData() );
  454. goto Cleanup;
  455. } // if: we could not query the service for its status.
  456. // If the service has stopped, we have nothing more to do.
  457. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  458. {
  459. // Nothing needs to be done here.
  460. LogMsg( "[BC] The '%s' service is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  461. goto Cleanup;
  462. } // if: the service has stopped.
  463. // If we are here, the service is running.
  464. TraceFlow( "The service is running." );
  465. //
  466. // Try and stop the service.
  467. //
  468. // If the service is stopping on its own, no need to send the stop control code.
  469. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  470. {
  471. LogMsg( "[BC] The service is stopping on its own. The stop control code will not be sent." );
  472. } // if: the service is stopping already
  473. else
  474. {
  475. TraceFlow( "The stop control code will be sent now." );
  476. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  477. if ( ControlService( sscmhServiceHandle.HHandle(), SERVICE_CONTROL_STOP, &ssStatus ) == 0 )
  478. {
  479. sc = GetLastError();
  480. if ( sc == ERROR_SERVICE_NOT_ACTIVE )
  481. {
  482. LogMsg( "[BC] The '%s' service is not running. Nothing more needs to be done here.", m_strName.PszData() );
  483. // The service is not running. Change the error code to success.
  484. sc = ERROR_SUCCESS;
  485. } // if: the service is already running.
  486. else
  487. {
  488. TW32( sc );
  489. LogMsg( "[BC] Error %#08x occurred trying to stop the '%s' service.", sc, m_strName.PszData() );
  490. }
  491. // There is nothing else to do.
  492. goto Cleanup;
  493. } // if: an error occurred trying to stop the service.
  494. } // else: the service has to be instructed to stop
  495. // Query the service for its state now and wait till the timeout expires
  496. cNumberOfQueries = 0;
  497. do
  498. {
  499. // Query the service for its status.
  500. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  501. if ( QueryServiceStatus( sscmhServiceHandle.HHandle(), &ssStatus ) == 0 )
  502. {
  503. sc = TW32( GetLastError() );
  504. LogMsg( "[BC] Error %#08x occurred while trying to query the state of the '%s' service.", sc, m_strName.PszData() );
  505. break;
  506. } // if: we could not query the service for its status.
  507. // If the service has stopped, we have nothing more to do.
  508. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  509. {
  510. // Nothing needs to be done here.
  511. LogMsg( "[BC] The service has been stopped." );
  512. fStopped = true;
  513. sc = ERROR_SUCCESS;
  514. break;
  515. } // if: the service has stopped.
  516. // Check if the timeout has expired
  517. if ( cNumberOfQueries >= cQueryCountIn )
  518. {
  519. LogMsg( "[BC] The service stop wait timeout has expired." );
  520. break;
  521. } // if: number of queries has exceeded the maximum specified
  522. TraceFlow2(
  523. "Waiting for %d milliseconds before querying service status again. %d such queries remaining."
  524. , ulQueryIntervalMilliSecIn
  525. , cQueryCountIn - cNumberOfQueries
  526. );
  527. ++cNumberOfQueries;
  528. // Send a progress report if the caller had passed in a valid pointer.
  529. if ( pStatusReportIn != NULL )
  530. {
  531. pStatusReportIn->SendNextStep( S_OK );
  532. } // if: a status report needs to be sent while we wait
  533. // Wait for the specified time.
  534. Sleep( ulQueryIntervalMilliSecIn );
  535. }
  536. while( true ); // while: loop infinitely
  537. if ( sc != ERROR_SUCCESS )
  538. goto Cleanup;
  539. if ( ! fStopped )
  540. {
  541. sc = TW32( ERROR_SERVICE_REQUEST_TIMEOUT );
  542. LogMsg( "[BC] The '%s' service has not stopped even after %d queries.", m_strName.PszData(), cQueryCountIn );
  543. goto Cleanup;
  544. } // if: the maximum number of queries have been made and the service is not running.
  545. LogMsg( "[BC] The '%s' service was successfully stopped.", m_strName.PszData() );
  546. Cleanup:
  547. if ( sc != ERROR_SUCCESS )
  548. {
  549. TraceFlow2( "Error %#08x has occurred trying to stop the '%s' service. Throwing exception.", sc, m_strName.PszData() );
  550. LogMsg( "[BC] Error %#08x has occurred trying to stop the '%s' service. Throwing an exception.", sc, m_strName.PszData() );
  551. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( sc ), IDS_ERROR_SERVICE_STOP );
  552. } // if: something has gone wrong
  553. TraceFuncExit();
  554. } //*** CService::Stop
  555. //////////////////////////////////////////////////////////////////////////////
  556. //++
  557. //
  558. // CService::ScStartService
  559. //
  560. // Description:
  561. // Wrapper for the Win32 API StartService().
  562. //
  563. // Arguments:
  564. // sscmhServiceHandleIn
  565. //
  566. // Return Value:
  567. // Win32 error code from StartService().
  568. //
  569. // Exceptions Thrown:
  570. // None.
  571. //
  572. //--
  573. //////////////////////////////////////////////////////////////////////////////
  574. DWORD
  575. CService::ScStartService(
  576. SC_HANDLE hServiceIn
  577. )
  578. {
  579. TraceFunc( "" );
  580. DWORD sc = ERROR_SUCCESS;
  581. // Try and start the service.
  582. if ( StartService( hServiceIn, 0, NULL ) == FALSE )
  583. {
  584. sc = GetLastError();
  585. if ( sc == ERROR_SERVICE_ALREADY_RUNNING )
  586. {
  587. LogMsg( "[BC] The '%s' service is already running.", m_strName.PszData() );
  588. // The service is already running. Change the error code to success.
  589. sc = ERROR_SUCCESS;
  590. } // if: the service is already running.
  591. else
  592. {
  593. TW32( sc );
  594. LogMsg( "[BC] Error %#08x occurred while trying to start the '%s' service.", sc, m_strName.PszData() );
  595. } // else:
  596. } // if: an error occurred trying to start the service.
  597. RETURN( sc );
  598. } //*** CService::ScStartService