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.

233 lines
8.6 KiB

  1. SERVICE.TXT
  2. This document contains information on how to use the CNtService class to
  3. produce a Win32 Service Executable. (6/24/96 a-davj)
  4. This is a simplified version of the CService class which was originally
  5. done by Ray McCollum. It is simpler chiefly because it is now the
  6. responsibility of the class user to manage their threads and the
  7. class does not rely on any SMS diagnostics.
  8. =========================================================================
  9. 1.1 A minimal conversion
  10. To produce a service from a normal EXE with the minimum work, you must
  11. derive a new class from CNtService and override the WorkerThread() and Stop()
  12. virtual functions:
  13. class MyService : public CNtService
  14. {
  15. public:
  16. virtual void WorkerThread(); // Your component goes here
  17. virtual void Stop(); // Your component goes here
  18. };
  19. void MyService::WorkerThread()
  20. {
  21. ...loop doing useful work until signaled by the Stop function.
  22. }
  23. void MyService::Stop()
  24. {
  25. ...Signal the thread running in WorkerThread to return from that
  26. ...routine
  27. }
  28. The WorkerThread() function is the entry point for your code. You can
  29. branch out to normal non-object functions, or whatever. It is the job of
  30. your Stop() function to signal the thread running in the WorkerFunction
  31. to return and when that thread returns, the service will terminate properly.
  32. Execution of the service is begun by using the Service Control Manager
  33. APIs in another program, the Control Panel or else using NET START/NET STOP/
  34. NET PAUSE/NET CONTINUE from a DOS box. When the user types
  35. NET START MYSERVICE
  36. ...your service will attempt to start.
  37. Note that you must have added your service to the SCM (Service Control
  38. Manager) data base before you can start it. There are various ways to add a
  39. service though I found the SC.EXE to be quite useful and it can be found
  40. via MSDN.
  41. Your main() entry point might be rewritten along the following
  42. lines:
  43. void main()
  44. {
  45. MyService Sample;
  46. Sample.Run(TRUE, "MYSERVICE"); // TRUE = run as a Service,
  47. // FALSE = run as a normal EXE
  48. }
  49. When Run() is called, you can make the decision as to whether you are
  50. running as a normal EXE or a service via the first argument. The
  51. second argument is the service name. This can be any length and is not
  52. related to the .EXE name itself.
  53. After Run() is invoked, then WorkerThread() is called to actually do
  54. the work of the service. [Run() and WorkerThread() are actually separate
  55. threads at this point, although Run() will not return until the service
  56. stops.]
  57. When the user types NET STOP MYSERVICE, then the Stop() function
  58. is called and it signals for the WorkerThread() function to
  59. return which then results in the service being stopped.
  60. =========================================================================
  61. 1.2 Supporting Pause, Continue, and custom control codes.
  62. If you want to support suspending your service by NET PAUSE MYSERVICE and
  63. resuming it by NET CONTINUE MYSERVICE, then you must override the Pause()
  64. and Continue() virtual functions and call the SetPauseContinue()
  65. function with a TRUE argument.
  66. It is the job of your Pause() and Continue() functions to do whatever is
  67. right for your service. In some cases that might mean actually suspending
  68. and resuming threads, or it could mean that the threads will just not perform
  69. certain functions.
  70. User codes can be accepted by overriding the UserCode() function.
  71. The UserCode() function is only valid if the program controlling
  72. the service has been coded to send user-defined control codes to the
  73. service. The command-line based NET functions have no facility for this.
  74. Basically, a specially coded control program can send codes other than
  75. STOP, PAUSE, CONTINUE or START. These codes must fall in the subrange
  76. 128..255 and the code being sent is in the DWORD argument.
  77. The meaning is user-defined. Since there is no response mechanism
  78. back to the sending process, this does not seem terribly useful. These
  79. codes are ignored if UserCode() is not overridden.
  80. Note that the original CService class did handle some specific UserCodes
  81. and this class does NOT.
  82. ===========================================================================
  83. 1.3 Logging support.
  84. The service wrapper code only logs unexpected errors. By default, it adds
  85. them to the NT event log when running as a service and to stdio when running
  86. as an executable. It does this through a virtual function named Log() which
  87. can be used for your code, or can be overridden if there is some other logging
  88. mechanism that is desired.
  89. =========================================================================
  90. 1.4 Timing Issues.
  91. Functions such as Pausing, Continuing and Stopping must complete in
  92. under 20 seconds. A delinquent Pause or Continue isn't too serious since
  93. that will only cause a error for the NET xyx command. However, the system
  94. will kill any service that is too slow in stopping.
  95. The Initialize() function is called during NET START, but before your
  96. worker thread begins execution. Again, this must return within 20
  97. seconds. In most cases, you will not need this and can do initializations
  98. in WorkerThread itself. Any command-line arguments passed in to the
  99. service are available from the parameters. These are only valid if the EXE
  100. is executing as a service; if you are executing as a normal EXE, then process
  101. the command line arguments from main() instead, since these will be NULL.
  102. In general, the command line arguments don't tend to be terribly useful for
  103. services.
  104. ========================================================================
  105. 1.5 Non-service EXE execution.
  106. If Run() is called with FALSE, then the service behaves as a normal
  107. multi-threaded EXE. The behavior is still basically the same as for a
  108. service: WorkerThread() should be the effective entry point for the work
  109. to get done. However, there will be no NET STOP, PAUSE , User Codes, or
  110. CONTINUE requests coming in, so your Pause(), Continue(), Initialize(),
  111. UserCode and Stop() functions will never be called.
  112. =========================================================================
  113. 1.6 Service Names
  114. Service names can contain any characters if you use the Win32 APIs or
  115. service control classes. However, the NET commands only support normal
  116. filename type characters. It is best to use alphanumeric characters with
  117. optional underscores.
  118. =========================================================================
  119. 1.7 An additional example and debugging hints.
  120. This sample uses the command line arguments to determine if the program
  121. should run as a service. It also handles Pause and Continue and it uses
  122. Initialize.
  123. This sample is also setup to INTENTIONALLY CAUSE A BREAK! The WorkerThread
  124. routine has some logic where by an INT 3 is caused 2 minutes into the run.
  125. This brings up an exception box which gives you the oportunity to debug the
  126. service even if it was automatically started by the system. Naturally you
  127. still need to be running a debug build.
  128. This code is a bit simplistic in that it uses simple variables
  129. to communicate pause and stop and a real production program would probably
  130. protect these variable via Mutex or use Signal(s).
  131. class MyService : public CNtService{
  132. public:
  133. MyService();
  134. DWORD WorkerThread();
  135. void Stop(){bRun = FALSE;return;};
  136. void Pause(){bContinue = FALSE;return;};
  137. void Continue(){bContinue = TRUE;return;};
  138. BOOL bRun, bContinue;
  139. };
  140. int main(int argc, char ** argv)
  141. {
  142. MyService svc;
  143. svc.SetPauseContinue(TRUE);
  144. if(argc > 1 && _stricmp(argv[1],"/EXE") == 0)
  145. {
  146. // run as console app. Note that Initialize will get the /EXE arg
  147. svc.Initialize(argc, argv);
  148. svc.Run("doesntMatter!", FALSE);
  149. }
  150. else
  151. svc.Run("MyService", TRUE);
  152. return 0;
  153. }
  154. MyService::MyService()
  155. {
  156. bRun = bContinue = TRUE;
  157. }
  158. DWORD MyService::WorkerThread()
  159. {
  160. int nCount = 0;
  161. while(bRun)
  162. {
  163. if(bContinue)
  164. {
  165. MessageBeep(0);
  166. }
  167. Sleep(3000);
  168. if(nCount == 40)
  169. _asm int 3;
  170. nCount++;
  171. }
  172. return 0;
  173. }
  174. =========================================================================
  175. 2.1 LIMITATIONS on the current implementation.
  176. A. Only one service is supported per .EXE. This limitation can be removed,
  177. however any having more than one service per EXE would require the
  178. service to run only in the Local Service account which is generally
  179. not acceptable.