ServiceBase.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /****************************** Module Header ******************************\
  2. * Module Name: ServiceBase.cpp
  3. * Project: CppWindowsService
  4. * Copyright (c) Microsoft Corporation.
  5. *
  6. * Provides a base class for a service that will exist as part of a service
  7. * application. CServiceBase must be derived from when creating a new service
  8. * class.
  9. *
  10. * This source is subject to the Microsoft Public License.
  11. * See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL.
  12. * All other rights reserved.
  13. *
  14. * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  15. * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  16. * WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  17. \***************************************************************************/
  18. #pragma region Includes
  19. #include "ServiceBase.h"
  20. #include <assert.h>
  21. #include <strsafe.h>
  22. #include <string>
  23. #pragma endregion
  24. #pragma region Static Members
  25. // Initialize the singleton service instance.
  26. CServiceBase *CServiceBase::s_service = NULL;
  27. //
  28. // FUNCTION: CServiceBase::Run(CServiceBase &)
  29. //
  30. // PURPOSE: Register the executable for a service with the Service Control
  31. // Manager (SCM). After you call Run(ServiceBase), the SCM issues a Start
  32. // command, which results in a call to the OnStart method in the service.
  33. // This method blocks until the service has stopped.
  34. //
  35. // PARAMETERS:
  36. // * service - the reference to a CServiceBase object. It will become the
  37. // singleton service instance of this service application.
  38. //
  39. // RETURN VALUE: If the function succeeds, the return value is TRUE. If the
  40. // function fails, the return value is FALSE. To get extended error
  41. // information, call GetLastError.
  42. //
  43. BOOL CServiceBase::Run(CServiceBase &service)
  44. {
  45. s_service = &service;
  46. SERVICE_TABLE_ENTRYA serviceTable[] =
  47. {
  48. { service.m_name, ServiceMain },
  49. { NULL, NULL }
  50. };
  51. // Connects the main thread of a service process to the service control
  52. // manager, which causes the thread to be the service control dispatcher
  53. // thread for the calling process. This call returns when the service has
  54. // stopped. The process should simply terminate when the call returns.
  55. return StartServiceCtrlDispatcher(serviceTable);
  56. }
  57. //
  58. // FUNCTION: CServiceBase::ServiceMain(DWORD, PWSTR *)
  59. //
  60. // PURPOSE: Entry point for the service. It registers the handler function
  61. // for the service and starts the service.
  62. //
  63. // PARAMETERS:
  64. // * dwArgc - number of command line arguments
  65. // * lpszArgv - array of command line arguments
  66. //
  67. void WINAPI CServiceBase::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
  68. {
  69. assert(s_service != NULL);
  70. // Register the handler function for the service
  71. s_service->m_statusHandle = RegisterServiceCtrlHandler(
  72. s_service->m_name, ServiceCtrlHandler);
  73. if (s_service->m_statusHandle == NULL)
  74. {
  75. throw GetLastError();
  76. }
  77. // Start the service.
  78. s_service->Start(dwArgc, pszArgv);
  79. }
  80. //
  81. // FUNCTION: CServiceBase::ServiceCtrlHandler(DWORD)
  82. //
  83. // PURPOSE: The function is called by the SCM whenever a control code is
  84. // sent to the service.
  85. //
  86. // PARAMETERS:
  87. // * dwCtrlCode - the control code. This parameter can be one of the
  88. // following values:
  89. //
  90. // SERVICE_CONTROL_CONTINUE
  91. // SERVICE_CONTROL_INTERROGATE
  92. // SERVICE_CONTROL_NETBINDADD
  93. // SERVICE_CONTROL_NETBINDDISABLE
  94. // SERVICE_CONTROL_NETBINDREMOVE
  95. // SERVICE_CONTROL_PARAMCHANGE
  96. // SERVICE_CONTROL_PAUSE
  97. // SERVICE_CONTROL_SHUTDOWN
  98. // SERVICE_CONTROL_STOP
  99. //
  100. // This parameter can also be a user-defined control code ranges from 128
  101. // to 255.
  102. //
  103. void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl)
  104. {
  105. switch (dwCtrl)
  106. {
  107. case SERVICE_CONTROL_STOP: s_service->Stop(); break;
  108. case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
  109. case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
  110. case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
  111. case SERVICE_CONTROL_INTERROGATE: break;
  112. default: break;
  113. }
  114. }
  115. #pragma endregion
  116. #pragma region Service Constructor and Destructor
  117. //
  118. // FUNCTION: CServiceBase::CServiceBase(PWSTR, BOOL, BOOL, BOOL)
  119. //
  120. // PURPOSE: The constructor of CServiceBase. It initializes a new instance
  121. // of the CServiceBase class. The optional parameters (fCanStop,
  122. /// fCanShutdown and fCanPauseContinue) allow you to specify whether the
  123. // service can be stopped, paused and continued, or be notified when system
  124. // shutdown occurs.
  125. //
  126. // PARAMETERS:
  127. // * pszServiceName - the name of the service
  128. // * fCanStop - the service can be stopped
  129. // * fCanShutdown - the service is notified when system shutdown occurs
  130. // * fCanPauseContinue - the service can be paused and continued
  131. //
  132. CServiceBase::CServiceBase(PSTR pszServiceName,
  133. BOOL fCanStop,
  134. BOOL fCanShutdown,
  135. BOOL fCanPauseContinue)
  136. {
  137. // Service name must be a valid string and cannot be NULL.
  138. m_name = (pszServiceName == NULL) ? "" : pszServiceName;
  139. m_statusHandle = NULL;
  140. // The service runs in its own process.
  141. m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  142. // The service is starting.
  143. m_status.dwCurrentState = SERVICE_START_PENDING;
  144. // The accepted commands of the service.
  145. DWORD dwControlsAccepted = 0;
  146. if (fCanStop)
  147. dwControlsAccepted |= SERVICE_ACCEPT_STOP;
  148. if (fCanShutdown)
  149. dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
  150. if (fCanPauseContinue)
  151. dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
  152. m_status.dwControlsAccepted = dwControlsAccepted;
  153. m_status.dwWin32ExitCode = NO_ERROR;
  154. m_status.dwServiceSpecificExitCode = 0;
  155. m_status.dwCheckPoint = 0;
  156. m_status.dwWaitHint = 0;
  157. }
  158. //
  159. // FUNCTION: CServiceBase::~CServiceBase()
  160. //
  161. // PURPOSE: The virtual destructor of CServiceBase.
  162. //
  163. CServiceBase::~CServiceBase(void)
  164. {
  165. }
  166. #pragma endregion
  167. #pragma region Service Start, Stop, Pause, Continue, and Shutdown
  168. //
  169. // FUNCTION: CServiceBase::Start(DWORD, PWSTR *)
  170. //
  171. // PURPOSE: The function starts the service. It calls the OnStart virtual
  172. // function in which you can specify the actions to take when the service
  173. // starts. If an error occurs during the startup, the error will be logged
  174. // in the Application event log, and the service will be stopped.
  175. //
  176. // PARAMETERS:
  177. // * dwArgc - number of command line arguments
  178. // * lpszArgv - array of command line arguments
  179. //
  180. void CServiceBase::Start(DWORD dwArgc, PSTR *pszArgv)
  181. {
  182. try
  183. {
  184. // Tell SCM that the service is starting.
  185. SetServiceStatus(SERVICE_START_PENDING);
  186. // Perform service-specific initialization.
  187. OnStart(dwArgc, pszArgv);
  188. // Tell SCM that the service is started.
  189. SetServiceStatus(SERVICE_RUNNING);
  190. }
  191. catch (DWORD dwError)
  192. {
  193. // Log the error.
  194. WriteErrorLogEntry("Service Start", dwError);
  195. // Set the service status to be stopped.
  196. SetServiceStatus(SERVICE_STOPPED, dwError);
  197. }
  198. catch (...)
  199. {
  200. // Log the error.
  201. WriteEventLogEntry("Service failed to start.", EVENTLOG_ERROR_TYPE);
  202. // Set the service status to be stopped.
  203. SetServiceStatus(SERVICE_STOPPED);
  204. }
  205. }
  206. //
  207. // FUNCTION: CServiceBase::OnStart(DWORD, PWSTR *)
  208. //
  209. // PURPOSE: When implemented in a derived class, executes when a Start
  210. // command is sent to the service by the SCM or when the operating system
  211. // starts (for a service that starts automatically). Specifies actions to
  212. // take when the service starts. Be sure to periodically call
  213. // CServiceBase::SetServiceStatus() with SERVICE_START_PENDING if the
  214. // procedure is going to take long time. You may also consider spawning a
  215. // new thread in OnStart to perform time-consuming initialization tasks.
  216. //
  217. // PARAMETERS:
  218. // * dwArgc - number of command line arguments
  219. // * lpszArgv - array of command line arguments
  220. //
  221. void CServiceBase::OnStart(DWORD dwArgc, PSTR *pszArgv)
  222. {
  223. }
  224. //
  225. // FUNCTION: CServiceBase::Stop()
  226. //
  227. // PURPOSE: The function stops the service. It calls the OnStop virtual
  228. // function in which you can specify the actions to take when the service
  229. // stops. If an error occurs, the error will be logged in the Application
  230. // event log, and the service will be restored to the original state.
  231. //
  232. void CServiceBase::Stop()
  233. {
  234. DWORD dwOriginalState = m_status.dwCurrentState;
  235. try
  236. {
  237. // Tell SCM that the service is stopping.
  238. SetServiceStatus(SERVICE_STOP_PENDING);
  239. // Perform service-specific stop operations.
  240. OnStop();
  241. // Tell SCM that the service is stopped.
  242. SetServiceStatus(SERVICE_STOPPED);
  243. }
  244. catch (DWORD dwError)
  245. {
  246. // Log the error.
  247. WriteErrorLogEntry("Service Stop", dwError);
  248. // Set the orginal service status.
  249. SetServiceStatus(dwOriginalState);
  250. }
  251. catch (...)
  252. {
  253. // Log the error.
  254. WriteEventLogEntry("Service failed to stop.", EVENTLOG_ERROR_TYPE);
  255. // Set the orginal service status.
  256. SetServiceStatus(dwOriginalState);
  257. }
  258. }
  259. //
  260. // FUNCTION: CServiceBase::OnStop()
  261. //
  262. // PURPOSE: When implemented in a derived class, executes when a Stop
  263. // command is sent to the service by the SCM. Specifies actions to take
  264. // when a service stops running. Be sure to periodically call
  265. // CServiceBase::SetServiceStatus() with SERVICE_STOP_PENDING if the
  266. // procedure is going to take long time.
  267. //
  268. void CServiceBase::OnStop()
  269. {
  270. }
  271. //
  272. // FUNCTION: CServiceBase::Pause()
  273. //
  274. // PURPOSE: The function pauses the service if the service supports pause
  275. // and continue. It calls the OnPause virtual function in which you can
  276. // specify the actions to take when the service pauses. If an error occurs,
  277. // the error will be logged in the Application event log, and the service
  278. // will become running.
  279. //
  280. void CServiceBase::Pause()
  281. {
  282. try
  283. {
  284. // Tell SCM that the service is pausing.
  285. SetServiceStatus(SERVICE_PAUSE_PENDING);
  286. // Perform service-specific pause operations.
  287. OnPause();
  288. // Tell SCM that the service is paused.
  289. SetServiceStatus(SERVICE_PAUSED);
  290. }
  291. catch (DWORD dwError)
  292. {
  293. // Log the error.
  294. WriteErrorLogEntry("Service Pause", dwError);
  295. // Tell SCM that the service is still running.
  296. SetServiceStatus(SERVICE_RUNNING);
  297. }
  298. catch (...)
  299. {
  300. // Log the error.
  301. WriteEventLogEntry("Service failed to pause.", EVENTLOG_ERROR_TYPE);
  302. // Tell SCM that the service is still running.
  303. SetServiceStatus(SERVICE_RUNNING);
  304. }
  305. }
  306. //
  307. // FUNCTION: CServiceBase::OnPause()
  308. //
  309. // PURPOSE: When implemented in a derived class, executes when a Pause
  310. // command is sent to the service by the SCM. Specifies actions to take
  311. // when a service pauses.
  312. //
  313. void CServiceBase::OnPause()
  314. {
  315. }
  316. //
  317. // FUNCTION: CServiceBase::Continue()
  318. //
  319. // PURPOSE: The function resumes normal functioning after being paused if
  320. // the service supports pause and continue. It calls the OnContinue virtual
  321. // function in which you can specify the actions to take when the service
  322. // continues. If an error occurs, the error will be logged in the
  323. // Application event log, and the service will still be paused.
  324. //
  325. void CServiceBase::Continue()
  326. {
  327. try
  328. {
  329. // Tell SCM that the service is resuming.
  330. SetServiceStatus(SERVICE_CONTINUE_PENDING);
  331. // Perform service-specific continue operations.
  332. OnContinue();
  333. // Tell SCM that the service is running.
  334. SetServiceStatus(SERVICE_RUNNING);
  335. }
  336. catch (DWORD dwError)
  337. {
  338. // Log the error.
  339. WriteErrorLogEntry("Service Continue", dwError);
  340. // Tell SCM that the service is still paused.
  341. SetServiceStatus(SERVICE_PAUSED);
  342. }
  343. catch (...)
  344. {
  345. // Log the error.
  346. WriteEventLogEntry("Service failed to resume.", EVENTLOG_ERROR_TYPE);
  347. // Tell SCM that the service is still paused.
  348. SetServiceStatus(SERVICE_PAUSED);
  349. }
  350. }
  351. //
  352. // FUNCTION: CServiceBase::OnContinue()
  353. //
  354. // PURPOSE: When implemented in a derived class, OnContinue runs when a
  355. // Continue command is sent to the service by the SCM. Specifies actions to
  356. // take when a service resumes normal functioning after being paused.
  357. //
  358. void CServiceBase::OnContinue()
  359. {
  360. }
  361. //
  362. // FUNCTION: CServiceBase::Shutdown()
  363. //
  364. // PURPOSE: The function executes when the system is shutting down. It
  365. // calls the OnShutdown virtual function in which you can specify what
  366. // should occur immediately prior to the system shutting down. If an error
  367. // occurs, the error will be logged in the Application event log.
  368. //
  369. void CServiceBase::Shutdown()
  370. {
  371. try
  372. {
  373. // Perform service-specific shutdown operations.
  374. OnShutdown();
  375. // Tell SCM that the service is stopped.
  376. SetServiceStatus(SERVICE_STOPPED);
  377. }
  378. catch (DWORD dwError)
  379. {
  380. // Log the error.
  381. WriteErrorLogEntry("Service Shutdown", dwError);
  382. }
  383. catch (...)
  384. {
  385. // Log the error.
  386. WriteEventLogEntry("Service failed to shut down.", EVENTLOG_ERROR_TYPE);
  387. }
  388. }
  389. //
  390. // FUNCTION: CServiceBase::OnShutdown()
  391. //
  392. // PURPOSE: When implemented in a derived class, executes when the system
  393. // is shutting down. Specifies what should occur immediately prior to the
  394. // system shutting down.
  395. //
  396. void CServiceBase::OnShutdown()
  397. {
  398. }
  399. #pragma endregion
  400. #pragma region Helper Functions
  401. //
  402. // FUNCTION: CServiceBase::SetServiceStatus(DWORD, DWORD, DWORD)
  403. //
  404. // PURPOSE: The function sets the service status and reports the status to
  405. // the SCM.
  406. //
  407. // PARAMETERS:
  408. // * dwCurrentState - the state of the service
  409. // * dwWin32ExitCode - error code to report
  410. // * dwWaitHint - estimated time for pending operation, in milliseconds
  411. //
  412. void CServiceBase::SetServiceStatus(DWORD dwCurrentState,
  413. DWORD dwWin32ExitCode,
  414. DWORD dwWaitHint)
  415. {
  416. static DWORD dwCheckPoint = 1;
  417. // Fill in the SERVICE_STATUS structure of the service.
  418. m_status.dwCurrentState = dwCurrentState;
  419. m_status.dwWin32ExitCode = dwWin32ExitCode;
  420. m_status.dwWaitHint = dwWaitHint;
  421. m_status.dwCheckPoint =
  422. ((dwCurrentState == SERVICE_RUNNING) ||
  423. (dwCurrentState == SERVICE_STOPPED)) ?
  424. 0 : dwCheckPoint++;
  425. // Report the status of the service to the SCM.
  426. ::SetServiceStatus(m_statusHandle, &m_status);
  427. }
  428. //
  429. // FUNCTION: CServiceBase::WriteEventLogEntry(PWSTR, WORD)
  430. //
  431. // PURPOSE: Log a message to the Application event log.
  432. //
  433. // PARAMETERS:
  434. // * pszMessage - string message to be logged.
  435. // * wType - the type of event to be logged. The parameter can be one of
  436. // the following values.
  437. //
  438. // EVENTLOG_SUCCESS
  439. // EVENTLOG_AUDIT_FAILURE
  440. // EVENTLOG_AUDIT_SUCCESS
  441. // EVENTLOG_ERROR_TYPE
  442. // EVENTLOG_INFORMATION_TYPE
  443. // EVENTLOG_WARNING_TYPE
  444. //
  445. void CServiceBase::WriteEventLogEntry(PSTR pszMessage, WORD wType)
  446. {
  447. HANDLE hEventSource = NULL;
  448. LPCSTR lpszStrings[2] = { NULL, NULL };
  449. hEventSource = RegisterEventSource(NULL, m_name);
  450. if (hEventSource)
  451. {
  452. lpszStrings[0] = m_name;
  453. lpszStrings[1] = pszMessage;
  454. ReportEvent(hEventSource, // Event log handle
  455. wType, // Event type
  456. 0, // Event category
  457. 0, // Event identifier
  458. NULL, // No security identifier
  459. 2, // Size of lpszStrings array
  460. 0, // No binary data
  461. lpszStrings, // Array of strings
  462. NULL // No binary data
  463. );
  464. DeregisterEventSource(hEventSource);
  465. }
  466. }
  467. //
  468. // FUNCTION: CServiceBase::WriteErrorLogEntry(PWSTR, DWORD)
  469. //
  470. // PURPOSE: Log an error message to the Application event log.
  471. //
  472. // PARAMETERS:
  473. // * pszFunction - the function that gives the error
  474. // * dwError - the error code
  475. //
  476. void CServiceBase::WriteErrorLogEntry(PSTR pszFunction, DWORD dwError)
  477. {
  478. char szMessage[260];
  479. StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
  480. "%s failed w/err 0x%08lx", pszFunction, dwError);
  481. WriteEventLogEntry(szMessage, EVENTLOG_ERROR_TYPE);
  482. }
  483. #pragma endregion