win32-service 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,7 +37,7 @@
37
37
  require 'win32/service'
38
38
  require 'rbconfig'
39
39
  include Win32
40
- include Config
40
+ include RbConfig
41
41
 
42
42
  # Make sure you're using the version you think you're using.
43
43
  puts 'VERSION: ' + Service::VERSION
@@ -46,7 +46,7 @@ SERVICE_NAME = 'DemoSvc'
46
46
  SERVICE_DISPLAYNAME = 'Demo'
47
47
 
48
48
  # Quote the full path to deal with possible spaces in the path name.
49
- ruby = File.join(CONFIG['bindir'], 'ruby').tr('/', '\\')
49
+ ruby = File.join(CONFIG['bindir'], CONFIG['ruby_install_name']).tr('/', '\\')
50
50
  path = ' "' + File.dirname(File.expand_path($0)).tr('/', '\\')
51
51
  path += '\demo_daemon.rb"'
52
52
  cmd = ruby + path
@@ -62,7 +62,7 @@ case ARGV[0].downcase
62
62
  :description => 'Sample Ruby service',
63
63
  :binary_path_name => cmd
64
64
  )
65
- puts 'Service ' + SERVICE_NAME + ' installed'
65
+ puts 'Service ' + SERVICE_NAME + ' installed'
66
66
  when 'start'
67
67
  if Service.status(SERVICE_NAME).current_state != 'running'
68
68
  Service.start(SERVICE_NAME, nil, 'hello', 'world')
@@ -1,30 +1,30 @@
1
- #######################################################################
2
- # demo_services.rb
3
- #
4
- # Test script for general futzing that shows off the basic
5
- # capabilities of this library. Modify as you see fit.
6
- #
7
- # You can run this sample program via the "example:services" task.
8
- #######################################################################
9
- require 'win32/service'
10
- include Win32
11
-
12
- puts "VERSION: " + Service::VERSION
13
-
14
- p Service.exists?('Schedule')
15
- p Service.exists?('bogusxxx')
16
-
17
- status = Service.status('Schedule')
18
- p status
19
-
20
- info = Service.config_info('Schedule')
21
-
22
- print "\n\nShowing config info for Schedule service\n\n"
23
- p info
24
-
25
- print "\n\nAbout to show all services\n\n"
26
- sleep 10
27
-
28
- Service.services{ |struct|
29
- p struct
30
- }
1
+ #######################################################################
2
+ # demo_services.rb
3
+ #
4
+ # Test script for general futzing that shows off the basic
5
+ # capabilities of this library. Modify as you see fit.
6
+ #
7
+ # You can run this sample program via the "example:services" task.
8
+ #######################################################################
9
+ require 'win32/service'
10
+ include Win32
11
+
12
+ puts "VERSION: " + Service::VERSION
13
+
14
+ p Service.exists?('Schedule')
15
+ p Service.exists?('bogusxxx')
16
+
17
+ status = Service.status('Schedule')
18
+ p status
19
+
20
+ info = Service.config_info('Schedule')
21
+
22
+ print "\n\nShowing config info for Schedule service\n\n"
23
+ p info
24
+
25
+ print "\n\nAbout to show all services\n\n"
26
+ sleep 10
27
+
28
+ Service.services{ |struct|
29
+ p struct
30
+ }
@@ -0,0 +1,345 @@
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::Constants
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.0'
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
+ # Handles control signals from the service control manager.
107
+ Service_Ctrl_ex = Proc.new do |dwCtrlCode,dwEventType,lpEventData,lpContext|
108
+ @@waiting_control_code = dwCtrlCode;
109
+
110
+ dwState = SERVICE_RUNNING
111
+
112
+ case dwCtrlCode
113
+ when SERVICE_CONTROL_STOP
114
+ dwState = SERVICE_STOP_PENDING
115
+ when SERVICE_CONTROL_SHUTDOWN
116
+ dwState = SERVICE_STOP_PENDING
117
+ when SERVICE_CONTROL_PAUSE
118
+ dwState = SERVICE_PAUSED
119
+ when SERVICE_CONTROL_CONTINUE
120
+ dwState = SERVICE_RUNNING
121
+ end
122
+
123
+ # Set the status of the service.
124
+ SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0)
125
+
126
+ # Tell service_main thread to stop.
127
+ if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN
128
+ if !SetEvent(@@hStopEvent)
129
+ SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0)
130
+ end
131
+ end
132
+ end
133
+
134
+ # Called by the service control manager after the call to StartServiceCtrlDispatcher.
135
+ Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc,lpszArgv|
136
+ # Obtain the name of the service.
137
+ if lpszArgv.address!=0
138
+ argv = lpszArgv.get_array_of_string(0,dwArgc)
139
+ lpszServiceName = argv[0]
140
+ else
141
+ lpszServiceName = ''
142
+ end
143
+
144
+ # Args passed to Service.start
145
+ if(dwArgc > 1)
146
+ @@Argv = argv[1..-1]
147
+ else
148
+ @@Argv = nil
149
+ end
150
+
151
+ # Register the service ctrl handler.
152
+ @@ssh = RegisterServiceCtrlHandlerEx(
153
+ lpszServiceName,
154
+ Service_Ctrl_ex,
155
+ nil
156
+ )
157
+
158
+ # No service to stop, no service handle to notify, nothing to do but exit.
159
+ return if @@ssh == 0
160
+
161
+ # The service has started.
162
+ SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0)
163
+
164
+ SetEvent(@@hStartEvent)
165
+
166
+ # Main loop for the service.
167
+ while(WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0) do
168
+ end
169
+
170
+ # Main loop for the service.
171
+ while(WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0) do
172
+ end
173
+
174
+ # Stop the service.
175
+ SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0)
176
+ end
177
+
178
+ ThreadProc = FFI::Function.new(:ulong,[:pointer]) do |lpParameter|
179
+ ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRY, 2)
180
+
181
+ s = SERVICE_TABLE_ENTRY.new(ste[0])
182
+ s[:lpServiceName] = FFI::MemoryPointer.from_string('')
183
+ s[:lpServiceProc] = Service_Main
184
+
185
+ s = SERVICE_TABLE_ENTRY.new(ste[1])
186
+ s[:lpServiceName] = nil
187
+ s[:lpServiceProc] = nil
188
+
189
+ # No service to step, no service handle, no ruby exceptions, just terminate the thread..
190
+ if !StartServiceCtrlDispatcher(ste)
191
+ return 1
192
+ end
193
+
194
+ return 0
195
+ end
196
+
197
+ public
198
+
199
+ # This is a shortcut for Daemon.new + Daemon#mainloop.
200
+ #
201
+ def self.mainloop
202
+ self.new.mainloop
203
+ end
204
+
205
+ # This is the method that actually puts your code into a loop and allows it
206
+ # to run as a service. The code that is actually run while in the mainloop
207
+ # is what you defined in your own Daemon#service_main method.
208
+ #
209
+ def mainloop
210
+ @@waiting_control_code = IDLE_CONTROL_CODE
211
+ @@dwServiceState = 0
212
+
213
+ # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still
214
+ # associated with a tty. This helps newbs avoid Errno::EBADF errors.
215
+ STDIN.reopen('NUL') if STDIN.isatty
216
+ STDOUT.reopen('NUL') if STDOUT.isatty
217
+ STDERR.reopen('NUL') if STDERR.isatty
218
+
219
+ # Calling init here so that init failures never even tries to start the
220
+ # service. Of course that means that init methods must be very quick
221
+ # because the SCM will be receiving no START_PENDING messages while
222
+ # init's running.
223
+ #
224
+ # TODO: Fix?
225
+ service_init() if respond_to?('service_init')
226
+
227
+ # Create the event to signal the service to start.
228
+ @@hStartEvent = CreateEvent(nil, true, false, nil)
229
+
230
+ if @@hStartEvent == 0
231
+ raise SystemCallError.new('CreateEvent', FFI.errno)
232
+ end
233
+
234
+ # Create the event to signal the service to stop.
235
+ @@hStopEvent = CreateEvent(nil, true, false, nil)
236
+
237
+ if @@hStopEvent == 0
238
+ raise SystemCallError.new('CreateEvent', FFI.errno)
239
+ end
240
+
241
+ # Create the event to signal the service that stop has completed
242
+ @@hStopCompletedEvent = CreateEvent(nil, true, false, nil)
243
+
244
+ if @@hStopCompletedEvent == 0
245
+ raise SystemCallError.new('CreateEvent', FFI.errno)
246
+ end
247
+
248
+ hThread = CreateThread(nil, 0, ThreadProc, nil, 0, nil)
249
+
250
+ if hThread == 0
251
+ raise SystemCallError.new('CreateThread', FFI.errno)
252
+ end
253
+
254
+ events = FFI::MemoryPointer.new(:pointer, FFI::Pointer.size*2)
255
+ events.put_pointer(0, FFI::Pointer.new(hThread))
256
+ events.put_pointer(FFI::Pointer.size, FFI::Pointer.new(@@hStartEvent))
257
+
258
+ while ((index = WaitForMultipleObjects(2, events, false, 1000)) == WAIT_TIMEOUT) do
259
+ end
260
+
261
+ if index == WAIT_FAILED
262
+ raise SystemCallError.new('WaitForMultipleObjects', FFI.errno)
263
+ end
264
+
265
+ # The thread exited, so the show is off.
266
+ if index == WAIT_OBJECT_0
267
+ raise "Service_Main thread exited abnormally"
268
+ end
269
+
270
+ thr = Thread.new do
271
+ while(WaitForSingleObject(@@hStopEvent, 10) == WAIT_TIMEOUT)
272
+ # Check to see if anything interesting has been signaled
273
+ case @@waiting_control_code
274
+ when SERVICE_CONTROL_PAUSE
275
+ service_pause() if respond_to?('service_pause')
276
+ when SERVICE_CONTROL_CONTINUE
277
+ service_resume() if respond_to?('service_resume')
278
+ when SERVICE_CONTROL_INTERROGATE
279
+ service_interrogate() if respond_to?('service_interrogate')
280
+ when SERVICE_CONTROL_SHUTDOWN
281
+ service_shutdown() if respond_to?('service_shutdown')
282
+ when SERVICE_CONTROL_PARAMCHANGE
283
+ service_paramchange() if respond_to?('service_paramchange')
284
+ when SERVICE_CONTROL_NETBINDADD
285
+ service_netbindadd() if respond_to?('service_netbindadd')
286
+ when SERVICE_CONTROL_NETBINDREMOVE
287
+ service_netbindremove() if respond_to?('service_netbindremove')
288
+ when SERVICE_CONTROL_NETBINDENABLE
289
+ service_netbindenable() if respond_to?('service_netbindenable')
290
+ when SERVICE_CONTROL_NETBINDDISABLE
291
+ service_netbinddisable() if respond_to?('service_netbinddisable')
292
+ end
293
+ @@waiting_control_code = IDLE_CONTROL_CODE
294
+ end
295
+
296
+ service_stop() if respond_to?('service_stop')
297
+ end
298
+
299
+ if respond_to?('service_main')
300
+ service_main(*@@Argv)
301
+ end
302
+
303
+ thr.join
304
+ end
305
+
306
+ # Returns the state of the service (as an constant integer) which can be any
307
+ # of the service status constants, e.g. RUNNING, PAUSED, etc.
308
+ #
309
+ # This method is typically used within your service_main method to setup the
310
+ # loop. For example:
311
+ #
312
+ # class MyDaemon < Daemon
313
+ # def service_main
314
+ # while state == RUNNING || state == PAUSED || state == IDLE
315
+ # # Your main loop here
316
+ # end
317
+ # end
318
+ # end
319
+ #
320
+ # See the Daemon#running? method for an abstraction of the above code.
321
+ #
322
+ def state
323
+ @@dwServiceState
324
+ end
325
+
326
+ #
327
+ # Returns whether or not the service is in a running state, i.e. the service
328
+ # status is either RUNNING, PAUSED or IDLE.
329
+ #
330
+ # This is typically used within your service_main method to setup the main
331
+ # loop. For example:
332
+ #
333
+ # class MyDaemon < Daemon
334
+ # def service_main
335
+ # while running?
336
+ # # Your main loop here
337
+ # end
338
+ # end
339
+ # end
340
+ #
341
+ def running?
342
+ [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState)
343
+ end
344
+ end # Daemon
345
+ end # Win32
data/lib/win32/service.rb CHANGED
@@ -1,11 +1,7 @@
1
- require 'rubygems'
2
- require 'windows/error'
3
- require 'windows/service'
4
- require 'windows/file'
5
- require 'windows/process'
6
- require 'windows/security'
7
- require 'windows/msvcrt/string'
8
- require 'windows/msvcrt/buffer'
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')
9
5
 
10
6
  # The Win32 module serves as a namespace only.
11
7
  module Win32
@@ -13,32 +9,18 @@ module Win32
13
9
  # The Service class encapsulates services controller actions, such as
14
10
  # creating, starting, configuring or deleting services.
15
11
  class Service
12
+ include Windows::Constants
13
+ include Windows::Structs
14
+ include Windows::Functions
15
+
16
+ extend Windows::Structs
17
+ extend Windows::Functions
16
18
 
17
- # This is the error typically raised if one of the Service methods
18
- # should fail for any reason.
19
- class Error < StandardError; end
20
-
21
- include Windows::Error
22
- include Windows::Service
23
- include Windows::File
24
- include Windows::Process
25
- include Windows::Security
26
- include Windows::MSVCRT::String
27
- include Windows::MSVCRT::Buffer
28
-
29
- extend Windows::Error
30
- extend Windows::Service
31
- extend Windows::File
32
- extend Windows::Process
33
- extend Windows::Security
34
- extend Windows::MSVCRT::String
35
- extend Windows::MSVCRT::Buffer
36
-
37
19
  # The version of the win32-service library
38
- VERSION = '0.7.2'
39
-
20
+ VERSION = '0.8.0'
21
+
40
22
  # SCM security and access rights
41
-
23
+
42
24
  # Includes STANDARD_RIGHTS_REQUIRED, in addition to all other rights
43
25
  MANAGER_ALL_ACCESS = SC_MANAGER_ALL_ACCESS
44
26
 
@@ -89,7 +71,7 @@ module Win32
89
71
 
90
72
  # Required to call ControlService with a user defined control code
91
73
  USER_DEFINED_CONTROL = SERVICE_USER_DEFINED_CONTROL
92
-
74
+
93
75
  # Service Types
94
76
 
95
77
  # Driver service
@@ -109,7 +91,7 @@ module Win32
109
91
 
110
92
  DRIVER = SERVICE_DRIVER
111
93
  TYPE_ALL = SERVICE_TYPE_ALL
112
-
94
+
113
95
  # Service start options
114
96
 
115
97
  # A service started automatically by the SCM during system startup
@@ -126,7 +108,7 @@ module Win32
126
108
 
127
109
  # A service that cannot be started
128
110
  DISABLED = SERVICE_DISABLED
129
-
111
+
130
112
  # Error control
131
113
 
132
114
  # Error logged, startup continues
@@ -140,9 +122,9 @@ module Win32
140
122
 
141
123
  # Error logged, startup fails, system restarted last known good config
142
124
  ERROR_CRITICAL = SERVICE_ERROR_CRITICAL
143
-
125
+
144
126
  # Current state
145
-
127
+
146
128
  # Service is not running
147
129
  STOPPED = SERVICE_STOPPED
148
130
 
@@ -163,7 +145,7 @@ module Win32
163
145
 
164
146
  # Service is paused
165
147
  PAUSED = SERVICE_PAUSED
166
-
148
+
167
149
  # Service controls
168
150
 
169
151
  # Notifies service that it should stop
@@ -206,9 +188,9 @@ module Win32
206
188
 
207
189
  # Run a command
208
190
  ACTION_RUN_COMMAND = SC_ACTION_RUN_COMMAND
209
-
191
+
210
192
  # :stopdoc: #
211
-
193
+
212
194
  StatusStruct = Struct.new(
213
195
  'ServiceStatus',
214
196
  :service_type,
@@ -235,7 +217,7 @@ module Win32
235
217
  :service_start_name,
236
218
  :display_name
237
219
  )
238
-
220
+
239
221
  ServiceStruct = Struct.new(
240
222
  'ServiceInfo',
241
223
  :service_name,
@@ -266,7 +248,7 @@ module Win32
266
248
  )
267
249
 
268
250
  # :startdoc: #
269
-
251
+
270
252
  # Creates a new service with the specified +options+. A +service_name+
271
253
  # must be specified or an ArgumentError is raised. A +host+ option may
272
254
  # be specified. If no host is specified the local machine is used.
@@ -311,7 +293,7 @@ module Win32
311
293
  # :display_name => 'This is some service',
312
294
  # )
313
295
  #
314
- def initialize(options={})
296
+ def initialize(options={})
315
297
  unless options.is_a?(Hash)
316
298
  raise ArgumentError, 'options parameter must be a hash'
317
299
  end
@@ -339,7 +321,7 @@ module Win32
339
321
  'failure_actions' => nil,
340
322
  'failure_delay' => 0,
341
323
  'host' => nil,
342
- 'service_name' => nil
324
+ 'service_name' => nil
343
325
  }
344
326
 
345
327
  # Validate the hash options
@@ -350,24 +332,22 @@ module Win32
350
332
  end
351
333
  opts[key] = value
352
334
  }
353
-
335
+
354
336
  unless opts['service_name']
355
- raise ArgumentError, 'No service_name specified'
337
+ raise ArgumentError, 'No service_name specified'
356
338
  end
357
-
339
+
358
340
  service_name = opts.delete('service_name')
359
341
  host = opts.delete('host')
360
-
342
+
361
343
  raise TypeError unless service_name.is_a?(String)
362
344
  raise TypeError if host && !host.is_a?(String)
363
345
 
364
346
  begin
365
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CREATE_SERVICE)
347
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CREATE_SERVICE)
348
+
349
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
366
350
 
367
- if handle_scm == 0
368
- raise Error, get_last_error
369
- end
370
-
371
351
  # Display name defaults to service_name
372
352
  opts['display_name'] ||= service_name
373
353
 
@@ -377,12 +357,13 @@ module Win32
377
357
  unless dependencies.is_a?(Array) || dependencies.is_a?(String)
378
358
  raise TypeError, 'dependencies must be a string or array'
379
359
  end
380
-
360
+
381
361
  if dependencies.is_a?(Array)
382
362
  dependencies = dependencies.join("\000")
383
363
  end
384
-
364
+
385
365
  dependencies += "\000"
366
+ dependencies = FFI::MemoryPointer.from_string(dependencies)
386
367
  end
387
368
 
388
369
  handle_scs = CreateService(
@@ -395,19 +376,17 @@ module Win32
395
376
  opts['error_control'],
396
377
  opts['binary_path_name'],
397
378
  opts['load_order_group'],
398
- 0,
379
+ nil,
399
380
  dependencies,
400
381
  opts['service_start_name'],
401
382
  opts['password']
402
383
  )
403
384
 
404
- if handle_scs == 0
405
- raise Error, get_last_error
406
- end
385
+ FFI.raise_windows_error('CreateService') if handle_scs == 0
407
386
 
408
387
  if opts['description']
409
- description = 0.chr * 4 # sizeof(SERVICE_DESCRIPTION)
410
- description[0,4] = [opts['description']].pack('p*')
388
+ description = SERVICE_DESCRIPTION.new
389
+ description[:lpDescription] = FFI::MemoryPointer.from_string(opts['description'])
411
390
 
412
391
  bool = ChangeServiceConfig2(
413
392
  handle_scs,
@@ -415,16 +394,14 @@ module Win32
415
394
  description
416
395
  )
417
396
 
418
- unless bool
419
- raise Error, get_last_error
420
- end
397
+ FFI.raise_windows_error('ChangeServiceConfig2') unless bool
421
398
  end
422
-
399
+
423
400
  if opts['failure_reset_period'] || opts['failure_reboot_message'] ||
424
401
  opts['failure_command'] || opts['failure_actions']
425
402
  then
426
403
  Service.configure_failure_actions(handle_scs, opts)
427
- end
404
+ end
428
405
  ensure
429
406
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
430
407
  CloseServiceHandle(handle_scm) if handle_scm && handle_scm > 0
@@ -476,7 +453,7 @@ module Win32
476
453
  # :description => 'A custom service I wrote just for fun'
477
454
  # )
478
455
  #
479
- def self.configure(options={})
456
+ def self.configure(options={})
480
457
  unless options.is_a?(Hash)
481
458
  raise ArgumentError, 'options parameter must be a hash'
482
459
  end
@@ -513,11 +490,11 @@ module Win32
513
490
  end
514
491
  opts[key] = value
515
492
  }
516
-
493
+
517
494
  unless opts['service_name']
518
- raise ArgumentError, 'No service_name specified'
495
+ raise ArgumentError, 'No service_name specified'
519
496
  end
520
-
497
+
521
498
  service = opts.delete('service_name')
522
499
  host = opts.delete('host')
523
500
 
@@ -525,14 +502,12 @@ module Win32
525
502
  raise TypeError unless host.is_a?(String) if host
526
503
 
527
504
  begin
528
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CONNECT)
505
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
506
+
507
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
529
508
 
530
- if handle_scm == 0
531
- raise Error, get_last_error
532
- end
533
-
534
509
  desired_access = SERVICE_CHANGE_CONFIG
535
-
510
+
536
511
  if opts['failure_actions']
537
512
  desired_access |= SERVICE_START
538
513
  end
@@ -543,9 +518,7 @@ module Win32
543
518
  desired_access
544
519
  )
545
520
 
546
- if handle_scs == 0
547
- raise Error, get_last_error
548
- end
521
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
549
522
 
550
523
  dependencies = opts['dependencies']
551
524
 
@@ -557,7 +530,7 @@ module Win32
557
530
  if dependencies.is_a?(Array)
558
531
  dependencies = dependencies.join("\000")
559
532
  end
560
-
533
+
561
534
  dependencies += "\000"
562
535
  end
563
536
 
@@ -568,20 +541,18 @@ module Win32
568
541
  opts['error_control'],
569
542
  opts['binary_path_name'],
570
543
  opts['load_order_group'],
571
- 0,
544
+ nil,
572
545
  dependencies,
573
546
  opts['service_start_name'],
574
547
  opts['password'],
575
548
  opts['display_name']
576
549
  )
577
550
 
578
- unless bool
579
- raise Error, get_last_error
580
- end
551
+ FFI.raise_windows_error('ChangeServiceConfig') unless bool
581
552
 
582
553
  if opts['description']
583
- description = 0.chr * 4 # sizeof(SERVICE_DESCRIPTION)
584
- description[0,4] = [opts['description']].pack('p*')
554
+ description = SERVICE_DESCRIPTION.new
555
+ description[:lpDescription] = FFI::MemoryPointer.from_string(opts['description'])
585
556
 
586
557
  bool = ChangeServiceConfig2(
587
558
  handle_scs,
@@ -589,9 +560,7 @@ module Win32
589
560
  description
590
561
  )
591
562
 
592
- unless bool
593
- raise Error, get_last_error
594
- end
563
+ FFI.raise_windows_error('ChangeServiceConfig2') unless bool
595
564
  end
596
565
 
597
566
  if opts['failure_reset_period'] || opts['failure_reboot_message'] ||
@@ -606,24 +575,22 @@ module Win32
606
575
 
607
576
  self
608
577
  end
609
-
578
+
610
579
  # Returns whether or not +service+ exists on +host+ or localhost, if
611
580
  # no host is specified.
612
581
  #
613
582
  # Example:
614
583
  #
615
584
  # Service.exists?('W32Time') => true
616
- #
585
+ #
617
586
  def self.exists?(service, host=nil)
618
587
  bool = false
619
588
 
620
589
  begin
621
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_ENUMERATE_SERVICE)
622
-
623
- if handle_scm == 0
624
- raise Error, get_last_error
625
- end
626
-
590
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
591
+
592
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
593
+
627
594
  handle_scs = OpenService(handle_scm, service, SERVICE_QUERY_STATUS)
628
595
  bool = true if handle_scs > 0
629
596
  ensure
@@ -631,9 +598,9 @@ module Win32
631
598
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
632
599
  end
633
600
 
634
- bool
601
+ bool
635
602
  end
636
-
603
+
637
604
  # Returns the display name of the specified service name, i.e. the string
638
605
  # displayed in the Services GUI. Raises a Service::Error if the service
639
606
  # name cannot be found.
@@ -647,33 +614,31 @@ module Win32
647
614
  # Service.get_display_name('W32Time') => 'Windows Time'
648
615
  #
649
616
  def self.get_display_name(service, host=nil)
650
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CONNECT)
651
-
652
- if handle_scm == 0
653
- raise Error, get_last_error
654
- end
655
-
656
- display_name = 0.chr * 260
657
- display_buf = [display_name.size].pack('L')
617
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
618
+
619
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
620
+
621
+ display_name = FFI::MemoryPointer.new(260)
622
+ display_size = FFI::MemoryPointer.new(:ulong)
623
+ display_size.write_ulong(display_name.size)
658
624
 
659
625
  begin
660
626
  bool = GetServiceDisplayName(
661
627
  handle_scm,
662
628
  service,
663
629
  display_name,
664
- display_buf
630
+ display_size
665
631
  )
666
632
 
667
- unless bool
668
- raise Error, get_last_error
669
- end
633
+
634
+ FFI.raise_windows_error('OpenSCManager') unless bool
670
635
  ensure
671
636
  CloseServiceHandle(handle_scm)
672
637
  end
673
638
 
674
- display_name.unpack('Z*')[0]
639
+ display_name.read_string
675
640
  end
676
-
641
+
677
642
  # Returns the service name of the specified service from the provided
678
643
  # +display_name+. Raises a Service::Error if the +display_name+ cannote
679
644
  # be found.
@@ -687,31 +652,28 @@ module Win32
687
652
  # Service.get_service_name('Windows Time') => 'W32Time'
688
653
  #
689
654
  def self.get_service_name(display_name, host=nil)
690
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CONNECT)
691
-
692
- if handle_scm == 0
693
- raise Error, get_last_error
694
- end
695
-
696
- service_name = 0.chr * 260
697
- service_buf = [service_name.size].pack('L')
698
-
655
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
656
+
657
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
658
+
659
+ service_name = FFI::MemoryPointer.new(260)
660
+ service_size = FFI::MemoryPointer.new(:ulong)
661
+ service_size.write_ulong(service_name.size)
662
+
699
663
  begin
700
664
  bool = GetServiceKeyName(
701
665
  handle_scm,
702
666
  display_name,
703
667
  service_name,
704
- service_buf
668
+ service_size
705
669
  )
706
670
 
707
- unless bool
708
- raise Error, get_last_error
709
- end
671
+ FFI.raise_windows_error('GetServiceKeyName') unless bool
710
672
  ensure
711
673
  CloseServiceHandle(handle_scm)
712
674
  end
713
675
 
714
- service_name.unpack('Z*')[0]
676
+ service_name.read_string
715
677
  end
716
678
 
717
679
  # Attempts to start the named +service+ on +host+, or the local machine
@@ -725,42 +687,53 @@ module Win32
725
687
  #
726
688
  # # Start 'SomeSvc' on host 'foo', passing 'hello' as an argument
727
689
  # Service.start('SomeSvc', 'foo', 'hello') => self
728
- #
690
+ #
729
691
  def self.start(service, host=nil, *args)
730
692
  handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
731
-
732
- if handle_scm == 0
733
- raise Error, get_last_error
734
- end
735
-
693
+
694
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
695
+
736
696
  begin
737
697
  handle_scs = OpenService(handle_scm, service, SERVICE_START)
738
-
739
- if handle_scs == 0
740
- raise Error, get_last_error
741
- end
742
-
698
+
699
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
700
+
743
701
  num_args = 0
744
-
702
+
745
703
  if args.empty?
746
704
  args = nil
747
- else
748
- num_args = args.length
749
- args = args.map{ |x| [x].pack('p*') }.join
750
- end
751
-
752
- unless StartService(handle_scs, num_args, args)
753
- raise Error, get_last_error
705
+ else # TODO: Verify that this works
706
+ str_ptrs = []
707
+ num_args = args.size
708
+
709
+ # First arg must be service name
710
+ str_ptrs << FFI::MemoryPointer.from_string(service)
711
+
712
+ args.each{ |string|
713
+ str_ptrs << FFI::MemoryPointer.from_string(string)
714
+ }
715
+
716
+ str_ptrs << nil
717
+
718
+ vector = FFI::MemoryPointer.new(:pointer, str_ptrs.size)
719
+
720
+ str_ptrs.each_with_index{ |p, i|
721
+ vector[i].put_pointer(0, p)
722
+ }
723
+ end
724
+
725
+ unless StartService(handle_scs, num_args, vector)
726
+ FFI.raise_windows_error('StartService')
754
727
  end
755
-
728
+
756
729
  ensure
757
730
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
758
731
  CloseServiceHandle(handle_scm)
759
732
  end
760
-
761
- self
733
+
734
+ self
762
735
  end
763
-
736
+
764
737
  # Stops a the given +service+ on +host+, or the local host if no host
765
738
  # is specified. Returns self.
766
739
  #
@@ -770,14 +743,14 @@ module Win32
770
743
  # Example:
771
744
  #
772
745
  # Service.stop('W32Time') => self
773
- #
746
+ #
774
747
  def self.stop(service, host=nil)
775
748
  service_signal = SERVICE_STOP
776
749
  control_signal = SERVICE_CONTROL_STOP
777
750
  send_signal(service, host, service_signal, control_signal)
778
751
  self
779
752
  end
780
-
753
+
781
754
  # Pauses the given +service+ on +host+, or the local host if no host
782
755
  # is specified. Returns self
783
756
  #
@@ -791,14 +764,14 @@ module Win32
791
764
  # Example:
792
765
  #
793
766
  # Service.pause('Schedule') => self
794
- #
767
+ #
795
768
  def self.pause(service, host=nil)
796
769
  service_signal = SERVICE_PAUSE_CONTINUE
797
770
  control_signal = SERVICE_CONTROL_PAUSE
798
771
  send_signal(service, host, service_signal, control_signal)
799
772
  self
800
773
  end
801
-
774
+
802
775
  # Resume the given +service+ on +host+, or the local host if no host
803
776
  # is specified. Returns self.
804
777
  #
@@ -808,14 +781,14 @@ module Win32
808
781
  # Example:
809
782
  #
810
783
  # Service.resume('Schedule') => self
811
- #
784
+ #
812
785
  def self.resume(service, host=nil)
813
786
  service_signal = SERVICE_PAUSE_CONTINUE
814
787
  control_signal = SERVICE_CONTROL_CONTINUE
815
788
  send_signal(service, host, service_signal, control_signal)
816
789
  self
817
790
  end
818
-
791
+
819
792
  # Deletes the specified +service+ from +host+, or the local host if
820
793
  # no host is specified. Returns self.
821
794
  #
@@ -830,21 +803,17 @@ module Win32
830
803
  # Service.delete('SomeService') => self
831
804
  #
832
805
  def self.delete(service, host=nil)
833
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CREATE_SERVICE)
834
-
835
- if handle_scm == 0
836
- raise Error, get_last_error
837
- end
838
-
806
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CREATE_SERVICE)
807
+
808
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
809
+
839
810
  begin
840
811
  handle_scs = OpenService(handle_scm, service, DELETE)
841
812
 
842
- if handle_scs == 0
843
- raise Error, get_last_error
844
- end
845
-
813
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
814
+
846
815
  unless DeleteService(handle_scs)
847
- raise Error, get_last_error
816
+ FFI.raise_windows_error('DeleteService')
848
817
  end
849
818
  ensure
850
819
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
@@ -871,72 +840,48 @@ module Win32
871
840
 
872
841
  handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
873
842
 
874
- if handle_scm == 0
875
- raise Error, get_last_error
876
- end
843
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
877
844
 
878
845
  begin
879
846
  handle_scs = OpenService(handle_scm, service, SERVICE_QUERY_CONFIG)
880
847
 
881
- if handle_scs == 0
882
- raise Error, get_last_error
883
- end
848
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
884
849
 
885
850
  # First, get the buf size needed
886
- bytes_needed = [0].pack('L')
851
+ bytes = FFI::MemoryPointer.new(:ulong)
887
852
 
888
- bool = QueryServiceConfig(handle_scs, nil, 0, bytes_needed)
853
+ bool = QueryServiceConfig(handle_scs, nil, 0, bytes)
889
854
 
890
- if !bool && GetLastError() != ERROR_INSUFFICIENT_BUFFER
891
- raise Error, get_last_error
855
+ if !bool && FFI.errno != ERROR_INSUFFICIENT_BUFFER
856
+ FFI.raise_windows_error('QueryServiceConfig')
892
857
  end
893
858
 
894
- buf = 0.chr * bytes_needed.unpack('L')[0]
895
- bytes = [0].pack('L')
859
+ buf = FFI::MemoryPointer.new(:char, bytes.read_ulong)
860
+ bytes = FFI::MemoryPointer.new(:ulong)
896
861
 
897
- bool = QueryServiceConfig(handle_scs, buf, buf.size, bytes_needed)
862
+ bool = QueryServiceConfig(handle_scs, buf, buf.size, bytes)
898
863
 
899
- unless bool
900
- raise Error, get_last_error
901
- end
864
+ struct = QUERY_SERVICE_CONFIG.new(buf) # cast the buffer
865
+
866
+ FFI.raise_windows_error('QueryServiceConfig') unless bool
902
867
  ensure
903
868
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
904
869
  CloseServiceHandle(handle_scm)
905
870
  end
906
871
 
907
- binary_path_name = 0.chr * 1024
908
- load_order_group = 0.chr * 1024
909
- dependencies = 0.chr * 1024
910
- service_start_name = 0.chr * 260
911
- display_name = 0.chr * 260
912
-
913
- strcpy(binary_path_name, buf[12,4].unpack('L')[0])
914
- binary_path_name = binary_path_name.unpack('Z*')[0]
915
-
916
- strcpy(load_order_group, buf[16,4].unpack('L')[0])
917
- load_order_group = load_order_group.unpack('Z*')[0]
918
-
919
- dependencies = get_dependencies(buf[24,4].unpack('L').first)
920
-
921
- strcpy(service_start_name, buf[28,4].unpack('L')[0])
922
- service_start_name = service_start_name.unpack('Z*')[0]
923
-
924
- strcpy(display_name, buf[32,4].unpack('L')[0])
925
- display_name = display_name.unpack('Z*')[0]
926
-
927
872
  ConfigStruct.new(
928
- get_service_type(buf[0,4].unpack('L')[0]),
929
- get_start_type(buf[4,4].unpack('L')[0]),
930
- get_error_control(buf[8,4].unpack('L')[0]),
931
- binary_path_name,
932
- load_order_group,
933
- buf[20,4].unpack('L')[0],
934
- dependencies,
935
- service_start_name,
936
- display_name
873
+ get_service_type(struct[:dwServiceType]),
874
+ get_start_type(struct[:dwStartType]),
875
+ get_error_control(struct[:dwErrorControl]),
876
+ struct[:lpBinaryPathName].read_string,
877
+ struct[:lpLoadOrderGroup].read_string,
878
+ struct[:dwTagId],
879
+ struct[:lpDependencies].read_array_of_null_separated_strings,
880
+ struct[:lpServiceStartName].read_string,
881
+ struct[:lpDisplayName].read_string
937
882
  )
938
883
  end
939
-
884
+
940
885
  # Returns a ServiceStatus struct indicating the status of service +name+
941
886
  # on +host+, or the localhost if none is provided.
942
887
  #
@@ -945,27 +890,23 @@ module Win32
945
890
  # Service.status('W32Time') => <struct Struct::ServiceStatus ...>
946
891
  #
947
892
  def self.status(service, host=nil)
948
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_ENUMERATE_SERVICE)
949
-
950
- if handle_scm == 0
951
- raise Error, get_last_error
952
- end
953
-
893
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
894
+
895
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
896
+
954
897
  begin
955
898
  handle_scs = OpenService(
956
899
  handle_scm,
957
900
  service,
958
901
  SERVICE_QUERY_STATUS
959
902
  )
960
-
961
- if handle_scs == 0
962
- raise Error, get_last_error
963
- end
964
-
903
+
904
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
905
+
965
906
  # SERVICE_STATUS_PROCESS struct
966
- status = [0,0,0,0,0,0,0,0,0].pack('LLLLLLLLL')
967
- bytes = [0].pack('L')
968
-
907
+ status = SERVICE_STATUS_PROCESS.new
908
+ bytes = FFI::MemoryPointer.new(:ulong)
909
+
969
910
  bool = QueryServiceStatusEx(
970
911
  handle_scs,
971
912
  SC_STATUS_PROCESS_INFO,
@@ -973,40 +914,35 @@ module Win32
973
914
  status.size,
974
915
  bytes
975
916
  )
976
-
977
- unless bool
978
- raise Error, get_last_error
979
- end
980
-
981
- dw_service_type = status[0,4].unpack('L').first
982
-
983
- service_type = get_service_type(dw_service_type)
984
- current_state = get_current_state(status[4,4].unpack('L').first)
985
- controls = get_controls_accepted(status[8,4].unpack('L').first)
986
- interactive = dw_service_type & SERVICE_INTERACTIVE_PROCESS > 0
987
-
917
+
918
+ FFI.raise_windows_error('QueryServiceStatusEx') unless bool
919
+
920
+ service_type = get_service_type(status[:dwServiceType])
921
+ current_state = get_current_state(status[:dwCurrentState])
922
+ controls = get_controls_accepted(status[:dwControlsAccepted])
923
+ interactive = status[:dwServiceType] & SERVICE_INTERACTIVE_PROCESS > 0
924
+
988
925
  # Note that the pid and service flags will always return 0 if you're
989
926
  # on Windows NT 4 or using a version of Ruby compiled with VC++ 6
990
927
  # or earlier.
991
- #
928
+ #
992
929
  status_struct = StatusStruct.new(
993
930
  service_type,
994
931
  current_state,
995
932
  controls,
996
- status[12,4].unpack('L').first, # Win32ExitCode
997
- status[16,4].unpack('L').first, # ServiceSpecificExitCode
998
- status[20,4].unpack('L').first, # CheckPoint
999
- status[24,4].unpack('L').first, # WaitHint
933
+ status[:dwWin32ExitCode],
934
+ status[:dwServiceSpecificExitCode],
935
+ status[:dwCheckPoint],
936
+ status[:dwWaitHint],
1000
937
  interactive,
1001
- status[28,4].unpack('L').first, # ProcessId
1002
- status[32,4].unpack('L').first # ServiceFlags
938
+ status[:dwProcessId],
939
+ status[:dwServiceFlags]
1003
940
  )
1004
-
1005
941
  ensure
1006
942
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
1007
943
  CloseServiceHandle(handle_scm)
1008
944
  end
1009
-
945
+
1010
946
  status_struct
1011
947
  end
1012
948
 
@@ -1030,25 +966,23 @@ module Win32
1030
966
  #
1031
967
  # # Enumerate over all 'network' services locally
1032
968
  # Service.services(nil, 'network'){ |service| p service }
1033
- #
969
+ #
1034
970
  def self.services(host=nil, group=nil)
1035
971
  unless host.nil?
1036
972
  raise TypeError unless host.is_a?(String) # Avoid strange errors
1037
973
  end
1038
-
974
+
1039
975
  unless group.nil?
1040
976
  raise TypeError unless group.is_a?(String) # Avoid strange errors
1041
977
  end
1042
-
1043
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_ENUMERATE_SERVICE)
1044
-
1045
- if handle_scm == 0
1046
- raise Error, get_last_error
1047
- end
1048
-
1049
- bytes_needed = [0].pack('L')
1050
- services_returned = [0].pack('L')
1051
- resume_handle = [0].pack('L')
978
+
979
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_ENUMERATE_SERVICE)
980
+
981
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
982
+
983
+ bytes_needed = FFI::MemoryPointer.new(:ulong)
984
+ services_returned = FFI::MemoryPointer.new(:ulong)
985
+ resume_handle = FFI::MemoryPointer.new(:ulong)
1052
986
 
1053
987
  begin
1054
988
  # The first call is used to determine the required buffer size
@@ -1057,7 +991,7 @@ module Win32
1057
991
  SC_ENUM_PROCESS_INFO,
1058
992
  SERVICE_WIN32 | SERVICE_DRIVER,
1059
993
  SERVICE_STATE_ALL,
1060
- 0,
994
+ nil,
1061
995
  0,
1062
996
  bytes_needed,
1063
997
  services_returned,
@@ -1065,14 +999,12 @@ module Win32
1065
999
  group
1066
1000
  )
1067
1001
 
1068
- err_num = GetLastError()
1069
-
1070
- if !bool && err_num == ERROR_MORE_DATA
1071
- service_buf = 0.chr * bytes_needed.unpack('L').first
1002
+ if !bool && FFI.errno == ERROR_MORE_DATA
1003
+ service_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
1072
1004
  else
1073
- raise Error, get_last_error(err_num)
1005
+ FFI.raise_windows_error('EnumServiceStatusEx')
1074
1006
  end
1075
-
1007
+
1076
1008
  bool = EnumServicesStatusEx(
1077
1009
  handle_scm,
1078
1010
  SC_ENUM_PROCESS_INFO,
@@ -1085,40 +1017,31 @@ module Win32
1085
1017
  resume_handle,
1086
1018
  group
1087
1019
  )
1088
-
1089
- unless bool
1090
- raise Error, get_last_error
1091
- end
1092
1020
 
1093
- num_services = services_returned.unpack('L').first
1094
-
1095
- index = 0
1021
+ FFI.raise_windows_error('EnumServiceStatusEx') unless bool
1022
+
1023
+ num_services = services_returned.read_ulong
1024
+
1096
1025
  services_array = [] unless block_given?
1097
1026
 
1098
1027
  1.upto(num_services){ |num|
1099
- service_name = 0.chr * 260
1100
- display_name = 0.chr * 260
1101
-
1102
- info = service_buf[index, 44] # sizeof(SERVICE_STATUS_PROCESS)
1103
-
1104
- strcpy(service_name, info[0,4].unpack('L').first)
1105
- strcpy(display_name, info[4,4].unpack('L').first)
1106
-
1107
- service_name = service_name.unpack('Z*')[0]
1108
- display_name = display_name.unpack('Z*')[0]
1109
-
1110
- dw_service_type = info[8,4].unpack('L').first
1111
-
1112
- service_type = get_service_type(dw_service_type)
1113
- current_state = get_current_state(info[12,4].unpack('L').first)
1114
- controls = get_controls_accepted(info[16,4].unpack('L').first)
1115
- interactive = dw_service_type & SERVICE_INTERACTIVE_PROCESS > 0
1116
- win_exit_code = info[20,4].unpack('L').first
1117
- ser_exit_code = info[24,4].unpack('L').first
1118
- check_point = info[28,4].unpack('L').first
1119
- wait_hint = info[32,4].unpack('L').first
1120
- pid = info[36,4].unpack('L').first
1121
- service_flags = info[40,4].unpack('L').first
1028
+ # Cast the buffer
1029
+ struct = ENUM_SERVICE_STATUS_PROCESS.new(service_buf)
1030
+
1031
+ service_name = struct[:lpServiceName].read_string
1032
+ display_name = struct[:lpDisplayName].read_string
1033
+
1034
+ service_type = get_service_type(struct[:ServiceStatusProcess][:dwServiceType])
1035
+ current_state = get_current_state(struct[:ServiceStatusProcess][:dwCurrentState])
1036
+ controls = get_controls_accepted(struct[:ServiceStatusProcess][:dwControlsAccepted])
1037
+
1038
+ interactive = struct[:ServiceStatusProcess][:dwServiceType] & SERVICE_INTERACTIVE_PROCESS > 0
1039
+ win_exit_code = struct[:ServiceStatusProcess][:dwWin32ExitCode]
1040
+ ser_exit_code = struct[:ServiceStatusProcess][:dwServiceSpecificExitCode]
1041
+ check_point = struct[:ServiceStatusProcess][:dwCheckPoint]
1042
+ wait_hint = struct[:ServiceStatusProcess][:dwWaitHint]
1043
+ pid = struct[:ServiceStatusProcess][:dwProcessId]
1044
+ service_flags = struct[:ServiceStatusProcess][:dwServiceFlags]
1122
1045
 
1123
1046
  begin
1124
1047
  handle_scs = OpenService(
@@ -1126,43 +1049,34 @@ module Win32
1126
1049
  service_name,
1127
1050
  SERVICE_QUERY_CONFIG
1128
1051
  )
1129
-
1130
- if handle_scs == 0
1131
- raise Error, get_last_error
1132
- end
1133
1052
 
1134
- config_buf = get_config_info(handle_scs)
1053
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
1135
1054
 
1136
- if config_buf != ERROR_FILE_NOT_FOUND
1137
- binary_path = 0.chr * 1024
1138
- strcpy(binary_path, config_buf[12,4].unpack('L').first)
1139
- binary_path = binary_path.unpack('Z*')[0]
1055
+ config_struct = get_config_info(handle_scs)
1140
1056
 
1141
- load_order = 0.chr * 1024
1142
- strcpy(load_order, config_buf[16,4].unpack('L').first)
1143
- load_order = load_order.unpack('Z*')[0]
1144
-
1145
- start_name = 0.chr * 1024
1146
- strcpy(start_name, config_buf[28,4].unpack('L').first)
1147
- start_name = start_name.unpack('Z*')[0]
1148
-
1149
- start_type = get_start_type(config_buf[4,4].unpack('L').first)
1150
- error_ctrl = get_error_control(config_buf[8,4].unpack('L').first)
1057
+ if config_struct != ERROR_FILE_NOT_FOUND
1058
+ binary_path = config_struct[:lpBinaryPathName].read_string
1059
+ load_order = config_struct[:lpLoadOrderGroup].read_string
1060
+ start_name = config_struct[:lpServiceStartName].read_string
1061
+ tag_id = config_struct[:dwTagId]
1151
1062
 
1152
- tag_id = config_buf[20,4].unpack('L').first
1063
+ start_type = get_start_type(config_struct[:dwStartType])
1064
+ error_ctrl = get_error_control(config_struct[:dwErrorControl])
1153
1065
 
1154
- deps = get_dependencies(config_buf[24,4].unpack('L').first)
1066
+ deps = config_struct[:lpDependencies].read_array_of_null_separated_strings
1155
1067
 
1156
- description = 0.chr * 2048
1157
- buf = get_config2_info(handle_scs, SERVICE_CONFIG_DESCRIPTION)
1068
+ buf = get_config2_info(handle_scs, SERVICE_CONFIG_DESCRIPTION)
1158
1069
 
1159
- strcpy(description, buf[0,4].unpack('L').first)
1160
- description = description.unpack('Z*')[0]
1070
+ if buf.read_pointer.null?
1071
+ description = ''
1072
+ else
1073
+ description = buf.read_pointer.read_string
1074
+ end
1161
1075
  else
1162
1076
  msg = "WARNING: The registry entry for the #{service_name} "
1163
1077
  msg += "service could not be found."
1164
1078
  warn msg
1165
-
1079
+
1166
1080
  binary_path = nil
1167
1081
  load_order = nil
1168
1082
  start_name = nil
@@ -1172,47 +1086,51 @@ module Win32
1172
1086
  deps = nil
1173
1087
  description = nil
1174
1088
  end
1175
-
1089
+
1176
1090
  buf2 = get_config2_info(handle_scs, SERVICE_CONFIG_FAILURE_ACTIONS)
1177
-
1178
- if buf2 != ERROR_FILE_NOT_FOUND
1179
- reset_period = buf2[0,4].unpack('L').first
1180
-
1181
- reboot_msg = 0.chr * 260
1182
- strcpy(reboot_msg, buf2[4,4].unpack('L').first)
1183
- reboot_msg = reboot_msg.unpack('Z*')[0]
1184
-
1185
- command = 0.chr * 260
1186
- strcpy(command, buf2[8,4].unpack('L').first)
1187
- command = command.unpack('Z*')[0]
1188
-
1189
- num_actions = buf2[12,4].unpack('L').first
1091
+
1092
+ if buf2.is_a?(FFI::MemoryPointer)
1093
+ fail_struct = SERVICE_FAILURE_ACTIONS.new(buf2)
1094
+
1095
+ reset_period = fail_struct[:dwResetPeriod]
1096
+ num_actions = fail_struct[:cActions]
1097
+
1098
+ if fail_struct[:lpRebootMsg].null?
1099
+ reboot_msg = nil
1100
+ else
1101
+ reboot_msg = fail_struct[:lpRebootMsg].read_string
1102
+ end
1103
+
1104
+ if fail_struct[:lpCommand].null?
1105
+ command = nil
1106
+ else
1107
+ command = fail_struct[:lpCommand].read_string
1108
+ end
1109
+
1190
1110
  actions = nil
1191
-
1111
+
1192
1112
  if num_actions > 0
1193
- action_ptr = buf2[16,4].unpack('L').first
1194
- action_buf = [0,0].pack('LL') * num_actions
1195
- memcpy(action_buf, action_ptr, action_buf.size)
1196
-
1197
- i = 0
1113
+ action_ptr = fail_struct[:lpsaActions]
1114
+
1198
1115
  actions = {}
1116
+
1199
1117
  num_actions.times{ |n|
1200
- action_type, delay = action_buf[i, 8].unpack('LL')
1201
- action_type = get_action_type(action_type)
1118
+ sc_action = SC_ACTION.new(action_ptr[n])
1119
+ delay = sc_action[:Delay]
1120
+ action_type = get_action_type(sc_action[:Type])
1202
1121
  actions[n+1] = {:action_type => action_type, :delay => delay}
1203
- i += 8
1204
1122
  }
1205
1123
  end
1206
1124
  else
1207
- reset_period = nil
1208
- reboot_message = nil
1209
- command = nil
1210
- actions = nil
1125
+ reset_period = nil
1126
+ reboot_msg = nil
1127
+ command = nil
1128
+ actions = nil
1211
1129
  end
1212
1130
  ensure
1213
1131
  CloseServiceHandle(handle_scs) if handle_scs > 0
1214
1132
  end
1215
-
1133
+
1216
1134
  struct = ServiceStruct.new(
1217
1135
  service_name,
1218
1136
  display_name,
@@ -1240,55 +1158,55 @@ module Win32
1240
1158
  num_actions,
1241
1159
  actions
1242
1160
  )
1243
-
1161
+
1244
1162
  if block_given?
1245
1163
  yield struct
1246
1164
  else
1247
1165
  services_array << struct
1248
1166
  end
1249
1167
 
1250
- index += 44 # sizeof(SERVICE_STATUS_PROCESS)
1168
+ service_buf += ENUM_SERVICE_STATUS_PROCESS.size
1251
1169
  }
1252
1170
  ensure
1253
1171
  CloseServiceHandle(handle_scm)
1254
1172
  end
1255
-
1173
+
1256
1174
  block_given? ? nil : services_array
1257
1175
  end
1258
-
1176
+
1259
1177
  private
1260
-
1178
+
1261
1179
  # Configures failure actions for a given service.
1262
1180
  #
1263
1181
  def self.configure_failure_actions(handle_scs, opts)
1264
1182
  if opts['failure_actions']
1265
1183
  token_handle = 0.chr * 4
1266
-
1184
+
1267
1185
  bool = OpenProcessToken(
1268
1186
  GetCurrentProcess(),
1269
1187
  TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1270
1188
  token_handle
1271
1189
  )
1272
-
1190
+
1273
1191
  unless bool
1274
1192
  error = get_last_error
1275
1193
  CloseServiceHandle(handle_scs)
1276
- raise Error, error
1194
+ raise Error, error
1277
1195
  end
1278
-
1196
+
1279
1197
  token_handle = token_handle.unpack('L').first
1280
-
1198
+
1281
1199
  # Get the LUID for shutdown privilege.
1282
1200
  luid = 0.chr * 8
1283
-
1201
+
1284
1202
  unless LookupPrivilegeValue('', 'SeShutdownPrivilege', luid)
1285
1203
  error = get_last_error
1286
1204
  CloseServiceHandle(handle_scs)
1287
- raise Error, error
1205
+ raise Error, error
1288
1206
  end
1289
-
1207
+
1290
1208
  tkp = [1].pack('L') + luid + [SE_PRIVILEGE_ENABLED].pack('L')
1291
-
1209
+
1292
1210
  # Enable shutdown privilege in access token of this process
1293
1211
  bool = AdjustTokenPrivileges(
1294
1212
  token_handle,
@@ -1298,69 +1216,69 @@ module Win32
1298
1216
  nil,
1299
1217
  nil
1300
1218
  )
1301
-
1219
+
1302
1220
  unless bool
1303
1221
  error = get_last_error
1304
1222
  CloseServiceHandle(handle_scs)
1305
- raise Error, error
1306
- end
1307
- end
1308
-
1223
+ raise Error, error
1224
+ end
1225
+ end
1226
+
1309
1227
  fail_buf = 0.chr * 20 # sizeof(SERVICE_FAILURE_ACTIONS)
1310
1228
 
1311
1229
  if opts['failure_reset_period']
1312
1230
  fail_buf[0,4] = [opts['failure_reset_period']].pack('L')
1313
1231
  end
1314
-
1232
+
1315
1233
  if opts['failure_reboot_message']
1316
1234
  fail_buf[4,4] = [opts['failure_reboot_message']].pack('p*')
1317
1235
  end
1318
-
1236
+
1319
1237
  if opts['failure_command']
1320
1238
  fail_buf[8,4] = [opts['failure_command']].pack('p*')
1321
1239
  end
1322
-
1240
+
1323
1241
  if opts['failure_actions']
1324
1242
  actions = []
1325
-
1243
+
1326
1244
  opts['failure_actions'].each{ |action|
1327
1245
  action_buf = 0.chr * 8
1328
1246
  action_buf[0, 4] = [action].pack('L')
1329
1247
  action_buf[4, 4] = [opts['failure_delay']].pack('L')
1330
1248
  actions << action_buf
1331
1249
  }
1332
-
1250
+
1333
1251
  actions = actions.join
1334
-
1252
+
1335
1253
  fail_buf[12,4] = [opts['failure_actions'].length].pack('L')
1336
1254
  fail_buf[16,4] = [actions].pack('p*')
1337
1255
  end
1338
-
1256
+
1339
1257
  bool = ChangeServiceConfig2(
1340
1258
  handle_scs,
1341
1259
  SERVICE_CONFIG_FAILURE_ACTIONS,
1342
1260
  fail_buf
1343
1261
  )
1344
-
1262
+
1345
1263
  unless bool
1346
1264
  error = get_last_error
1347
1265
  CloseServiceHandle(handle_scs)
1348
1266
  raise Error, error
1349
- end
1267
+ end
1350
1268
  end
1351
1269
 
1352
1270
  # Unravels a pointer to an array of dependencies. Takes the address
1353
1271
  # that points the array as an argument.
1354
1272
  #
1355
1273
  def self.get_dependencies(address)
1356
- dep_buf = ""
1274
+ dep_buf = ""
1357
1275
 
1358
1276
  while address != 0
1359
1277
  char_buf = 0.chr
1360
1278
  memcpy(char_buf, address, 1)
1361
- address += 1
1279
+ address += 1
1362
1280
  dep_buf += char_buf
1363
- break if dep_buf[-2,2] == "\0\0"
1281
+ break if dep_buf[-2,2] == "\0\0"
1364
1282
  end
1365
1283
 
1366
1284
  dependencies = []
@@ -1371,7 +1289,7 @@ module Win32
1371
1289
 
1372
1290
  dependencies
1373
1291
  end
1374
-
1292
+
1375
1293
  # Returns a human readable string indicating the action type.
1376
1294
  #
1377
1295
  def self.get_action_type(action_type)
@@ -1386,7 +1304,7 @@ module Win32
1386
1304
  'command'
1387
1305
  else
1388
1306
  'unknown'
1389
- end
1307
+ end
1390
1308
  end
1391
1309
 
1392
1310
  # Shortcut for QueryServiceConfig. Returns the buffer. In rare cases
@@ -1395,24 +1313,22 @@ module Win32
1395
1313
  # instead.
1396
1314
  #
1397
1315
  def self.get_config_info(handle)
1398
- bytes_needed = [0].pack('L')
1316
+ bytes_needed = FFI::MemoryPointer.new(:ulong)
1399
1317
 
1400
1318
  # First attempt at QueryServiceConfig is to get size needed
1401
- bool = QueryServiceConfig(handle, 0, 0, bytes_needed)
1402
-
1403
- err_num = GetLastError()
1319
+ bool = QueryServiceConfig(handle, nil, 0, bytes_needed)
1404
1320
 
1405
- if !bool && err_num == ERROR_INSUFFICIENT_BUFFER
1406
- config_buf = 0.chr * bytes_needed.unpack('L').first
1407
- elsif err_num == ERROR_FILE_NOT_FOUND
1408
- return err_num
1321
+ if !bool && FFI.errno == ERROR_INSUFFICIENT_BUFFER
1322
+ config_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
1323
+ elsif FFI.errno == ERROR_FILE_NOT_FOUND
1324
+ return FFI.errno
1409
1325
  else
1410
- error = get_last_error(err_num)
1326
+ error = FFI.errno
1411
1327
  CloseServiceHandle(handle)
1412
- raise Error, error
1328
+ FFI.raise_windows_error('QueryServiceConfig', error)
1413
1329
  end
1414
1330
 
1415
- bytes_needed = [0].pack('L')
1331
+ bytes_needed = FFI::MemoryPointer.new(:ulong)
1416
1332
 
1417
1333
  # Second attempt at QueryServiceConfig gets the actual info
1418
1334
  begin
@@ -1423,34 +1339,34 @@ module Win32
1423
1339
  bytes_needed
1424
1340
  )
1425
1341
 
1426
- raise Error, get_last_error unless bool
1342
+ FFI.raise_windows_error('QueryServiceConfig') unless bool
1427
1343
  ensure
1428
1344
  CloseServiceHandle(handle) unless bool
1429
1345
  end
1430
1346
 
1431
- config_buf
1347
+ QUERY_SERVICE_CONFIG.new(config_buf) # cast the buffer
1432
1348
  end
1433
-
1349
+
1434
1350
  # Shortcut for QueryServiceConfig2. Returns the buffer.
1435
- #
1436
- def self.get_config2_info(handle, info_level)
1437
- bytes_needed = [0].pack('L')
1438
-
1351
+ #
1352
+ def self.get_config2_info(handle, info_level)
1353
+ bytes_needed = FFI::MemoryPointer.new(:ulong)
1354
+
1439
1355
  # First attempt at QueryServiceConfig2 is to get size needed
1440
- bool = QueryServiceConfig2(handle, info_level, 0, 0, bytes_needed)
1356
+ bool = QueryServiceConfig2(handle, info_level, nil, 0, bytes_needed)
1441
1357
 
1442
- err_num = GetLastError()
1358
+ err_num = FFI.errno
1443
1359
 
1444
1360
  if !bool && err_num == ERROR_INSUFFICIENT_BUFFER
1445
- config2_buf = 0.chr * bytes_needed.unpack('L').first
1361
+ config2_buf = FFI::MemoryPointer.new(:char, bytes_needed.read_ulong)
1446
1362
  elsif err_num == ERROR_FILE_NOT_FOUND
1447
1363
  return err_num
1448
1364
  else
1449
1365
  CloseServiceHandle(handle)
1450
- raise Error, get_last_error(err_num)
1366
+ FFI.raise_windows_error('QueryServiceConfig2', err_num)
1451
1367
  end
1452
-
1453
- bytes_needed = [0].pack('L')
1368
+
1369
+ bytes_needed = FFI::MemoryPointer.new(:ulong)
1454
1370
 
1455
1371
  # Second attempt at QueryServiceConfig2 gets the actual info
1456
1372
  begin
@@ -1462,14 +1378,14 @@ module Win32
1462
1378
  bytes_needed
1463
1379
  )
1464
1380
 
1465
- raise Error, get_last_error unless bool
1381
+ FFI.raise_windows_error('QueryServiceConfig2') unless bool
1466
1382
  ensure
1467
1383
  CloseServiceHandle(handle) unless bool
1468
1384
  end
1469
-
1385
+
1470
1386
  config2_buf
1471
1387
  end
1472
-
1388
+
1473
1389
  # Returns a human readable string indicating the error control
1474
1390
  #
1475
1391
  def self.get_error_control(error_control)
@@ -1486,7 +1402,7 @@ module Win32
1486
1402
  nil
1487
1403
  end
1488
1404
  end
1489
-
1405
+
1490
1406
  # Returns a human readable string indicating the start type.
1491
1407
  #
1492
1408
  def self.get_start_type(start_type)
@@ -1505,7 +1421,7 @@ module Win32
1505
1421
  nil
1506
1422
  end
1507
1423
  end
1508
-
1424
+
1509
1425
  # Returns an array of human readable strings indicating the controls
1510
1426
  # that the service accepts.
1511
1427
  #
@@ -1550,9 +1466,9 @@ module Win32
1550
1466
 
1551
1467
  array
1552
1468
  end
1553
-
1469
+
1554
1470
  # Converts a service state numeric constant into a readable string.
1555
- #
1471
+ #
1556
1472
  def self.get_current_state(state)
1557
1473
  case state
1558
1474
  when SERVICE_CONTINUE_PENDING
@@ -1573,9 +1489,9 @@ module Win32
1573
1489
  nil
1574
1490
  end
1575
1491
  end
1576
-
1492
+
1577
1493
  # Converts a service type numeric constant into a human readable string.
1578
- #
1494
+ #
1579
1495
  def self.get_service_type(service_type)
1580
1496
  case service_type
1581
1497
  when SERVICE_FILE_SYSTEM_DRIVER
@@ -1602,33 +1518,29 @@ module Win32
1602
1518
  nil
1603
1519
  end
1604
1520
  end
1605
-
1521
+
1606
1522
  # A shortcut method that simplifies the various service control methods.
1607
- #
1523
+ #
1608
1524
  def self.send_signal(service, host, service_signal, control_signal)
1609
- handle_scm = OpenSCManager(host, 0, SC_MANAGER_CONNECT)
1610
-
1611
- if handle_scm == 0
1612
- raise Error, get_last_error
1613
- end
1614
-
1525
+ handle_scm = OpenSCManager(host, nil, SC_MANAGER_CONNECT)
1526
+
1527
+ FFI.raise_windows_error('OpenSCManager') if handle_scm == 0
1528
+
1615
1529
  begin
1616
1530
  handle_scs = OpenService(handle_scm, service, service_signal)
1617
-
1618
- if handle_scs == 0
1619
- raise Error, get_last_error
1620
- end
1621
-
1622
- status = [0,0,0,0,0,0,0].pack('LLLLLLL')
1623
-
1531
+
1532
+ FFI.raise_windows_error('OpenService') if handle_scs == 0
1533
+
1534
+ status = SERVICE_STATUS.new
1535
+
1624
1536
  unless ControlService(handle_scs, control_signal, status)
1625
- raise Error, get_last_error
1626
- end
1537
+ FFI.raise_windows_error('ControlService')
1538
+ end
1627
1539
  ensure
1628
1540
  CloseServiceHandle(handle_scs) if handle_scs && handle_scs > 0
1629
1541
  CloseServiceHandle(handle_scm) if handle_scm && handle_scm > 0
1630
1542
  end
1631
-
1543
+
1632
1544
  status
1633
1545
  end
1634
1546