win32-service 0.8.6 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,364 +1,364 @@
1
- require File.join(File.dirname(__FILE__), 'windows', 'helper')
2
- require File.join(File.dirname(__FILE__), 'windows', 'constants')
3
- require File.join(File.dirname(__FILE__), 'windows', 'structs')
4
- require File.join(File.dirname(__FILE__), 'windows', 'functions')
5
-
6
- require 'ffi'
7
-
8
- # The Win32 module serves as a namespace only.
9
- module Win32
10
-
11
- # The Daemon class
12
- class Daemon
13
- include Windows::ServiceConstants
14
- include Windows::Structs
15
- include Windows::Functions
16
-
17
- extend Windows::Structs
18
- extend Windows::Functions
19
-
20
- # The version of this library
21
- VERSION = '0.8.6'
22
-
23
- private
24
-
25
- # Service is not running
26
- STOPPED = SERVICE_STOPPED
27
-
28
- # Service has received a start signal but is not yet running
29
- START_PENDING = SERVICE_START_PENDING
30
-
31
- # Service has received a stop signal but is not yet stopped
32
- STOP_PENDING = SERVICE_STOP_PENDING
33
-
34
- # Service is running
35
- RUNNING = SERVICE_RUNNING
36
-
37
- # Service has received a signal to resume but is not yet running
38
- CONTINUE_PENDING = SERVICE_CONTINUE_PENDING
39
-
40
- # Service has received a signal to pause but is not yet paused
41
- PAUSE_PENDING = SERVICE_PAUSE_PENDING
42
-
43
- # Service is paused
44
- PAUSED = SERVICE_PAUSED
45
-
46
- # Service controls
47
-
48
- # Notifies service that it should stop
49
- CONTROL_STOP = SERVICE_CONTROL_STOP
50
-
51
- # Notifies service that it should pause
52
- CONTROL_PAUSE = SERVICE_CONTROL_PAUSE
53
-
54
- # Notifies service that it should resume
55
- CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE
56
-
57
- # Notifies service that it should return its current status information
58
- CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE
59
-
60
- # Notifies a service that its parameters have changed
61
- CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE
62
-
63
- # Notifies a service that there is a new component for binding
64
- CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD
65
-
66
- # Notifies a service that a component for binding has been removed
67
- CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE
68
-
69
- # Notifies a service that a component for binding has been enabled
70
- CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE
71
-
72
- # Notifies a service that a component for binding has been disabled
73
- CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE
74
-
75
- IDLE = 0
76
-
77
- # Wraps SetServiceStatus.
78
- SetTheServiceStatus = Proc.new do |dwCurrentState, dwWin32ExitCode,dwCheckPoint, dwWaitHint|
79
- ss = SERVICE_STATUS.new # Current status of the service.
80
-
81
- # Disable control requests until the service is started.
82
- if dwCurrentState == SERVICE_START_PENDING
83
- ss[:dwControlsAccepted] = 0
84
- else
85
- ss[:dwControlsAccepted] =
86
- SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
87
- SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN
88
- end
89
-
90
- # Initialize ss structure.
91
- ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS
92
- ss[:dwServiceSpecificExitCode] = 0
93
- ss[:dwCurrentState] = dwCurrentState
94
- ss[:dwWin32ExitCode] = dwWin32ExitCode
95
- ss[:dwCheckPoint] = dwCheckPoint
96
- ss[:dwWaitHint] = dwWaitHint
97
-
98
- @@dwServiceState = dwCurrentState
99
-
100
- # Send status of the service to the Service Controller.
101
- if !SetServiceStatus(@@ssh, ss)
102
- SetEvent(@@hStopEvent)
103
- end
104
- end
105
-
106
- ERROR_CALL_NOT_IMPLEMENTED = 0x78
107
-
108
- # Handles control signals from the service control manager.
109
- Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
110
- @@waiting_control_code = dwCtrlCode;
111
- return_value = NO_ERROR
112
-
113
- begin
114
- dwState = SERVICE_RUNNING
115
-
116
- case dwCtrlCode
117
- when SERVICE_CONTROL_STOP
118
- dwState = SERVICE_STOP_PENDING
119
- when SERVICE_CONTROL_SHUTDOWN
120
- dwState = SERVICE_STOP_PENDING
121
- when SERVICE_CONTROL_PAUSE
122
- dwState = SERVICE_PAUSED
123
- when SERVICE_CONTROL_CONTINUE
124
- dwState = SERVICE_RUNNING
125
- #else
126
- # TODO: Handle other control codes? Retain the current state?
127
- end
128
-
129
- # Set the status of the service except on interrogation.
130
- unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE
131
- SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
132
- end
133
-
134
- # Tell service_main thread to stop.
135
- if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
136
- if SetEvent(@@hStopEvent) == 0
137
- SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
138
- end
139
- end
140
- rescue
141
- return_value = ERROR_CALL_NOT_IMPLEMENTED
142
- end
143
-
144
- return_value
145
- end
146
-
147
- # Called by the service control manager after the call to StartServiceCtrlDispatcher.
148
- Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
149
- begin
150
- # Obtain the name of the service.
151
- if lpszArgv.address!=0
152
- argv = lpszArgv.get_array_of_string(0,dwArgc)
153
- lpszServiceName = argv[0]
154
- else
155
- lpszServiceName = ''
156
- end
157
-
158
- # Args passed to Service.start
159
- if(dwArgc > 1)
160
- @@Argv = argv[1..-1]
161
- else
162
- @@Argv = nil
163
- end
164
-
165
- # Register the service ctrl handler.
166
- @@ssh = RegisterServiceCtrlHandlerEx(
167
- lpszServiceName,
168
- Service_Ctrl_ex,
169
- nil
170
- )
171
-
172
- # No service to stop, no service handle to notify, nothing to do but exit.
173
- return if @@ssh == 0
174
-
175
- # The service has started.
176
- SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
177
-
178
- SetEvent(@@hStartEvent)
179
-
180
- # Main loop for the service.
181
- while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
182
- end
183
-
184
- # Main loop for the service.
185
- while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
186
- end
187
- ensure
188
- # Stop the service.
189
- SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
190
- end
191
- end
192
-
193
- ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
194
- ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
195
-
196
- s = SERVICE_TABLE_ENTRY.new(ste[0])
197
- s[:lpServiceName] = FFI::MemoryPointer.from_string('')
198
- s[:lpServiceProc] = lpParameter
199
-
200
- s = SERVICE_TABLE_ENTRY.new(ste[1])
201
- s[:lpServiceName] = nil
202
- s[:lpServiceProc] = nil
203
-
204
- # No service to step, no service handle, no ruby exceptions, just terminate the thread..
205
- if !StartServiceCtrlDispatcher(ste)
206
- return 1
207
- end
208
-
209
- return 0
210
- end
211
-
212
- public
213
-
214
- # This is a shortcut for Daemon.new + Daemon#mainloop.
215
- #
216
- def self.mainloop
217
- self.new.mainloop
218
- end
219
-
220
- # This is the method that actually puts your code into a loop and allows it
221
- # to run as a service. The code that is actually run while in the mainloop
222
- # is what you defined in your own Daemon#service_main method.
223
- #
224
- def mainloop
225
- @@waiting_control_code = IDLE_CONTROL_CODE
226
- @@dwServiceState = 0
227
-
228
- # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
229
- # associated with a tty. This helps newbs avoid Errno::EBADF errors.
230
- STDIN.reopen('NUL') if STDIN.isatty
231
- STDOUT.reopen('NUL') if STDOUT.isatty
232
- STDERR.reopen('NUL') if STDERR.isatty
233
-
234
- # Calling init here so that init failures never even tries to start the
235
- # service. Of course that means that init methods must be very quick
236
- # because the SCM will be receiving no START_PENDING messages while
237
- # init's running.
238
- #
239
- # TODO: Fix?
240
- service_init() if respond_to?('service_init')
241
-
242
- # Create the event to signal the service to start.
243
- @@hStartEvent = CreateEvent(nil, true, false, nil)
244
-
245
- if @@hStartEvent == 0
246
- raise SystemCallError.new('CreateEvent', FFI.errno)
247
- end
248
-
249
- # Create the event to signal the service to stop.
250
- @@hStopEvent = CreateEvent(nil, true, false, nil)
251
-
252
- if @@hStopEvent == 0
253
- raise SystemCallError.new('CreateEvent', FFI.errno)
254
- end
255
-
256
- # Create the event to signal the service that stop has completed
257
- @@hStopCompletedEvent = CreateEvent(nil, true, false, nil)
258
-
259
- if @@hStopCompletedEvent == 0
260
- raise SystemCallError.new('CreateEvent', FFI.errno)
261
- end
262
-
263
- hThread = CreateThread(nil, 0, ThreadProc, Service_Main, 0, nil)
264
-
265
- if hThread == 0
266
- raise SystemCallError.new('CreateThread', FFI.errno)
267
- end
268
-
269
- events = FFI::MemoryPointer.new(:pointer, 2)
270
- events.put_pointer(0, FFI::Pointer.new(hThread))
271
- events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
272
-
273
- while ((index = WaitForMultipleObjects(2, events, false, 1000)) == WAIT_TIMEOUT) do
274
- end
275
-
276
- if index == WAIT_FAILED
277
- raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
278
- end
279
-
280
- # The thread exited, so the show is off.
281
- if index == WAIT_OBJECT_0
282
- raise "Service_Main thread exited abnormally"
283
- end
284
-
285
- thr = Thread.new do
286
- begin
287
- while(WaitForSingleObject(@@hStopEvent, 10) == WAIT_TIMEOUT)
288
- # Check to see if anything interesting has been signaled
289
- case @@waiting_control_code
290
- when SERVICE_CONTROL_PAUSE
291
- service_pause() if respond_to?('service_pause')
292
- when SERVICE_CONTROL_CONTINUE
293
- service_resume() if respond_to?('service_resume')
294
- when SERVICE_CONTROL_INTERROGATE
295
- service_interrogate() if respond_to?('service_interrogate')
296
- when SERVICE_CONTROL_SHUTDOWN
297
- service_shutdown() if respond_to?('service_shutdown')
298
- when SERVICE_CONTROL_PARAMCHANGE
299
- service_paramchange() if respond_to?('service_paramchange')
300
- when SERVICE_CONTROL_NETBINDADD
301
- service_netbindadd() if respond_to?('service_netbindadd')
302
- when SERVICE_CONTROL_NETBINDREMOVE
303
- service_netbindremove() if respond_to?('service_netbindremove')
304
- when SERVICE_CONTROL_NETBINDENABLE
305
- service_netbindenable() if respond_to?('service_netbindenable')
306
- when SERVICE_CONTROL_NETBINDDISABLE
307
- service_netbinddisable() if respond_to?('service_netbinddisable')
308
- end
309
- @@waiting_control_code = IDLE_CONTROL_CODE
310
- end
311
-
312
- service_stop() if respond_to?('service_stop')
313
- ensure
314
- SetEvent(@@hStopCompletedEvent)
315
- end
316
- end
317
-
318
- if respond_to?('service_main')
319
- service_main(*@@Argv)
320
- end
321
-
322
- thr.join
323
- end
324
-
325
- # Returns the state of the service (as an constant integer) which can be any
326
- # of the service status constants, e.g. RUNNING, PAUSED, etc.
327
- #
328
- # This method is typically used within your service_main method to setup the
329
- # loop. For example:
330
- #
331
- # class MyDaemon < Daemon
332
- # def service_main
333
- # while state == RUNNING || state == PAUSED || state == IDLE
334
- # # Your main loop here
335
- # end
336
- # end
337
- # end
338
- #
339
- # See the Daemon#running? method for an abstraction of the above code.
340
- #
341
- def state
342
- @@dwServiceState
343
- end
344
-
345
- #
346
- # Returns whether or not the service is in a running state, i.e. the service
347
- # status is either RUNNING, PAUSED or IDLE.
348
- #
349
- # This is typically used within your service_main method to setup the main
350
- # loop. For example:
351
- #
352
- # class MyDaemon < Daemon
353
- # def service_main
354
- # while running?
355
- # # Your main loop here
356
- # end
357
- # end
358
- # end
359
- #
360
- def running?
361
- [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
362
- end
363
- end # Daemon
364
- end # Win32
1
+ require File.join(File.dirname(__FILE__), 'windows', 'helper')
2
+ require File.join(File.dirname(__FILE__), 'windows', 'constants')
3
+ require File.join(File.dirname(__FILE__), 'windows', 'structs')
4
+ require File.join(File.dirname(__FILE__), 'windows', 'functions')
5
+
6
+ require 'ffi'
7
+
8
+ # The Win32 module serves as a namespace only.
9
+ module Win32
10
+
11
+ # The Daemon class
12
+ class Daemon
13
+ include Windows::ServiceConstants
14
+ include Windows::Structs
15
+ include Windows::Functions
16
+
17
+ extend Windows::Structs
18
+ extend Windows::Functions
19
+
20
+ # The version of this library
21
+ VERSION = '0.8.6'
22
+
23
+ private
24
+
25
+ # Service is not running
26
+ STOPPED = SERVICE_STOPPED
27
+
28
+ # Service has received a start signal but is not yet running
29
+ START_PENDING = SERVICE_START_PENDING
30
+
31
+ # Service has received a stop signal but is not yet stopped
32
+ STOP_PENDING = SERVICE_STOP_PENDING
33
+
34
+ # Service is running
35
+ RUNNING = SERVICE_RUNNING
36
+
37
+ # Service has received a signal to resume but is not yet running
38
+ CONTINUE_PENDING = SERVICE_CONTINUE_PENDING
39
+
40
+ # Service has received a signal to pause but is not yet paused
41
+ PAUSE_PENDING = SERVICE_PAUSE_PENDING
42
+
43
+ # Service is paused
44
+ PAUSED = SERVICE_PAUSED
45
+
46
+ # Service controls
47
+
48
+ # Notifies service that it should stop
49
+ CONTROL_STOP = SERVICE_CONTROL_STOP
50
+
51
+ # Notifies service that it should pause
52
+ CONTROL_PAUSE = SERVICE_CONTROL_PAUSE
53
+
54
+ # Notifies service that it should resume
55
+ CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE
56
+
57
+ # Notifies service that it should return its current status information
58
+ CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE
59
+
60
+ # Notifies a service that its parameters have changed
61
+ CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE
62
+
63
+ # Notifies a service that there is a new component for binding
64
+ CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD
65
+
66
+ # Notifies a service that a component for binding has been removed
67
+ CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE
68
+
69
+ # Notifies a service that a component for binding has been enabled
70
+ CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE
71
+
72
+ # Notifies a service that a component for binding has been disabled
73
+ CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE
74
+
75
+ IDLE = 0
76
+
77
+ # Wraps SetServiceStatus.
78
+ SetTheServiceStatus = Proc.new do |dwCurrentState, dwWin32ExitCode,dwCheckPoint, dwWaitHint|
79
+ ss = SERVICE_STATUS.new # Current status of the service.
80
+
81
+ # Disable control requests until the service is started.
82
+ if dwCurrentState == SERVICE_START_PENDING
83
+ ss[:dwControlsAccepted] = 0
84
+ else
85
+ ss[:dwControlsAccepted] =
86
+ SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
87
+ SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_SHUTDOWN
88
+ end
89
+
90
+ # Initialize ss structure.
91
+ ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS
92
+ ss[:dwServiceSpecificExitCode] = 0
93
+ ss[:dwCurrentState] = dwCurrentState
94
+ ss[:dwWin32ExitCode] = dwWin32ExitCode
95
+ ss[:dwCheckPoint] = dwCheckPoint
96
+ ss[:dwWaitHint] = dwWaitHint
97
+
98
+ @@dwServiceState = dwCurrentState
99
+
100
+ # Send status of the service to the Service Controller.
101
+ if !SetServiceStatus(@@ssh, ss)
102
+ SetEvent(@@hStopEvent)
103
+ end
104
+ end
105
+
106
+ ERROR_CALL_NOT_IMPLEMENTED = 0x78
107
+
108
+ # Handles control signals from the service control manager.
109
+ Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
110
+ @@waiting_control_code = dwCtrlCode;
111
+ return_value = NO_ERROR
112
+
113
+ begin
114
+ dwState = SERVICE_RUNNING
115
+
116
+ case dwCtrlCode
117
+ when SERVICE_CONTROL_STOP
118
+ dwState = SERVICE_STOP_PENDING
119
+ when SERVICE_CONTROL_SHUTDOWN
120
+ dwState = SERVICE_STOP_PENDING
121
+ when SERVICE_CONTROL_PAUSE
122
+ dwState = SERVICE_PAUSED
123
+ when SERVICE_CONTROL_CONTINUE
124
+ dwState = SERVICE_RUNNING
125
+ #else
126
+ # TODO: Handle other control codes? Retain the current state?
127
+ end
128
+
129
+ # Set the status of the service except on interrogation.
130
+ unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE
131
+ SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
132
+ end
133
+
134
+ # Tell service_main thread to stop.
135
+ if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
136
+ if SetEvent(@@hStopEvent) == 0
137
+ SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
138
+ end
139
+ end
140
+ rescue
141
+ return_value = ERROR_CALL_NOT_IMPLEMENTED
142
+ end
143
+
144
+ return_value
145
+ end
146
+
147
+ # Called by the service control manager after the call to StartServiceCtrlDispatcher.
148
+ Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
149
+ begin
150
+ # Obtain the name of the service.
151
+ if lpszArgv.address!=0
152
+ argv = lpszArgv.get_array_of_string(0,dwArgc)
153
+ lpszServiceName = argv[0]
154
+ else
155
+ lpszServiceName = ''
156
+ end
157
+
158
+ # Args passed to Service.start
159
+ if(dwArgc > 1)
160
+ @@Argv = argv[1..-1]
161
+ else
162
+ @@Argv = nil
163
+ end
164
+
165
+ # Register the service ctrl handler.
166
+ @@ssh = RegisterServiceCtrlHandlerEx(
167
+ lpszServiceName,
168
+ Service_Ctrl_ex,
169
+ nil
170
+ )
171
+
172
+ # No service to stop, no service handle to notify, nothing to do but exit.
173
+ return if @@ssh == 0
174
+
175
+ # The service has started.
176
+ SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
177
+
178
+ SetEvent(@@hStartEvent)
179
+
180
+ # Main loop for the service.
181
+ while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
182
+ end
183
+
184
+ # Main loop for the service.
185
+ while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
186
+ end
187
+ ensure
188
+ # Stop the service.
189
+ SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
190
+ end
191
+ end
192
+
193
+ ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
194
+ ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
195
+
196
+ s = SERVICE_TABLE_ENTRY.new(ste[0])
197
+ s[:lpServiceName] = FFI::MemoryPointer.from_string('')
198
+ s[:lpServiceProc] = lpParameter
199
+
200
+ s = SERVICE_TABLE_ENTRY.new(ste[1])
201
+ s[:lpServiceName] = nil
202
+ s[:lpServiceProc] = nil
203
+
204
+ # No service to step, no service handle, no ruby exceptions, just terminate the thread..
205
+ if !StartServiceCtrlDispatcher(ste)
206
+ return 1
207
+ end
208
+
209
+ return 0
210
+ end
211
+
212
+ public
213
+
214
+ # This is a shortcut for Daemon.new + Daemon#mainloop.
215
+ #
216
+ def self.mainloop
217
+ self.new.mainloop
218
+ end
219
+
220
+ # This is the method that actually puts your code into a loop and allows it
221
+ # to run as a service. The code that is actually run while in the mainloop
222
+ # is what you defined in your own Daemon#service_main method.
223
+ #
224
+ def mainloop
225
+ @@waiting_control_code = IDLE_CONTROL_CODE
226
+ @@dwServiceState = 0
227
+
228
+ # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
229
+ # associated with a tty. This helps newbs avoid Errno::EBADF errors.
230
+ STDIN.reopen('NUL') if STDIN.isatty
231
+ STDOUT.reopen('NUL') if STDOUT.isatty
232
+ STDERR.reopen('NUL') if STDERR.isatty
233
+
234
+ # Calling init here so that init failures never even tries to start the
235
+ # service. Of course that means that init methods must be very quick
236
+ # because the SCM will be receiving no START_PENDING messages while
237
+ # init's running.
238
+ #
239
+ # TODO: Fix?
240
+ service_init() if respond_to?('service_init')
241
+
242
+ # Create the event to signal the service to start.
243
+ @@hStartEvent = CreateEvent(nil, true, false, nil)
244
+
245
+ if @@hStartEvent == 0
246
+ raise SystemCallError.new('CreateEvent', FFI.errno)
247
+ end
248
+
249
+ # Create the event to signal the service to stop.
250
+ @@hStopEvent = CreateEvent(nil, true, false, nil)
251
+
252
+ if @@hStopEvent == 0
253
+ raise SystemCallError.new('CreateEvent', FFI.errno)
254
+ end
255
+
256
+ # Create the event to signal the service that stop has completed
257
+ @@hStopCompletedEvent = CreateEvent(nil, true, false, nil)
258
+
259
+ if @@hStopCompletedEvent == 0
260
+ raise SystemCallError.new('CreateEvent', FFI.errno)
261
+ end
262
+
263
+ hThread = CreateThread(nil, 0, ThreadProc, Service_Main, 0, nil)
264
+
265
+ if hThread == 0
266
+ raise SystemCallError.new('CreateThread', FFI.errno)
267
+ end
268
+
269
+ events = FFI::MemoryPointer.new(:pointer, 2)
270
+ events.put_pointer(0, FFI::Pointer.new(hThread))
271
+ events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
272
+
273
+ while ((index = WaitForMultipleObjects(2, events, false, 1000)) == WAIT_TIMEOUT) do
274
+ end
275
+
276
+ if index == WAIT_FAILED
277
+ raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
278
+ end
279
+
280
+ # The thread exited, so the show is off.
281
+ if index == WAIT_OBJECT_0
282
+ raise "Service_Main thread exited abnormally"
283
+ end
284
+
285
+ thr = Thread.new do
286
+ begin
287
+ while(WaitForSingleObject(@@hStopEvent, 10) == WAIT_TIMEOUT)
288
+ # Check to see if anything interesting has been signaled
289
+ case @@waiting_control_code
290
+ when SERVICE_CONTROL_PAUSE
291
+ service_pause() if respond_to?('service_pause')
292
+ when SERVICE_CONTROL_CONTINUE
293
+ service_resume() if respond_to?('service_resume')
294
+ when SERVICE_CONTROL_INTERROGATE
295
+ service_interrogate() if respond_to?('service_interrogate')
296
+ when SERVICE_CONTROL_SHUTDOWN
297
+ service_shutdown() if respond_to?('service_shutdown')
298
+ when SERVICE_CONTROL_PARAMCHANGE
299
+ service_paramchange() if respond_to?('service_paramchange')
300
+ when SERVICE_CONTROL_NETBINDADD
301
+ service_netbindadd() if respond_to?('service_netbindadd')
302
+ when SERVICE_CONTROL_NETBINDREMOVE
303
+ service_netbindremove() if respond_to?('service_netbindremove')
304
+ when SERVICE_CONTROL_NETBINDENABLE
305
+ service_netbindenable() if respond_to?('service_netbindenable')
306
+ when SERVICE_CONTROL_NETBINDDISABLE
307
+ service_netbinddisable() if respond_to?('service_netbinddisable')
308
+ end
309
+ @@waiting_control_code = IDLE_CONTROL_CODE
310
+ end
311
+
312
+ service_stop() if respond_to?('service_stop')
313
+ ensure
314
+ SetEvent(@@hStopCompletedEvent)
315
+ end
316
+ end
317
+
318
+ if respond_to?('service_main')
319
+ service_main(*@@Argv)
320
+ end
321
+
322
+ thr.join
323
+ end
324
+
325
+ # Returns the state of the service (as an constant integer) which can be any
326
+ # of the service status constants, e.g. RUNNING, PAUSED, etc.
327
+ #
328
+ # This method is typically used within your service_main method to setup the
329
+ # loop. For example:
330
+ #
331
+ # class MyDaemon < Daemon
332
+ # def service_main
333
+ # while state == RUNNING || state == PAUSED || state == IDLE
334
+ # # Your main loop here
335
+ # end
336
+ # end
337
+ # end
338
+ #
339
+ # See the Daemon#running? method for an abstraction of the above code.
340
+ #
341
+ def state
342
+ @@dwServiceState
343
+ end
344
+
345
+ #
346
+ # Returns whether or not the service is in a running state, i.e. the service
347
+ # status is either RUNNING, PAUSED or IDLE.
348
+ #
349
+ # This is typically used within your service_main method to setup the main
350
+ # loop. For example:
351
+ #
352
+ # class MyDaemon < Daemon
353
+ # def service_main
354
+ # while running?
355
+ # # Your main loop here
356
+ # end
357
+ # end
358
+ # end
359
+ #
360
+ def running?
361
+ [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
362
+ end
363
+ end # Daemon
364
+ end # Win32