1 // Turn on logging to the event log.
2 #define LOGEVENTS
3
4 using System;
5 using System.IO;
6 using System.Threading;
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Data;
10 using System.Diagnostics;
11 using System.ServiceProcess;
12 using System.Text;
13 using Microsoft.Win32;
14 using System.Runtime.InteropServices;
15 using System.Windows.Forms;
16
17 namespace ServiceSample
18 {
19 // Define custom commands for the SimpleService.
20 public enum SimpleServiceCustomCommands { StopWorker = 128, RestartWorker, CheckWorker };
21 [StructLayout(LayoutKind.Sequential)]
22 public struct SERVICE_STATUS
23 {
24 public int serviceType;
25 public int currentState;
26 public int controlsAccepted;
27 public int win32ExitCode;
28 public int serviceSpecificExitCode;
29 public int checkPoint;
30 public int waitHint;
31 }
32
33 public enum State
34 {
35 SERVICE_STOPPED = 0x00000001,
36 SERVICE_START_PENDING = 0x00000002,
37 SERVICE_STOP_PENDING = 0x00000003,
38 SERVICE_RUNNING = 0x00000004,
39 SERVICE_CONTINUE_PENDING = 0x00000005,
40 SERVICE_PAUSE_PENDING = 0x00000006,
41 SERVICE_PAUSED = 0x00000007,
42 }
43
44 // Define a simple service implementation.
45 public class SimpleService : System.ServiceProcess.ServiceBase
46 {
47 private static int userCount = 0;
48 private static ManualResetEvent pause = new ManualResetEvent(false);
49
50 [DllImport("ADVAPI32.DLL", EntryPoint = "SetServiceStatus")]
51 public static extern bool SetServiceStatus(
52 IntPtr hServiceStatus,
53 SERVICE_STATUS lpServiceStatus
54 );
55 private SERVICE_STATUS myServiceStatus;
56
57 private Thread workerThread = null;
58
59 public SimpleService()
60 {
61 CanPauseAndContinue = true;
62 CanHandleSessionChangeEvent = true;
63 ServiceName = "SimpleService";
64 }
65
66 static void Main()
67 {
68 #if LOGEVENTS
69 EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
70 " - Service main method starting...");
71 #endif
72
73 // Load the service into memory.
74 System.ServiceProcess.ServiceBase.Run(new SimpleService());
75
76 #if LOGEVENTS
77 EventLog.WriteEntry("SimpleService.Main", DateTime.Now.ToLongTimeString() +
78 " - Service main method exiting...");
79 #endif
80
81 }
82
83 private void InitializeComponent()
84 {
85 // Initialize the operating properties for the service.
86 this.CanPauseAndContinue = true;
87 this.CanShutdown = true;
88 this.CanHandleSessionChangeEvent = true;
89 this.ServiceName = "SimpleService";
90 }
91
92 // Start the service.
93 protected override void OnStart(string[] args)
94 {
95 IntPtr handle = this.ServiceHandle;
96 myServiceStatus.currentState = (int)State.SERVICE_START_PENDING;
97 SetServiceStatus(handle, myServiceStatus);
98
99 // Start a separate thread that does the actual work.
100
101 if ((workerThread == null) ||
102 ((workerThread.ThreadState &
103 (System.Threading.ThreadState.Unstarted | System.Threading.ThreadState.Stopped)) != 0))
104 {
105 #if LOGEVENTS
106 EventLog.WriteEntry("SimpleService.OnStart", DateTime.Now.ToLongTimeString() +
107 " - Starting the service worker thread.");
108 #endif
109
110 workerThread = new Thread(new ThreadStart(ServiceWorkerMethod));
111 workerThread.Start();
112 }
113 if (workerThread != null)
114 {
115 #if LOGEVENTS
116 EventLog.WriteEntry("SimpleService.OnStart", DateTime.Now.ToLongTimeString() +
117 " - Worker thread state = " +
118 workerThread.ThreadState.ToString());
119 #endif
120 }
121 myServiceStatus.currentState = (int)State.SERVICE_RUNNING;
122 SetServiceStatus(handle, myServiceStatus);
123
124 }
125
126 // Stop this service.
127 protected override void OnStop()
128 {
129 // New in .NET Framework version 2.0.
130 this.RequestAdditionalTime(4000);
131 // Signal the worker thread to exit.
132 if ((workerThread != null) && (workerThread.IsAlive))
133 {
134 #if LOGEVENTS
135 EventLog.WriteEntry("SimpleService.OnStop", DateTime.Now.ToLongTimeString() +
136 " - Stopping the service worker thread.");
137 #endif
138 pause.Reset();
139 Thread.Sleep(5000);
140 workerThread.Abort();
141
142 }
143 if (workerThread != null)
144 {
145 #if LOGEVENTS
146 EventLog.WriteEntry("SimpleService.OnStop", DateTime.Now.ToLongTimeString() +
147 " - OnStop Worker thread state = " +
148 workerThread.ThreadState.ToString());
149 #endif
150 }
151 // Indicate a successful exit.
152 this.ExitCode = 0;
153 }
154
155 // Pause the service.
156 protected override void OnPause()
157 {
158 // Pause the worker thread.
159 if ((workerThread != null) &&
160 (workerThread.IsAlive) &&
161 ((workerThread.ThreadState &
162 (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) == 0))
163 {
164 #if LOGEVENTS
165 EventLog.WriteEntry("SimpleService.OnPause", DateTime.Now.ToLongTimeString() +
166 " - Pausing the service worker thread.");
167 #endif
168
169 pause.Reset();
170 Thread.Sleep(5000);
171 }
172
173 if (workerThread != null)
174 {
175 #if LOGEVENTS
176 EventLog.WriteEntry("SimpleService.OnPause", DateTime.Now.ToLongTimeString() +
177 " OnPause - Worker thread state = " +
178 workerThread.ThreadState.ToString());
179 #endif
180 }
181 }
182
183 // Continue a paused service.
184 protected override void OnContinue()
185 {
186
187 // Signal the worker thread to continue.
188 if ((workerThread != null) &&
189 ((workerThread.ThreadState &
190 (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) != 0))
191 {
192 #if LOGEVENTS
193 EventLog.WriteEntry("SimpleService.OnContinue", DateTime.Now.ToLongTimeString() +
194 " - Resuming the service worker thread.");
195
196 #endif
197 pause.Set();
198 }
199 if (workerThread != null)
200 {
201 #if LOGEVENTS
202 EventLog.WriteEntry("SimpleService.OnContinue", DateTime.Now.ToLongTimeString() +
203 " OnContinue - Worker thread state = " +
204 workerThread.ThreadState.ToString());
205 #endif
206 }
207 }
208
209 // Handle a custom command.
210 protected override void OnCustomCommand(int command)
211 {
212 #if LOGEVENTS
213 EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
214 " - Custom command received: " +
215 command.ToString());
216 #endif
217
218 // If the custom command is recognized,
219 // signal the worker thread appropriately.
220
221 switch (command)
222 {
223 case (int)SimpleServiceCustomCommands.StopWorker:
224 // Signal the worker thread to terminate.
225 // For this custom command, the main service
226 // continues to run without a worker thread.
227 OnStop();
228 break;
229
230 case (int)SimpleServiceCustomCommands.RestartWorker:
231
232 // Restart the worker thread if necessary.
233 OnStart(null);
234 break;
235
236 case (int)SimpleServiceCustomCommands.CheckWorker:
237 #if LOGEVENTS
238 // Log the current worker thread state.
239 EventLog.WriteEntry("SimpleService.OnCustomCommand", DateTime.Now.ToLongTimeString() +
240 " OnCustomCommand - Worker thread state = " +
241 workerThread.ThreadState.ToString());
242 #endif
243
244 break;
245
246 default:
247 #if LOGEVENTS
248 EventLog.WriteEntry("SimpleService.OnCustomCommand",
249 DateTime.Now.ToLongTimeString());
250 #endif
251 break;
252 }
253 }
254 // Handle a session change notice
255 protected override void OnSessionChange(SessionChangeDescription changeDescription)
256 {
257 #if LOGEVENTS
258 EventLog.WriteEntry("SimpleService.OnSessionChange", DateTime.Now.ToLongTimeString() +
259 " - Session change notice received: " +
260 changeDescription.Reason.ToString() + " Session ID: " +
261 changeDescription.SessionId.ToString());
262 #endif
263
264 switch (changeDescription.Reason)
265 {
266 case SessionChangeReason.SessionLogon:
267 userCount += 1;
268 #if LOGEVENTS
269 EventLog.WriteEntry("SimpleService.OnSessionChange",
270 DateTime.Now.ToLongTimeString() +
271 " SessionLogon, total users: " +
272 userCount.ToString());
273 #endif
274 break;
275
276 case SessionChangeReason.SessionLogoff:
277
278 userCount -= 1;
279 #if LOGEVENTS
280 EventLog.WriteEntry("SimpleService.OnSessionChange",
281 DateTime.Now.ToLongTimeString() +
282 " SessionLogoff, total users: " +
283 userCount.ToString());
284 #endif
285 break;
286 case SessionChangeReason.RemoteConnect:
287 userCount += 1;
288 #if LOGEVENTS
289 EventLog.WriteEntry("SimpleService.OnSessionChange",
290 DateTime.Now.ToLongTimeString() +
291 " RemoteConnect, total users: " +
292 userCount.ToString());
293 #endif
294 break;
295
296 case SessionChangeReason.RemoteDisconnect:
297
298 userCount -= 1;
299 #if LOGEVENTS
300 EventLog.WriteEntry("SimpleService.OnSessionChange",
301 DateTime.Now.ToLongTimeString() +
302 " RemoteDisconnect, total users: " +
303 userCount.ToString());
304 #endif
305 break;
306 case SessionChangeReason.SessionLock:
307 #if LOGEVENTS
308 EventLog.WriteEntry("SimpleService.OnSessionChange",
309 DateTime.Now.ToLongTimeString() +
310 " SessionLock");
311 #endif
312 break;
313
314 case SessionChangeReason.SessionUnlock:
315 #if LOGEVENTS
316 EventLog.WriteEntry("SimpleService.OnSessionChange",
317 DateTime.Now.ToLongTimeString() +
318 " SessionUnlock");
319 #endif
320 break;
321
322 default:
323
324 break;
325 }
326 }
327 // Define a simple method that runs as the worker thread for
328 // the service.
329 public void ServiceWorkerMethod()
330 {
331 #if LOGEVENTS
332 EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
333 " - Starting the service worker thread.");
334 #endif
335
336 try
337 {
338 do
339 {
340 // Simulate 4 seconds of work.
341 Thread.Sleep(4000);
342 // Block if the service is paused or is shutting down.
343 pause.WaitOne();
344 #if LOGEVENTS
345 EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
346 " - heartbeat cycle.");
347 #endif
348 }
349 while (true);
350 }
351 catch (ThreadAbortException)
352 {
353 // Another thread has signalled that this worker
354 // thread must terminate. Typically, this occurs when
355 // the main service thread receives a service stop
356 // command.
357
358 // Write a trace line indicating that the worker thread
359 // is exiting. Notice that this simple thread does
360 // not have any local objects or data to clean up.
361 #if LOGEVENTS
362 EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
363 " - Thread abort signaled.");
364 #endif
365 }
366 #if LOGEVENTS
367
368 EventLog.WriteEntry("SimpleService.WorkerThread", DateTime.Now.ToLongTimeString() +
369 " - Exiting the service worker thread.");
370 #endif
371
372 }
373 }
374 }