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 }