美文网首页
Python 创建windows服务并打包EXE、安全模式可启动

Python 创建windows服务并打包EXE、安全模式可启动

作者: Bug2Coder | 来源:发表于2019-08-05 13:23 被阅读0次

    一、 创建服务程序

    # encoding=utf-8
    import os
    import sys
    import winerror
    import win32serviceutil
    import win32service
    import win32event
    import servicemanager
    import win32timezone
     
     
    class PythonService(win32serviceutil.ServiceFramework):
     
        # 服务名
        _svc_name_ = "PythonService1"
        # 服务显示名称
        _svc_display_name_ = "PythonServiceDemo"
        # 服务描述
        _svc_description_ = "Python service demo."
     
        def __init__(self, args):
            win32serviceutil.ServiceFramework.__init__(self, args)
            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
            self.logger = self._getLogger()
            self.isAlive = True
     
        def _getLogger(self):
            import logging
            import os
            import inspect
     
            logger = logging.getLogger('[PythonService]')
     
            this_file = inspect.getfile(inspect.currentframe())
            dirpath = os.path.abspath(os.path.dirname(this_file))
            handler = logging.FileHandler(os.path.join(dirpath, "service.log"))
     
            formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
            handler.setFormatter(formatter)
     
            logger.addHandler(handler)
            logger.setLevel(logging.INFO)
     
            return logger
     
        def SvcDoRun(self):
            import time
            self.logger.error("svc do run....")
            try:
                while self.isAlive:
                    self.logger.error("I am alive.")
                    time.sleep(1)
                    # 等待服务被停止
                    # win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
            except Exception as e:
                self.logger.error(e)
                time.sleep(60)
     
        def SvcStop(self):
            # 先告诉SCM停止这个过程
            self.logger.error("svc do stop....")
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
            # 设置事件
            win32event.SetEvent(self.hWaitStop)
            self.isAlive = False
     
     
    if __name__ == '__main__':
        if len(sys.argv) == 1:
            try:
                src_dll = os.path.abspath(servicemanager.__file__)
                servicemanager.PrepareToHostSingle(PythonService)
                servicemanager.Initialize("PythonService", src_dll)
                servicemanager.StartServiceCtrlDispatcher()
            except win32service.error as details:
                import winerror
                if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
                    win32serviceutil.usage()    
        else:
            win32serviceutil.HandleCommandLine(PythonService)  # 参数和上述定义类名一致
    

    注意:
    1、服务程序文件路径为:C:\Windows\System32目录下

    二、 打包EXE,并注册服务
    1、安装打包程序:pyinstaller
    命令:pip install pyinstaller
    2、打包服务程序
    打包单文件命令:pyinstaller -F PythonService.py
    3、注册服务
    命令:PythonService.exe install
    4、相关命令
    sc config 服务名 start= demand     //手动
    
    sc config 服务名 start= auto       //自动
    
    sc config 服务名 start= disabled //禁用
    
    sc start 服务名
    
    sc stop 服务名
    
    三、安全模式注册表配置
    注册表配置路径:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SafeBoot
    在各安全模式中创建子项
    名字:服务名
    数据:Service
    
    Demoservice
    四、获取关机重启事件
    # -*- coding:utf-8 -*-
    # A Demo of a service that takes advantage of the additional notifications
    # available in later Windows versions.
    
    # Note that all output is written as event log entries - so you must install
    # and start the service, then look at the event log for messages as events
    # are generated.
    
    # Events are generated for USB device insertion and removal, power state
    # changes and hardware profile events - so try putting your computer to
    # sleep and waking it, inserting a memory stick, etc then check the event log
    
    import win32serviceutil, win32service
    import win32event
    import servicemanager
    import win32timezone   # pyinstaller 打包时没有导入,需要主动导入
    import sys, os,time
    import winerror
    
    
    class EventDemoService(win32serviceutil.ServiceFramework):
        _svc_name_ = "PyServiceEventDemo"
        _svc_display_name_ = "Python Service Event Demo"
        _svc_description_ = "Demonstrates a Python service which takes advantage of the extra notifications"
    
        def __init__(self, args):
            win32serviceutil.ServiceFramework.__init__(self, args)
            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
            # register for a device notification - we pass our service handle
            # instead of a window handle.
    
        # Override the base class so we can accept additional events.
        def GetAcceptedControls(self):
            # say we accept them all.
            rc = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
            rc |= win32service.SERVICE_ACCEPT_SHUTDOWN\
            | win32service.SERVICE_ACCEPT_PRESHUTDOWN
            return rc
    
        # All extra events are sent via SvcOtherEx (SvcOther remains as a
        # function taking only the first args for backwards compat)
        def SvcOtherEx(self, control, event_type, data):
       
            if win32service.SERVICE_CONTROL_PRESHUTDOWN:
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, waitHint=100) # waitHint:延时
                msg = "System is stop: type=%s, data=%s" % (event_type, data)
               # 此处可以写自己的程序
            else:
                msg = 'None'
            servicemanager.LogMsg(
                servicemanager.EVENTLOG_INFORMATION_TYPE,
                0xF000,  # generic message
                (msg, '')
            )  # 写入程序日志--事件查看器、windows日志、应用程序
    
    
        def SvcStop(self):
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
            win32event.SetEvent(self.hWaitStop)
    
        def SvcDoRun(self):
            # do nothing at all - just wait to be stopped
            win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
            # Write a stop message.
            servicemanager.LogMsg(
                servicemanager.EVENTLOG_INFORMATION_TYPE,
                servicemanager.PYS_SERVICE_STOPPED,
                (self._svc_name_, '')
            )
    
    
    if __name__ == '__main__':
        if len(sys.argv) == 1:
            try:
                src_dll = os.path.abspath(servicemanager.__file__)
                servicemanager.PrepareToHostSingle(EventDemoService)
                servicemanager.Initialize("EventDemoService", src_dll)
                servicemanager.StartServiceCtrlDispatcher()
            except win32service.error as details:
                if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
                    win32serviceutil.usage()
        else:
            win32serviceutil.HandleCommandLine(EventDemoService)  # 参数和上述定义类名一致
    
    windows服务创建并设置group组类别,实现开机快速启动

    步骤:重写win32serviceutil模块中的InstallService、HandleCommandLine这两个模块

    # -*- coding:utf-8 -*-
    import contextlib
    import json
    import win32com.client
    import win32con
    import win32timezone
    import warnings
    
    import win32serviceutil
    from win32serviceutil import usage, GetServiceClassString, StartService, RestartService, WaitForServiceStatus, \
        LocateSpecificServiceExe, DebugService, InstallService, RemoveService, ChangeServiceConfig
    import win32event
    import servicemanager
    import win32timezone  # pyinstaller 打包时没有导入,需要主动导入
    import win32service, win32api, winerror
    import sys, os
    import Service
    
    error = RuntimeError
    
    # 服务安装方法
    def InstallService(pythonClassString, serviceName, displayName, startType=None, errorControl=None, bRunInteractive=0,
                       serviceDeps=None, userName=None, password=None, exeName=None, perfMonIni=None, perfMonDll=None,
                       exeArgs=None,
                       description=None, delayedstart=None):
        # Handle the default arguments.
        if startType is None:
            startType = win32service.SERVICE_DEMAND_START
        serviceType = win32service.SERVICE_WIN32_OWN_PROCESS
        if bRunInteractive:
            serviceType = serviceType | win32service.SERVICE_INTERACTIVE_PROCESS
        if errorControl is None:
            errorControl = win32service.SERVICE_ERROR_NORMAL
    
        exeName = '"%s"' % win32serviceutil.LocatePythonServiceExe(exeName)  # None here means use default PythonService.exe
        commandLine = win32serviceutil._GetCommandLine(exeName, exeArgs)
        hscm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_ALL_ACCESS)
        try:
            hs = win32service.CreateService(hscm,
                                            serviceName,
                                            displayName,
                                            win32service.SERVICE_ALL_ACCESS,  # desired access
                                            serviceType,  # service type
                                            startType,
                                            errorControl,  # error control type
                                            commandLine,
                                            "Base",     # 修改此处的None为Base组,即可开机快速启动,提高服务优先级
                                            0,
                                            serviceDeps,
                                            userName,
                                            password)
            if description is not None:
                try:
                    win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_DESCRIPTION, description)
                except NotImplementedError:
                    pass  ## ChangeServiceConfig2 and description do not exist on NT
            if delayedstart is not None:
                try:
                    win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedstart)
                except (win32service.error, NotImplementedError):
                    ## delayed start only exists on Vista and later - warn only when trying to set delayed to True
                    if delayedstart:
                        warnings.warn('Delayed Start not available on this system')
            win32service.CloseServiceHandle(hs)
        finally:
            win32service.CloseServiceHandle(hscm)
        win32serviceutil.InstallPythonClassString(pythonClassString, serviceName)
        # If I have performance monitor info to install, do that.
        if perfMonIni is not None:
            win32serviceutil.InstallPerfmonForService(serviceName, perfMonIni, perfMonDll)
    
    
    def HandleCommandLine(cls, serviceClassString=None, argv=None, customInstallOptions="", customOptionHandler=None):
        """Utility function allowing services to process the command line.
    
        Allows standard commands such as 'start', 'stop', 'debug', 'install' etc.
    
        Install supports 'standard' command line options prefixed with '--', such as
        --username, --password, etc.  In addition,
        the function allows custom command line options to be handled by the calling function.
        """
        err = 0
    
        if argv is None: argv = sys.argv
    
        if len(argv) <= 1:
            usage()
    
        serviceName = cls._svc_name_
        serviceDisplayName = cls._svc_display_name_
        if serviceClassString is None:
            serviceClassString = GetServiceClassString(cls)
    
        # Pull apart the command line
        import getopt
        try:
            opts, args = getopt.getopt(argv[1:], customInstallOptions,
                                       ["password=", "username=", "startup=", "perfmonini=", "perfmondll=", "interactive",
                                        "wait="])
        except getopt.error as details:
            print(details)
            usage()
        userName = None
        password = None
        perfMonIni = perfMonDll = None
        startup = None
        delayedstart = None
        interactive = None
        waitSecs = 0
        for opt, val in opts:
            if opt == '--username':
                userName = val
            elif opt == '--password':
                password = val
            elif opt == '--perfmonini':
                perfMonIni = val
            elif opt == '--perfmondll':
                perfMonDll = val
            elif opt == '--interactive':
                interactive = 1
            elif opt == '--startup':
                map = {"manual": win32service.SERVICE_DEMAND_START,
                       "auto": win32service.SERVICE_AUTO_START,
                       "delayed": win32service.SERVICE_AUTO_START,  ## ChangeServiceConfig2 called later
                       "disabled": win32service.SERVICE_DISABLED}
                try:
                    startup = map[val.lower()]
                except KeyError:
                    print("'%s' is not a valid startup option" % val)
                if val.lower() == "delayed":
                    delayedstart = True
                elif val.lower() == "auto":
                    delayedstart = False
                ## else no change
            elif opt == '--wait':
                try:
                    waitSecs = int(val)
                except ValueError:
                    print("--wait must specify an integer number of seconds.")
                    usage()
    
        arg = args[0]
        knownArg = 0
        # First we process all arguments which pass additional args on
        if arg == "start":
            knownArg = 1
            print("Starting service %s" % (serviceName))
            try:
                StartService(serviceName, args[1:])
                if waitSecs:
                    WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
            except win32service.error as exc:
                print("Error starting service: %s" % exc.strerror)
                err = exc.winerror
    
        elif arg == "restart":
            knownArg = 1
            print("Restarting service %s" % (serviceName))
            RestartService(serviceName, args[1:])
            if waitSecs:
                WaitForServiceStatus(serviceName, win32service.SERVICE_RUNNING, waitSecs)
    
        elif arg == "debug":
            knownArg = 1
            if not hasattr(sys, "frozen"):
                # non-frozen services use pythonservice.exe which handles a
                # -debug option
                svcArgs = " ".join(args[1:])
                try:
                    exeName = LocateSpecificServiceExe(serviceName)
                except win32api.error as exc:
                    if exc.winerror == winerror.ERROR_FILE_NOT_FOUND:
                        print("The service does not appear to be installed.")
                        print("Please install the service before debugging it.")
                        sys.exit(1)
                    raise
                try:
                    os.system("%s -debug %s %s" % (exeName, serviceName, svcArgs))
                # ^C is used to kill the debug service.  Sometimes Python also gets
                # interrupted - ignore it...
                except KeyboardInterrupt:
                    pass
            else:
                # py2exe services don't use pythonservice - so we simulate
                # debugging here.
                DebugService(cls, args)
    
        if not knownArg and len(args) != 1:
            usage()  # the rest of the cmds don't take addn args
    
        if arg == "install":
            knownArg = 1
            try:
                serviceDeps = cls._svc_deps_
            except AttributeError:
                serviceDeps = None
            try:
                exeName = cls._exe_name_
            except AttributeError:
                exeName = None  # Default to PythonService.exe
            try:
                exeArgs = cls._exe_args_
            except AttributeError:
                exeArgs = None
            try:
                description = cls._svc_description_
            except AttributeError:
                description = None
            print("Installing service %s" % (serviceName,))
            # Note that we install the service before calling the custom option
            # handler, so if the custom handler fails, we have an installed service (from NT's POV)
            # but is unlikely to work, as the Python code controlling it failed.  Therefore
            # we remove the service if the first bit works, but the second doesnt!
            try:
                InstallService(serviceClassString, serviceName, serviceDisplayName, serviceDeps=serviceDeps,
                               startType=startup, bRunInteractive=interactive, userName=userName, password=password,
                               exeName=exeName, perfMonIni=perfMonIni, perfMonDll=perfMonDll, exeArgs=exeArgs,
                               description=description, delayedstart=delayedstart)
                if customOptionHandler:
                    customOptionHandler(*(opts,))
                print("Service installed")
            except win32service.error as exc:
                if exc.winerror == winerror.ERROR_SERVICE_EXISTS:
                    arg = "update"  # Fall through to the "update" param!
                else:
                    print("Error installing service: %s (%d)" % (exc.strerror, exc.winerror))
                    err = exc.winerror
            except ValueError as msg:  # Can be raised by custom option handler.
                print("Error installing service: %s" % str(msg))
                err = -1
                # xxx - maybe I should remove after _any_ failed install - however,
                # xxx - it may be useful to help debug to leave the service as it failed.
                # xxx - We really _must_ remove as per the comments above...
                # As we failed here, remove the service, so the next installation
                # attempt works.
                try:
                    RemoveService(serviceName)
                except win32api.error:
                    print("Warning - could not remove the partially installed service.")
    
        if arg == "update":
            knownArg = 1
            try:
                serviceDeps = cls._svc_deps_
            except AttributeError:
                serviceDeps = None
            try:
                exeName = cls._exe_name_
            except AttributeError:
                exeName = None  # Default to PythonService.exe
            try:
                exeArgs = cls._exe_args_
            except AttributeError:
                exeArgs = None
            try:
                description = cls._svc_description_
            except AttributeError:
                description = None
            print("Changing service configuration")
            try:
                ChangeServiceConfig(serviceClassString, serviceName, serviceDeps=serviceDeps, startType=startup,
                                    bRunInteractive=interactive, userName=userName, password=password, exeName=exeName,
                                    displayName=serviceDisplayName, perfMonIni=perfMonIni, perfMonDll=perfMonDll,
                                    exeArgs=exeArgs,
                                    description=description, delayedstart=delayedstart)
                if customOptionHandler:
                    customOptionHandler(*(opts,))
                print("Service updated")
            except win32service.error as exc:
                print("Error changing service configuration: %s (%d)" % (exc.strerror, exc.winerror))
                err = exc.winerror
    
        elif arg == "remove":
            knownArg = 1
            print("Removing service %s" % (serviceName))
            try:
                RemoveService(serviceName)
                print("Service removed")
            except win32service.error as exc:
                print("Error removing service: %s (%d)" % (exc.strerror, exc.winerror))
                err = exc.winerror
        elif arg == "stop":
            knownArg = 1
            print("Stopping service %s" % (serviceName))
            try:
                if waitSecs:
                    win32serviceutil.StopServiceWithDeps(serviceName, waitSecs=waitSecs)
                else:
                    win32serviceutil.StopService(serviceName)
            except win32service.error as exc:
                print("Error stopping service: %s (%d)" % (exc.strerror, exc.winerror))
                err = exc.winerror
        if not knownArg:
            err = -1
            print("Unknown command - '%s'" % arg)
            usage()
        return err
    
    
    class PythonService(win32serviceutil.ServiceFramework):
        # 服务名
        _svc_name_ = "TestService"
        # 服务显示名称
        _svc_display_name_ = "TestService"
        # 服务描述
        _svc_description_ = "Test service"
    
        def __init__(self, args):
            win32serviceutil.ServiceFramework.__init__(self, args)
            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
    
        def GetAcceptedControls(self):
            # 截取消息
            rc = win32serviceutil.ServiceFramework.GetAcceptedControls(self)
            rc |= win32service.SERVICE_ACCEPT_SHUTDOWN \
                  | win32service.SERVICE_ACCEPT_PRESHUTDOWN
            return rc
    
        def SvcOtherEx(self, control, event_type, data):
            if win32service.SERVICE_CONTROL_PRESHUTDOWN:  # 事件验证
                self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, waitHint=100) # 关机操作延时
                # 关机时需要的操作
    
        def SvcDoRun(self):
            try:
                # 你的服务功能
            except Exception as e:
                time.sleep(10)
    
    
    if __name__ == '__main__':
        if len(sys.argv) == 1:
            try:
                src_dll = os.path.abspath(servicemanager.__file__)
                servicemanager.PrepareToHostSingle(PythonService)
                servicemanager.Initialize("PythonService", src_dll)
                servicemanager.StartServiceCtrlDispatcher()
            except win32service.error as details:
                if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
                    win32serviceutil.usage()
        else:
            HandleCommandLine(PythonService)  # 参数和上述定义类名一致
    

    相关文章

      网友评论

          本文标题:Python 创建windows服务并打包EXE、安全模式可启动

          本文链接:https://www.haomeiwen.com/subject/adtmdctx.html