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.

400 lines
12 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. CNTSERV.CPP
  5. Abstract:
  6. A class which allows easy creation of Win32 Services. This class
  7. only allows one service per .EXE file. The process can be run as a
  8. service or a regular non-service EXE, a runtime option.
  9. This class is largly based on the SMS CService class which was created by
  10. a-raymcc. This differs in that it is simplified in two ways; First, it
  11. does not keep track of the worker threads since that is the responsibility
  12. of the derived code, and second, it doesnt use some SMS specific diagnostics
  13. NOTE: See the file SERVICE.TXT for details on how to use this class.
  14. There are a number of issues which cannot be conveyed by simply studying
  15. the class declaration.
  16. History:
  17. a-davj 20-June-96 Created.
  18. --*/
  19. #include "precomp.h"
  20. #include <wtypes.h>
  21. #include <stdio.h>
  22. #include "cntserv.h"
  23. //****************************************************************************
  24. //
  25. // CNtService::CNtService
  26. // CNtService::~CNtService
  27. //
  28. // Constructor and destructor.
  29. //
  30. //****************************************************************************
  31. CNtService::CNtService(DWORD ControlAccepted)
  32. {
  33. m_dwCtrlAccepted = ControlAccepted;
  34. m_bStarted = FALSE;
  35. m_pszServiceName = NULL;
  36. }
  37. CNtService::~CNtService()
  38. {
  39. if(m_pszServiceName)
  40. delete m_pszServiceName;
  41. }
  42. //
  43. //
  44. // CNtService::Run
  45. //
  46. //
  47. //////////////////////////////////////////////////////////////////
  48. DWORD CNtService::Run(LPWSTR pszServiceName,
  49. DWORD dwNumServicesArgs,
  50. LPWSTR *lpServiceArgVectors,
  51. PVOID lpData)
  52. {
  53. m_pszServiceName = new TCHAR[lstrlen(pszServiceName)+1];
  54. if(m_pszServiceName == NULL)
  55. return ERROR_NOT_ENOUGH_MEMORY;
  56. lstrcpyW(m_pszServiceName,pszServiceName);
  57. // Register our service control handler.
  58. // =====================================
  59. sshStatusHandle = RegisterServiceCtrlHandlerEx(m_pszServiceName,
  60. (LPHANDLER_FUNCTION_EX)CNtService::_HandlerEx,
  61. lpData);
  62. if (!sshStatusHandle)
  63. {
  64. Log(TEXT("Initial call to RegisterServiceCtrlHandler failed"));
  65. goto cleanup;
  66. }
  67. ssStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  68. ssStatus.dwServiceSpecificExitCode = 0;
  69. // Report the status to the service control manager.
  70. // =================================================
  71. if (!ReportStatusToSCMgr(
  72. SERVICE_START_PENDING, // service state
  73. NO_ERROR, // exit code
  74. 1, // checkpoint
  75. DEFAULT_WAIT_HINT)) // wait hint
  76. goto cleanup;
  77. if (!Initialize(dwNumServicesArgs, lpServiceArgVectors))
  78. {
  79. Log(TEXT("Initialize call failed, bailing out"));
  80. goto cleanup;
  81. }
  82. // Report the status to the service control manager.
  83. // =================================================
  84. if (!ReportStatusToSCMgr(
  85. SERVICE_RUNNING, // service state
  86. NO_ERROR, // exit code
  87. 0, // checkpoint
  88. 0)) // wait hint
  89. goto cleanup;
  90. m_bStarted = TRUE;
  91. // The next routine is always over ridden and is
  92. // where the acutal work of the service is done.
  93. // =============================================
  94. WorkerThread();
  95. // Service is done, send last report to SCM.
  96. // =========================================
  97. cleanup:
  98. m_bStarted = FALSE;
  99. //
  100. //
  101. // we cannot rely on the distructor to be called after
  102. // the SetServiceStatus(STOPPED) to perform operations
  103. //
  104. /////////////////////////////////////////////////////////
  105. FinalCleanup();
  106. ReportStatusToSCMgr(
  107. SERVICE_STOPPED, // service state
  108. NO_ERROR, // exit code
  109. 0, // checkpoint
  110. 0); // wait hint
  111. return 0;
  112. }
  113. //
  114. //
  115. // CNtService::Log
  116. //
  117. //
  118. //////////////////////////////////////////////////////////////////
  119. VOID CNtService::Log(LPCTSTR lpszMsg)
  120. {
  121. TCHAR szMsg[256];
  122. HANDLE hEventSource;
  123. LPCTSTR lpszStrings[2];
  124. DWORD dwErr = GetLastError();
  125. wsprintf(szMsg, TEXT("%s error: %d"), m_pszServiceName, dwErr);
  126. // Dump the error code and text message out to the event log
  127. hEventSource = RegisterEventSource(NULL, m_pszServiceName);
  128. lpszStrings[0] = szMsg;
  129. lpszStrings[1] = lpszMsg;
  130. if (hEventSource != NULL) {
  131. ReportEvent(hEventSource, // handle of event source
  132. EVENTLOG_ERROR_TYPE, // event type
  133. 0, // event category
  134. 0, // event ID
  135. NULL, // current user's SID
  136. 2, // strings in lpszStrings
  137. 0, // no bytes of raw data
  138. lpszStrings, // array of error strings
  139. NULL); // no raw data
  140. (VOID) DeregisterEventSource(hEventSource);
  141. }
  142. }
  143. //****************************************************************************
  144. //
  145. // CNtService::_Handler
  146. //
  147. // Entry points for calls from the NT service control manager. These entry
  148. // points just call the actual functions using the default object.
  149. //
  150. //****************************************************************************
  151. DWORD WINAPI CNtService::_HandlerEx(
  152. DWORD dwControl, // requested control code
  153. DWORD dwEventType, // event type
  154. LPVOID lpEventData, // event data
  155. LPVOID lpContext // user-defined context data
  156. )
  157. {
  158. if (lpContext)
  159. {
  160. return ((CNtService *)lpContext)->HandlerEx(dwControl,dwEventType,lpEventData,lpContext);
  161. }
  162. else
  163. {
  164. DebugBreak();
  165. return ERROR_INVALID_PARAMETER;
  166. }
  167. }
  168. //****************************************************************************
  169. //
  170. // CNtService::ReportStatusToSCMgr
  171. //
  172. // Used by other member functions to report their status to the
  173. // service control manager.
  174. //
  175. // Parameters:
  176. // DWORD dwCurrentState One of the SERVICE_ codes.
  177. // DWORD dwWin32ExitCode A Win32 Error code; usually 0.
  178. // DWORD dwCheckPoint Checkpoint value (not used).
  179. // DWORD dwWaitHint Milliseconds before Service Control
  180. // Manager gets worried.
  181. // Returns:
  182. //
  183. // BOOL fResult Whatever code was returned
  184. // by SetServiceStatus().
  185. //
  186. //****************************************************************************
  187. BOOL CNtService::ReportStatusToSCMgr(DWORD dwCurrentState,
  188. DWORD dwWin32ExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
  189. {
  190. BOOL fResult;
  191. // Disable control requests until the service is started.
  192. // ======================================================
  193. if (dwCurrentState == SERVICE_START_PENDING)
  194. {
  195. ssStatus.dwControlsAccepted = 0;
  196. }
  197. else if (dwCurrentState == SERVICE_STOPPED)
  198. {
  199. ssStatus.dwControlsAccepted = 0;
  200. }
  201. else
  202. {
  203. ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN |
  204. m_dwCtrlAccepted;
  205. }
  206. // These SERVICE_STATUS members are set from parameters.
  207. // =====================================================
  208. ssStatus.dwCurrentState = dwCurrentState;
  209. ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  210. ssStatus.dwCheckPoint = dwCheckPoint;
  211. ssStatus.dwWaitHint = dwWaitHint;
  212. // Report the status of the service to the service control manager.
  213. // ================================================================
  214. if (!(fResult = SetServiceStatus(
  215. sshStatusHandle, // service reference handle
  216. &ssStatus)))
  217. {
  218. // If an error occurs, log it.
  219. // =====================================
  220. Log(TEXT("Could not SetServiceStatus"));
  221. }
  222. //Sleep(250); // Give SC Man a chance to read this
  223. return fResult;
  224. }
  225. //*****************************************************************************
  226. //
  227. // CNtService::Handler
  228. //
  229. // This handles incoming messages from the Service Controller.
  230. //
  231. // Parameters:
  232. //
  233. // DWORD dwControlCode One of the SERVICE_CONTROL_
  234. // codes or a user defined code 125..255.
  235. //
  236. //*****************************************************************************
  237. DWORD WINAPI
  238. CNtService::HandlerEx( DWORD dwControl, // requested control code
  239. DWORD dwEventType, // event type
  240. LPVOID lpEventData, // event data
  241. LPVOID lpContext // user-defined context data
  242. )
  243. {
  244. switch(dwControl) {
  245. // Pause, set initial status, call overriden function and set final status
  246. //========================================================================
  247. case SERVICE_CONTROL_PAUSE:
  248. ReportStatusToSCMgr(
  249. SERVICE_PAUSE_PENDING, // current state
  250. NO_ERROR, // exit code
  251. 1, // checkpoint
  252. DEFAULT_WAIT_HINT); // wait hint
  253. Pause();
  254. ReportStatusToSCMgr(
  255. SERVICE_PAUSED, // current state
  256. NO_ERROR, // exit code
  257. 0, // checkpoint
  258. 0); // wait hint
  259. break;
  260. // Continue, set initial status, call overriden function and set final status
  261. //===========================================================================
  262. case SERVICE_CONTROL_CONTINUE:
  263. ReportStatusToSCMgr(
  264. SERVICE_CONTINUE_PENDING, // current state
  265. NO_ERROR, // exit code
  266. 1, // checkpoint
  267. DEFAULT_WAIT_HINT); // wait hint
  268. Continue();
  269. ReportStatusToSCMgr(
  270. SERVICE_RUNNING, // current state
  271. NO_ERROR, // exit code
  272. 0, // checkpoint
  273. 0); // wait hint
  274. break;
  275. // Stop the service. Note that the Stop function is supposed
  276. // to signal the worker thread which should return which then
  277. // causes the StartMain() function to end which sends the
  278. // final status!
  279. //==========================================================
  280. case SERVICE_CONTROL_SHUTDOWN:
  281. case SERVICE_CONTROL_STOP:
  282. ReportStatusToSCMgr(
  283. SERVICE_STOP_PENDING, // current state
  284. NO_ERROR, // exit code
  285. 1, // checkpoint
  286. DEFAULT_WAIT_HINT); // wait hint
  287. Stop((dwControl == SERVICE_CONTROL_SHUTDOWN)?TRUE:FALSE);
  288. break;
  289. // Could get an interrogate at any time, just report the current status.
  290. //======================================================================
  291. case SERVICE_CONTROL_INTERROGATE:
  292. ReportStatusToSCMgr(
  293. ssStatus.dwCurrentState, // current state
  294. NO_ERROR, // exit code
  295. 1, // checkpoint
  296. DEFAULT_WAIT_HINT); // wait hint
  297. break;
  298. // Some user defined code. Call the overriden function and report status.
  299. //========================================================================
  300. default:
  301. ReportStatusToSCMgr(
  302. ssStatus.dwCurrentState, // current state
  303. NO_ERROR, // exit code
  304. 1, // checkpoint
  305. DEFAULT_WAIT_HINT); // wait hint
  306. return ERROR_CALL_NOT_IMPLEMENTED;
  307. }
  308. return NO_ERROR;
  309. }