win32-service 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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