Creating a Windows Service

Credit: Bill Froelich

Problem

You want to write a self-contained Ruby program for Windows that performs a task in the background.

Solution

Create a Windows service using the win32-service library, available as the win32-service gem.

Put all the service code below into a Ruby file called rubysvc.rb. It defines a service that watches for the creation of a file c:findme.txt; if it ever finds that file, it immediately renames it.

The first step is to register the service with Windows. Running ruby rubysrvc.rb register will create the service.

	# rubysrvc.rb
	require 
ubygems
	require win32/service
	include Win32

	SERVICE_NAME = "RubySvc"
	SERVICE_DISPLAYNAME = "A Ruby Service"
	if ARGV[0] == "register"
	 # Start the service.
	 svc = Service.new
	 svc.create_service do |s|
	 s.service_name = SERVICE_NAME
	 s.display_name = SERVICE_DISPLAYNAME
	 s.binary_path_name = C:InstantRails-1.3
ubyin
uby  +
	 File.expand_path($0)
	 s.dependencies = []
	 end
	 svc.close
	 puts "Registered Service - " + SERVICE_DISPLAYNAME

When you e all done, you can run rubysrvc.rb stop to stop the service and remove it from Windows:

	elsif ARGV[0] == "delete"
	 # Stop the service.
	 if Service.status(SERVICE_NAME).current_state == "running"
	 Service.stop(SERVICE_NAME)
	 end
	 Service.delete(SERVICE_NAME)
	 puts "Removed Service - " + SERVICE_DISPLAYNAME
	else

If you run rubysrvc.rb with no arguments, nothing will happen, but it will remind you what parameters you can use:

	if ENV["HOMEDRIVE"]!=nil
	 # We are not running as a service, but the user didn	 provide any
	 # command line arguments. Weve got nothing to do.
	 puts "Usage: ruby rubysvc.rb [option]"
	 puts " Where option is one of the following:"
	 puts " register - To register the Service so it " +
	 "appears in the control panel"
	 puts " delete - To delete the Service from the control panel"
	 exit
	end

But when Windows runs rubysrvc.rb as a service, the real action starts:

	# If we got this far, we are running as a service.
	class Daemon
	 def service_init
	 # Give the service time to get everything initialized and running,
	 # before we enter the service_main function.
	 sleep 10
	 end

	 def service_main
	 fileCount = 0 # Initialize the file counter for the rename
	 watchForFile = "c:\findme.txt"
	 while state == RUNNING
	 sleep 5
	 if File.exists? watchForFile
	 fileCount += 1
	 File.rename watchForFile, watchForFile + "." + fileCount.to_s
	 end
	 end
	 end
	 end
	 d = Daemon.new
	 d.mainloop
	end

Once you run ruby rubysrvc.rb register, the service will show up in the Services Control Panel as "A Ruby Service". To see it, go to Start images/U2192.jpg border=0> ControlPanel images/U2192.jpg border=0> Administrative Tools images/U2192.jpg border=0> Services (Figure 20-1). Start the service by clicking the service name in the list and clicking the start button.

Figure 20-1. The Services Control Panel


To test the service, create a file in c: called findme.txt.

	$ echo "test" > findme.txt

Within seconds, the file you just created will be renamed to findme.txt:

	$ dir findme*
	# Volume in drive C has no label.
	# Volume Serial Number is 7C61-E72E
	# Directory of c:
	# 04/14/2006 02:29 PM 9 findme.txt.1

To remove the service, run ruby rubysrvc.rb delete.

Discussion

Theres no reason why the code that registers rubysrvc.rb as a Windows service has to be in rubysrvc.rb itself, but it makes things much simpler. When you run ruby rubysrvc.rb register, the script tells Windows to run rubysrvc.rb again, only as a service. The key is the binary_path_name defined on the Service object: this is the command for Windows to run as a service. In this case, its an invocation of the ruby interpreter with the service script passed as an input. But you could have run the same code from an irb session: then, rubysrvc.rb would only have been invoked once, by Windows, when running it as a service.

The code above assumes that your Ruby interpreter is located in c:InstantRails-1.3 ubyin uby. Of course, you can change this to point to your Ruby interpreter if its somewhere else: perhaps c: ubyin uby. If youve got the Ruby interpreter in your path, you just do this:

	s.binary_path_name = 
uby  + File.expand_path($0)

When you create a service, you specify both a service name and a display name. The service name is shorter, and is used when referring to the service from within Ruby code. The display name is the one shown in the Services Control Panel.

Our example service checks every five seconds for a file with a certain name. Whenever it finds that file, it renames it by appending a number to the filename. To keep things simple, it does no error checking to see if the new filename already exists; nor does it do any file locking to ensure that the file is completely written before renaming it. Real services should include at least some basic high-level error handling:

	def service_main
	 begin
	 while state == RUNNING
	 # Do my work
	 end
	 # Finish my work
	 rescue StandardError, Interrupt => e
	 # Handle the error
	 end
	end

In addition to the service_main method, your service can define additional methods to handle the other service events (stop, pause, and restart). The win32-service gem comes with a useful example script, daemon_test.rb, which provides sample implementations of these methods.

See Also

  • The win32-service library was written by Daniel J. Berger, and is part of the win32utils project (http://rubyforge.org/projects/win32utils/)
  • Recipe 6.13, "Locking a File," and Recipe 6.14, "Backing Up to Versioned Filenames," demonstrate more robust renaming and file locking strategies
  • Recipe 20.1, "Running a Daemon Process on Unix," for similar functionality on Unix
  • Recipe 23.2, "Managing Windows Services"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net