win32-service 0.8.10 → 1.0.1

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