2008-05-29

Windows Service using win32-service and win32/daemon

Windows Services series Update 2008-6-13
Rubyforge has got some good documentation on creating a daemon so I will start with that. The first thing to do is to get the demo code which doesn't come in the gem install of win32-services but can be got by downloading the zipped code.

Run through the demo (on a local drive not a networked share as the networked shares are not visible to services) and see how you go. I extracted a small version and installed with the manual service installer to make sure I could build a service.

Here is a sample daemon code taken from the demo plus I added a timestamp and a comment on close:
LOG_FILE = 'C:\\test.log'

begin
require 'win32/daemon'

include Win32

class DemoDaemon < Daemon

def service_main
while running?
sleep 3
File.open("c:\\test.log", "a"){ |f| f.puts "Service is running #{Time.now}" }
end
end

def service_stop
File.open("c:\\test.log", "a"){ |f| f.puts "***Service stopped #{
Time.now}" }
exit!
end
end

DemoDaemon.mainloop
rescue Exception => err
File.open(LOG_FILE,'a+'){ |f| f.puts " ***Daemon failure #{Time.now} err=#{err} " }
raise
end


So I copied the code to NetBeans and tried to run. I had installed win32-services (gem install win32-services), I was using Ruby 1.86 and not Jruby as the interpreter.

Make sure it has include Win32 (which is not in some early versions of the demo code) I get
daemon1.rb:28: undefined method 'mainloop' for #

With I get this due to trying to run the Daemon from the command line:
daemon1.rb:29:in `mainloop': Service_Main thread exited abnormally (Win32::Daemon::Error)

However if you create a service and then run it it will work see the following with sc.

C:\>sc create test1 binPath= "C:\ruby\bin\rubyw.exe -C c:\temp\testDaemon\lib\ main.rb" type= own start= auto
[SC] CreateService SUCCESS

C:\>sc start test1

SERVICE_NAME: test1
TYPE : 10 WIN32_OWN_PROCESS
STATE : 4 RUNNING
(STOPPABLE,PAUSABLE,ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 5972
FLAGS :
C:\>sc stop test1

SERVICE_NAME: test1
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE,PAUSABLE,ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0

C:\>

And looking at c:\Test.log you can see the results.

10 comments:

LeoB said...

muy bueno y muy didáctico el ejemplo... muchas gracias
lo unico que encontre en esta parte del codigo 2 cuestiones:

rescue Exception =< err
File.open(LOG_FILE,'a+'){ |fh| fh

rescue Exception =< err - seria
rescue Exception => err

File.open(LOG_FILE,'a+'){ |fh| fh
falta cerrar el bloque con el signo }.

RubyPane said...

Hi Antelero
Thanks for the feedback. Sorry I had some typos in there. I have also put in the -C on the starting the service.
thanks

Daniel Berger said...

Yes, you have to create the service first before you can run it. You can't just run the daemon as standalone code.

I've updated that document to add "include Win32" as well, though that page by itself is not meant as a full tutorial.

RubyPane said...

Hi Daniel
I have updated my comments. I have been working slowly through this although my first service is now working well - I still need to check about memory leaks and add instrumentation for monitoring it. What I do now is to create a class and then either run if with a daemon or through a command line - I was going to write about that as well later.

LeoB said...

Hi Rubypayne
very good the article I help myself much, since not this so explained in the documentation.

thanks

Unknown said...

Thanks for the article! I'm seeing some weirdness calling methods in external files, and I hoped somebody here could help. For example, my main daemon class contains:

require 'c:\\myservice\\fancy_methods'

Let's say I have defined three methods:

doStuffA
doStuffB
helperA

If I call doStuffA or doStuffB directly from service_main everything is fine. However, if doStuffA calls helperA (both in fancy_methods.rb), I get "undefined method" errors.

I'm hoping to avoid dumping everything into one huge file :)

RubyPane said...

Hi Bryan
Not sure I can help and unclear why you would want different methods. In the service I am using I have put everything into a another class so that I can either have a command line calling version or a daemon calling version and so can debug.
i am trying to work out why my service only lasts a few days at a time before keeling over.

Unknown said...

I was putting the methods in separate files mainly for convenience (100's of lines). My service/script isn't doing anything particularly object-oriented, so I didn't define classes. However that is probably the best approach to avoid the "undefined method" weirdness.

I soon hope to be testing the durability of win32-services too, and will let you know if I see anything interesting.

Sriram said...

hey...nice article..
I could get a clear idea of how it works...
I am able to register the service..
bt I have a problem while starting the service...
it says the service did not respond in a timely manner...
Any idea why this is happening?

RubyPane said...

Hi Sriram
Is this the demo service as I wrote or your service that won't start. I did a later article on how to break your service into two bits so that you can test it not as a service and then once working as a service. http://rubypane.blogspot.com/2008_06_01_archive.html