2012-01-09 14 views
19

Napisałem usługę dla systemu Windows XP + w pythonie przy użyciu przykładowego kodu With Extended Service Notifications. Działa doskonale do wykrywania ekranu blokady logowania użytkownika/wylogowania i innych zdarzeń. Problem polega na tym, że nigdy nie wykonuje zamykania zdarzeń i z wdziękiem zatrzymuje usługę po ponownym uruchomieniu/zamknięciu. Pozostaje on żywy dzięki zdarzeniom logowania/wylogowywania, a także ponownemu uruchamianiu po ponownym uruchomieniu. Każda pomoc będzie doceniona.Usługa systemu Windows napisana w pythonie, nie wykrywająca zdarzeń zamknięcia i zatrzymująca się z wdziękiem

Nie chcę używać RegisterServiceCtrlHandlerEx i obsługiwać sygnałów konsolowych, jeśli mogę w tym pomóc - usługi mają wbudowaną tę funkcję, po prostu jakoś ją zmanipulowałem.

Oto kod:

from os.path import splitext, abspath 
from sys import modules 

import win32serviceutil 
import win32service 
import win32event 
import win32api 
import win32security 
import win32ts 

class Service(win32serviceutil.ServiceFramework): 
    _svc_name_ = '_unNamed' 
    _svc_display_name_ = '_Service Template' 

    def __init__(self, *args): 
    win32serviceutil.ServiceFramework.__init__(self, *args) 
    self.log('Initializing Service') 
    self.stop_event = win32event.CreateEvent(None, 0, 0, None) 
    self.server = None 

    def log(self, msg): 
    import servicemanager 
    servicemanager.LogInfoMsg(str(msg)) 
    def logErr(self, msg): 
    import servicemanager 
    servicemanager.LogErrorMsg(str(msg)) 
    def logWarn(self, msg): 
    import servicemanager 
    servicemanager.LogWarningMsg(str(msg)) 

    def sleep(self, sec): 
    win32api.Sleep(sec*1000, True) 

    def GetAcceptedControls(self): 
    # Accept SESSION_CHANGE control 
    rc = win32serviceutil.ServiceFramework.GetAcceptedControls(self) 
    rc |= win32service.SERVICE_ACCEPT_SESSIONCHANGE 
    rc |= win32service.SERVICE_ACCEPT_SHUTDOWN 
    return rc 

    def GetUserInfo(self, sess_id): 
    sessions = win32security.LsaEnumerateLogonSessions()[:-5] 
    for sn in sessions: 
     sn_info = win32security.LsaGetLogonSessionData(sn) 
     if sn_info['Session'] == sess_id: 
     return sn_info 

    def getUserSessionInfo(self, sess_id): 
    msg = "" 
    try: 
     for key, val in self.GetUserInfo(sess_id).items(): 
     msg += '%s : %s\n'%(key, val) 
     if key == "UserName": 
      self.server.username = val 
    except Exception, e: 
     msg += '%s'%e 
    return msg 

    # All extra events are sent via SvcOtherEx (SvcOther remains as a 
    # function taking only the first args for backwards compatability) 
    def SvcOtherEx(self, control, event_type, data): 
     # This is only showing a few of the extra events - see the MSDN 
     # docs for "HandlerEx callback" for more info. 
     if control == win32service.SERVICE_CONTROL_SESSIONCHANGE: 
      sess_id = data[0] 
      msg = "" 
      if event_type == 5: # logon 
      msg = "Logon event: type=%s, sessionid=%s\n" % (event_type, sess_id) 
#   user_token = win32ts.WTSQueryUserToken(int(sess_id)) 
      self.server.status = 1 #logon event 
      self.getUserSessionInfo(sess_id) 
      self.sendHeartbeat() 
      self.server.status = 2 #active user 
      elif event_type == 6: # logoff 
      msg = "Logoff event: type=%s, sessionid=%s\n" % (event_type, sess_id) 
      self.server.status = 3 #logoff event 
      self.sendHeartbeat() 
      self.server.username = "" 
      self.server.status = 0 #no user 
      elif event_type == 7: # lock 
      msg = "Lock event: type=%s, sessionid=%s\n" % (event_type, sess_id) 
      self.server.status = 1 #logon event 
      self.getUserSessionInfo(sess_id) 
      self.sendHeartbeat() 
      self.server.status = 2 #active user 
      elif event_type == 8: # unlock 
      self.server.status = 3 #logoff event 
      self.server.username = "" 
      self.sendHeartbeat() 
      self.server.status = 0 #no user 
      else: 
      msg = "Other session event: type=%s, sessionid=%s\n" % (event_type, sess_id) 

#   msg += self.getUserSessionInfo(sess_id) 
      self.log(msg) 
#  elif control == win32service.SERVICE_CONTROL_SHUTDOWN: 
#  msg = "Server being shutdown..." 
#  self.log(msg) 

    def SvcDoRun(self): 
    self.ReportServiceStatus(win32service.SERVICE_START_PENDING) 
    try: 
     self.ReportServiceStatus(win32service.SERVICE_RUNNING) 
     self.log('Starting Service') 
     self.start() 
     self.log('Waiting') 
     win32event.WaitForSingleObject(self.stop_event, win32event.INFINITE) 
     self.log('Done') 
    except Exception, x: 
     self.logErr('Exception : %s' % x) 
     self.SvcStop() 

    def SvcStop(self): 
    self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 
    self.log('Stopping Service') 
    self.stop() 
    self.log('Stopped') 
    win32event.SetEvent(self.stop_event) 
    self.ReportServiceStatus(win32service.SERVICE_STOPPED) 

    def sendHeartbeat(self): 
    # 0 = no user, standard beat (format: 0||hostname) 
    # 1 = login event (format: 1|username|hostname) 
    # 2 = active user session 
    # 3 = logout event (format: 2|username|hostname) 
    # 4 = Service exception (format 3||hostname) 
    return self.server.sendHeartbeat(category=self.server.status, username=self.server.username) 

    def start(self): pass 
    # to be overridden 
    def stop(self): pass 
    # to be overridden 

    #reboot/halt makes a different call than 'net stop mytestservice' 
    def SvcShutdown(self): 
    msg = "Server being shutdown..." 
    self.log(msg) 
    self.SvcStop() 


def instart(cls, name, display_name=None, stay_alive=True, exe_name="caedmSAM.exe"): 
    ''' 
     Install and Start (auto) a Service 

      cls : the class (derived from Service) that implement the Service 
      name : Service name 
      display_name : the name displayed in the service manager 
      stay_alive : Service will stop on logout if False 
    ''' 
    cls._svc_name_ = name 
    cls._svc_display_name_ = display_name or name 
    cls._exe_name_ = exe_name 
    cls._svc_description_ = "CAEDM SAM Server registration and montioring service" 
    try: 
     module_path=modules[cls.__module__].__file__ 
    except AttributeError: 
     # maybe py2exe went by 
     from sys import executable 
     module_path=executable 
    module_file=splitext(abspath(module_path))[0] 
    cls._svc_reg_class_ = '%s.%s' % (module_file, cls.__name__) 
    if stay_alive: win32api.SetConsoleCtrlHandler(lambda x: True, True) 
    try: 
     win32serviceutil.InstallService(
       cls._svc_reg_class_, 
       cls._svc_name_, 
       cls._svc_display_name_, 
       startType=win32service.SERVICE_AUTO_START 
       ) 
#  print 'Install: OK' 
     win32serviceutil.StartService(
       cls._svc_name_ 
       ) 
#  print 'Start: OK' 
    except Exception, x: 
     print str(x) 
+1

Zwykle usługa może być sygnalizowana po jej zakończeniu, czy nie otrzymasz tego zdarzenia? Wystąpiłoby to, gdyby użytkownik ręcznie zamknął usługę lub system Windows został zamknięty. –

Odpowiedz

1

Czy spróbować PRESHUTDOWN? Zwykle SHUTDOWN jest za późno.

def GetAcceptedControls(self): 
    # Accept SESSION_CHANGE control 
    rc = win32serviceutil.ServiceFramework.GetAcceptedControls(self) 
    rc |= win32service.SERVICE_ACCEPT_SESSIONCHANGE 
    rc |= win32service.SERVICE_ACCEPT_SHUTDOWN 
    rc |= win32service.SERVICE_ACCEPT_PRESHUTDOWN 
    return r 

Następnie w SvcOtherEx() przechwycić Zdarz.wst Shutdown i poprosić o więcej czasu.

if win32service.SERVICE_CONTROL_PRESHUTDOWN: 
    self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, waitHint=10000) 
    win32event.SetEvent(self.stop_event) 
Powiązane problemy