Monday, April 25, 2005

Python Windows Services

We had project last friday to write a windows service that would poll an Exchange mail box and write to a database. It was executed in C#. Well... i got thinking... hmmm... can't python be used to do this? Because if it can, then that project would not last more than a couple of hours (actualy, the C# project shouldn't also last more than a couple of hours if you know what you'r doing, but hey.. i have to have something to say about why i decided to do it in Python write? i mean.... I LOVE Python may not cut it for some folks :) )

OK... lets get to the meat of it.

I first hit google, looking for links to python and windows services. I then found some mailing list archives and in one of them, there was a reference to a chapter on Windows Services in the book, Programming Python on Win32.

Well... i had access to the book, and went headlong. Long and short of it, 15 mins of perusing latter, I had a windows service running. Lets attempt to get this stuff to work together now.


First there are two very important modules that helps our lives out. win32service and win32serviceutil

We're going to write a service that just writes to the event log every 10 seconds. U'll have to stop this service soon or else, you'll run out of EvenLog space ;)

First some important pre-parations and background on python-windows services. There is a program called pythonservice.exe, that actually handles everything that concerns windows services written in Python. I'm walking a thin rope here with this explanation now, but this is how i understand it, just don't quote me in a conference where there are other Pythonistas and Lords Of the Eggs, I'll deny it vehemently ;)

The way i figure it, pythonservice.exe is to python services what the usual python.exe is for all other normal python scripts. Normally on windows, once .py is associated with python.exe, each time you run a script, the python intepreter, python.exe is called to 'intepret' it. For services, apparently some funky stuff goes on, so instead of calling python.exe as the intepreter, pythonservice.exe is called as the intepreter (well, not exactly intepreter i guess, but it is the process that runs the services.) You can also look at it like this: You say service start, windows identifies it as a python service, and starts pythonservice.exe passing it parameters to find the service itself. Pythonservice.exe locates the service, and starts it. As far as windows is concerned, it is running a process called PythonService.exe (You'll see that without special tricks, when writting to eventlog, PythonService is the one that does the writing)

Now the preceeding means that windows has to know to associate python services with pythonservice.exe . This is essentially called registeration. So pythonservice.exe must be registered with windows to handle python windows services. to do that, locate where the python win32 extensions are on your box. They'll probably be in your site-packages folder. Look for the win32\ subdirectory, and you'll locate pythonservice.exe sitting somewhere there:

Mine is at C:\Python24\Lib\site-packages\win32\pythonservice.exe

you can change to that directory:

C:\Python24\Lib\site-packages\win32

and then do: pythonservice.exe /register

You should see a message about registering the Python Service Manager.

After that, if there are no errors, we're ready to plunge.


First, we need to import the two all important modules.

For those who don't understand Python... the lines beginning with #, are just comments.




import win32service
import win32serviceutil
import time

#at this point. We're ready to go.
#Put simply, a python windows service inherits from win32serviceutils.ServiceFramework
#simply extending that class, sets up all you ever need to do.

class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "aservice"
_svc_display_name_ = "aservice - It Does nothing"

def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
#at this point service is created pefectly.
#you could stop here and jump to setting up the '__main__' section,
#but you wont be able to stop your service, and it won't do anything.
#at the very least, you need to implement SvcDoRun() and better still SvcStop():
#This next attribute is used when its stopping time.
self.isAlive = True

def SvcDoRun(self):
import servicemanager
while self.isAlive:
#remember when i said you needed only two modules?
#well... i think i lied. If you're going to do anything
#usefull, you're going to obviously need more modules.
#This next module servicemanager, has some funny
#properties that makes it only to be visible when
#the service is properly setup. This means it can't be imported
#in normal python programs, and can't even be imported
#in the Global Namespace, but only in local functions that
#will be called after the service is setup. Anyway,
#this module contains some utilities for writing to EventLog.

servicemanager.LogInfoMsg("aservice - is alive and well")
time.sleep(10)
servicemanager.LogInfoMsg("aservice - Stopped")

def SvcStop(self):
#before you stop, you'll want to inform windows that
#you've recieved a stop signal, and you're trying to stop.
#in the windows Service manager, this is what shows the status message as
#'stopping'. This is important, since SvcDoRun() may take sometime before it stops.
import servicemanager
servicemanager.LogInfoMsg("aservice - Recieved stop signal")
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.isAlive = False #this will make SvcDoRun() break the while loop at the next iteration.

if __name__ == '__main__':
win32serviceutil.HandleCommandLine(aservice) #this line sets it all up to run properly.



Uhh... just incase you haven't guessed. That is all for the service.
The next part is installing and starting it. You can save this as aservice.py

cd to the directory where it is saved and do:

aservice.py install

Note that 'aservice.py remove' will remove the service

you can start and stop with Windows Service manager or:

aservice.py start

and

aservice.py stop


OK... that's it... play around, flesh out... anything.

You may or may not have figured out that the entire functionality of the serivice gets started from SvcDoRun()


That's all folks... i hope this is usefull :)
Hey... i just had a brainwave. I'll repeat the code here without any comments :)

import win32service
import win32serviceutil
import time

class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "aservice"
_svc_display_name_ = "aservice - It Does nothing"

def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.isAlive = True

def SvcDoRun(self):
import servicemanager

while self.isAlive:
servicemanager.LogInfoMsg("aservice - is alive and well")
time.sleep(10)
servicemanager.LogInfoMsg("aservice - Stopped")

def SvcStop(self):
import servicemanager

servicemanager.LogInfoMsg("aservice - Recieved stop signal")
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.isAlive = False

if __name__ == '__main__':
win32serviceutil.HandleCommandLine(aservice)


Note that if you're going to copy and paste this stuff, you may have some white space issues, since i'm just typing straight and editing here.


gtg

22 Comments:

At Monday, April 17, 2006 9:37:00 PM, Blogger Chris Bloom said...

Thanks for posting this. The existing documentation is well hidden if not non-existent. We had a python script running as a service already but needed to make a second one and no one could remember how we got the first one installed :)

 
At Tuesday, May 01, 2007 7:47:00 AM, Blogger Matt said...

Thanks for the post. This made installing a python service really easy!

 
At Tuesday, May 01, 2007 8:18:00 AM, Blogger essiene said...

i'm glad that this post is still usefull after about 2 years? Who says there's no support for OpenSource technologies... I guess they're just the ones who aren't getting *paid* support any more :)

 
At Wednesday, August 08, 2007 2:07:00 PM, Anonymous Anonymous said...

Thanks for this useful post. I carefully repeated all the steps you described and it works. But it works only if I'm logged in. Once I log out the service stops. App Event log shows the error:

The instance's SvcRun() method failed
File "C:\Python25\Lib\site-packages\win32\lib\win32serviceutil.py", line 785, in SvcRun
self.SvcDoRun()
File "C:\altworx_repository\wwwroot\cgi-bin\pokus\aservice.py", line 18, in SvcDoRun
time.sleep(10)
type 'exceptions.IOError': (4, 'Interrupted function call')

I use WXP Prof and Python 2.5. Do you have any idea what is wrong? Thank you.

 
At Thursday, August 09, 2007 5:24:00 PM, Anonymous Anonymous said...

After some consultation with people from python-win32 mailing list at python.org there is the solution which survives log out. The modified code of service is below.

import win32service
import win32serviceutil
import win32api
import win32con

class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "aservice"
_svc_display_name_ = "aservice - It Does nothing"

def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.isAlive = True

def SvcDoRun(self):
import servicemanager

while self.isAlive:
servicemanager.LogInfoMsg("aservice - is alive and well")
win32api.SleepEx(10000, True)
servicemanager.LogInfoMsg("aservice - Stopped")

def SvcStop(self):
import servicemanager

servicemanager.LogInfoMsg("aservice - Recieved stop signal")
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.isAlive = False

def ctrlHandler(ctrlType):
return True

if __name__ == '__main__':
win32api.SetConsoleCtrlHandler(ctrlHandler, True)
win32serviceutil.HandleCommandLine(aservice)

 
At Wednesday, February 06, 2008 6:38:00 PM, Blogger aatiis said...

Thanks for posting this. It helped me a lot.

 
At Friday, April 04, 2008 4:22:00 PM, Anonymous Anonymous said...

This was the first thing that came up when I Googled. Thanks so much for sharing this.

 
At Thursday, May 15, 2008 12:27:00 PM, Blogger Andriy Drozdyuk said...

It's strange I get:

Python could not import the service's module

type 'exceptions.ImportError': No module named aservice

 
At Saturday, June 07, 2008 4:51:00 PM, Anonymous Anonymous said...

I ran into similiar issues as Andriy Drozdyuk. The issues lie in the path. I moved the code to Python25 directory and it works fine. Somehow, I need to either put in a fully qualified path or set the path correctly to allow my script to live somewhere else.

-David

 
At Sunday, June 08, 2008 3:07:00 AM, Anonymous Admin said...

The file you wish to make into a service must be accessible to the PYTHONPATH which means you must make sure to place the file you wih to make into a service somewhere in the list of paths as specified by the environment variable known as PYTHONPATH. Yu can also place the file you wish to make into a service by extending the PYTHONPATH to include the folder in which aservice.py resides.

 
At Tuesday, November 04, 2008 2:51:00 AM, Blogger Andrew said...

When I try to run aservice.py as as service, it starts and stops immediately. Isn't it supposed to run and write to the log every ten seconds?

 
At Friday, April 17, 2009 5:04:00 PM, Blogger Munna said...

Really this help a lot for understanding web service in python.

Thanks a lot......

 
At Wednesday, July 15, 2009 2:32:00 AM, Blogger reyt said...

I think wow gold and world of warcraft gold

 
At Sunday, October 18, 2009 1:30:00 PM, Blogger Andrew Bale said...

All OK - except that when I try to register (or run/stop etc) the aservice.py I get the usage information.
The class is being loaded, but it appears that the commandline handler is somehow not reading the command line paramters.

 
At Sunday, October 18, 2009 2:44:00 PM, Blogger Andrew Bale said...

OK - a few small issues I've found
1) this must be run (at least in windows) as 'python aservice.py install', not as 'aservice.py install' otherwise the parameters are not picked up
2) the names of '_svc_name' etc should be '_svc_name_'

Then all works fine!!

Thanks

 
At Monday, September 27, 2010 3:55:00 PM, Blogger vinay khemka said...

Hi,
I want to run the python win32 services for my client, whose status will logged-in and logged out. Based on these status service manager will work. but i have a an exe for my client code ,how do i integrate with the services . please help me, i am unable to figure out.

Thanks in advance

 
At Wednesday, May 11, 2011 5:53:00 AM, Blogger Jayson said...

This is a great article. However, when you go to Windows Service Manager, you can see that the service is stop. Once i manually turned it on, a message box appears saying:
"Windows could not start on local computer. For more info, review the System Event.Log..."
any ideas?

 
At Tuesday, June 05, 2012 2:07:00 AM, Anonymous Anonymous said...

I am using wsgisvc to run window service it work very well


http://pypi.python.org/pypi/wsgisvc

 
At Wednesday, December 12, 2012 4:39:00 PM, Anonymous Anonymous said...

Thank you very much for this Great work!
Your article helps me a lot!
Richard

 
At Monday, February 11, 2013 5:32:00 PM, Anonymous Dell Cooling Fan said...

The file you wish to make into a service must be accessible to the PYTHONPATH which means you must make sure to place the file you wih to make into a service somewhere in the list of paths as specified by the environment variable known as PYTHONPATH. Yu can also place the file you wish to make into a service by extending the PYTHONPATH to include the folder in which aservice.py resides.

 
At Tuesday, March 11, 2014 12:58:00 PM, Blogger Harish Barvekar said...

I am getting this warning while starting from service manager.
The Myservice service on Local Computer started and then stopped.
Some services stop automatically if they are not in use by other services or programs.

 
At Tuesday, March 11, 2014 1:39:00 PM, Blogger Harish Barvekar said...

getting error as Traceback (most recent call last): File "C:\Python27\lib\site-packages\win32\lib\win32serviceutil.py", line 835, in SvcRun self.SvcDoRun() AttributeError: Myservice instance has no attribute 'SvcDoRun'

 

Post a Comment

<< Home