Skip to Content

Рецепты приготовления Windows Services из Python-скриптов

Опубликовано в

Возникла у меня давеча необходимость (а может просто непреодолимое желание Smile) сделать парочку Windows служб из Python-скриптов. Немного порывшись в «интернетах» я обнаружил, что осуществить это не так уж и сложно и, более того, способов существует аж.. два. Итак, хозяйке на заметку...

Рецепт первый. Для непривередливых

Этот рецепт позволяет приготовить Windows службу не только из Python скрипта, но и вообще из любой программы. Для приготовления понадобятся:

  1. Утилиты srvany.exe и instsrv.exe (необязательно, но об этом ниже).Найти их можно в пакете Windows Server 2003 Resource Kit Tools. Устанавливать все утилиты необязательно, можно просто распаковать дистрибутив (например, при помощи 7Zip) и скопировать из него нужные файлы.
  2. Собственно Python-скрипт, в котором необходимые вам действия выполняются в бесконечном цикле (либо, как было сказано выше, любое другое приложение также крутящееся в бесконечном цикле)
  3. Notepad Smile

Если все ингредиенты присутствуют, то дело остается за малым — смешать, встряхнуть, употребить соединить все вместе, для чего делаем следующее:

  1. Выполняем в командной строке
    instsrv <Service Name> "C:\Program Files\Custom Service\srvany.exe"

    где <Service Name> – желаемое имя сервиса, т.е. именно так он будет называться в оснастке Services
    Внимание, путь к srvany.exe нужно указывать полностью!

    Как было отмечено выше, использовать instserv.exe необязательно, вместо него можно воспользоваться стандартной утилитой SC. В случае использования этой утилиты в командной строке необходимо выполнить следующее:

    sc create <Service Name> binpath= "C:\Program Files\Custom Service\srvany.exe"

    где <Service Name> – желаемое имя сервиса.
    Внимание! Путь к srvany.exe нужно указывать полностью. После binpath= обязательно должен быть пробел, иначе ничего работать не будет!

  2. Создаем reg файл следующего содержания:
    REGEDIT4
        [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<Service Name>\Parameters]
        ; Путь к интерпретатору pythonw.exe
            "Application"="C:\\Python26\\pythonw.exe"
        ; Путь к папке в которой находится необходимый скрипт
            "AppDirectory"="C:\\Program Files\\Custom Service"
        ; Собственно, путь к самому скрипту
            "AppParameters"="C:\\Program Files\\Custom Service\\custom_script.py" 

    Внимание! Слеши обязательно должны быть двойными.
    В случае, если вы создаете службу не из Python-скрипта, то ключ Application должен содержать путь к приложению, которое вы хотите запускать как Windows Service, а AppDirectory и AppParametrs рабочую директорию и параметры его запуска соответственно.

  3. Добавляем ключи из созданного файла в реестр
     
  4. Открываем оснастку Services и запускаем свежеиспеченную службу, либо выполняем в командной строке:
    sc start <Service Name>

Рецепт второй. Для гурманов

Почему для гурманов? Да потому, что этот рецепт, во-первых, подразумевает приготовление службы без использования сторонних утилит, во-вторых, реализуется при помощи Windows API со всеми вытекающими (т.е. позволит, например, выполнить необходимые действия при остановке службы или записывать все что вам необходимо в Event Log). Для приготовления понадобятся:

  1. PyWin32 (скажу по-секрету, что пример реализации службы присутствует в демо-скриптах этого пакета, просто не все их смотрели Wink)
  2. hands.dll Smile

Когда все необходимые приготовления будут закончены, можно приступать к написанию кода. Выглядеть он должен приблизительно вот так:

  1. # -*- coding: utf-8 -*-
  2. #! /usr/bin/env python
  3. import win32serviceutil
  4. import win32service
  5. import win32event
  6. import servicemanager
  7.  
  8. class AppServerSvc (win32serviceutil.ServiceFramework):
  9.     _svc_name_ = "DummyService"
  10.     _svc_display_name_ = "Dummy Service"
  11.     _svc_description_ = "Dummy Service Description"
  12.  
  13.     def __init__(self,args):
  14.         win32serviceutil.ServiceFramework.__init__(self,args)
  15.         self.hWaitStop = win32event.CreateEvent(None,0,0,None)
  16.         self.hWaitResume = win32event.CreateEvent(None, 0, 0, None)
  17.         self.timeout = 10000 #Пауза между выполнением основного цикла службы в миллисекундах
  18.         self.resumeTimeout = 1000
  19.         self._paused = False
  20.  
  21.     def SvcStop(self):
  22.         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  23.         win32event.SetEvent(self.hWaitStop)
  24.         servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
  25.                               servicemanager.PYS_SERVICE_STOPPED,
  26.                               (self._svc_name_, ''))
  27.        
  28.     def SvcPause(self):
  29.         self.ReportServiceStatus(win32service.SERVICE_PAUSE_PENDING)
  30.         self._paused = True
  31.         self.ReportServiceStatus(win32service.SERVICE_PAUSED)
  32.         servicemanager.LogInfoMsg("The %s service has paused." % (self._svc_name_, ))
  33.    
  34.     def SvcContinue(self):
  35.         self.ReportServiceStatus(win32service.SERVICE_CONTINUE_PENDING)
  36.         win32event.SetEvent(self.hWaitResume)
  37.         self.ReportServiceStatus(win32service.SERVICE_RUNNING)
  38.         servicemanager.LogInfoMsg("The %s service has resumed." % (self._svc_name_, ))
  39.                
  40.  
  41.     def SvcDoRun(self):
  42.         servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
  43.                               servicemanager.PYS_SERVICE_STARTED,
  44.                               (self._svc_name_,''))
  45.         self.main()  
  46.    
  47.     #В этом методе реализовываем нашу службу    
  48.     def main(self):
  49.         #Здесь выполняем необходимые действия при старте службы
  50.         servicemanager.LogInfoMsg("Hello! I'm a Dummy Service.")
  51.         while True:
  52.             #Здесь должен находиться основной код сервиса
  53.             servicemanager.LogInfoMsg("I'm still here.")
  54.            
  55.             #Проверяем не поступила ли команда завершения работы службы
  56.             rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
  57.             if rc == win32event.WAIT_OBJECT_0:
  58.                 #Здесь выполняем необходимые действия при остановке службы
  59.                 servicemanager.LogInfoMsg("Bye!")
  60.                 break
  61.  
  62.             #Здесь выполняем необходимые действия при приостановке службы
  63.             if self._paused:
  64.                 servicemanager.LogInfoMsg("I'm paused... Keep waiting...")
  65.             #Приостановка работы службы                
  66.             while self._paused:
  67.                 #Проверям не поступила ли команда возобновления работы службы
  68.                 rc = win32event.WaitForSingleObject(self.hWaitResume, self.resumeTimeout)
  69.                 if rc == win32event.WAIT_OBJECT_0:
  70.                     self._paused = False
  71.                     #Здесь выполняем необходимые действия при возобновлении работы службы
  72.                     servicemanager.LogInfoMsg("Yeah! Let's continue!")
  73.                     break                  
  74.  
  75. if __name__ == '__main__':
  76.     win32serviceutil.HandleCommandLine(AppServerSvc)


Как только с написанием кода будет покончено, сохраняем службу в обычный питоновский файл, например, myservice.py и запускаем его из командной строки следующим образом:

myservice.py install

 

Служба готова. Можно пользоваться.

Сервис готов. Можно пользоваться. Dummy Windows Service

++++++++++

Отправить комментарий

  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступны HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Строки и параграфы переносятся автоматически.
  • Pairs of<blockquote> tags will be styled as a block that indicates a quotation.
  • Textual smileys will be replaced with graphical ones.

Подробнее о форматировании