python程序以win服务方式运行

  1 import os
  2 import subprocess
  3 import sys
  4 import time
  5 
  6 import psutil
  7 import win32event
  8 import win32service
  9 import win32serviceutil
 10 
 11 PROJECT_NAME = "PROJECT_NAME"
 12 STARTUP_FILE_NAME = "main.py"
 13 
 14 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 15 python_dir = sys.executable.replace("pythonservice.exe", "python.exe")
 16 script_dir = os.path.join(BASE_DIR, "logs", "log.log")
 17 
 18 COMMAND = [python_dir, os.path.join(BASE_DIR, STARTUP_FILE_NAME)]
 19 HEARTBEAT_FILE = os.path.join(BASE_DIR, "heartbeat.txt")
 20 TIMEOUT = 300  # 5 分钟无心跳更新,认为卡死
 21 
 22 
 23 class Service(win32serviceutil.ServiceFramework):
 24     _svc_name_ = PROJECT_NAME
 25     _svc_display_name_ = PROJECT_NAME
 26     _svc_description_ = PROJECT_NAME
 27 
 28     def __init__(self, args):
 29         super().__init__(args)
 30         self.stop_event = win32event.CreateEvent(None, 0, 0, None)
 31         self.pid = None
 32         self.running = True
 33 
 34     def start_process(self):
 35         """启动进程并返回 PID"""
 36         proc = subprocess.Popen(COMMAND, creationflags=subprocess.CREATE_NEW_CONSOLE)
 37         self.pid = proc.pid
 38         print(f"启动进程 PID: {self.pid}")
 39 
 40     def is_window_open(self):
 41         """检查进程窗口是否打开"""
 42         if not self.pid:
 43             return False
 44         try:
 45             process = psutil.Process(self.pid)
 46             return process.is_running()
 47         except psutil.NoSuchProcess:
 48             return False
 49 
 50     def kill_process(self):
 51         """强制关闭进程"""
 52         if not self.pid:
 53             return
 54         try:
 55             process = psutil.Process(self.pid)
 56             parent = process.parent()
 57             process.terminate()
 58             time.sleep(1)
 59             if parent and "cmd.exe" in parent.name().lower():
 60                 parent.terminate()
 61             time.sleep(2)
 62         except psutil.NoSuchProcess:
 63             pass
 64 
 65     def get_last_heartbeat(self):
 66         """获取上次心跳时间"""
 67         if not os.path.exists(HEARTBEAT_FILE):
 68             return 0
 69         try:
 70             with open(HEARTBEAT_FILE, "r") as f:
 71                 return float(f.read().strip())
 72         except:
 73             return 0
 74 
 75     def SvcStop(self):
 76         """停止服务"""
 77         self.running = False
 78         self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
 79         self.kill_process()
 80         win32event.SetEvent(self.stop_event)
 81 
 82     def SvcDoRun(self):
 83         """服务运行逻辑"""
 84         self.start_process()
 85         while self.running:
 86             time.sleep(15)
 87 
 88             if self.pid is None and self.running:
 89                 self.start_process()
 90                 continue
 91 
 92             if not self.is_window_open() and self.running:
 93                 print("进程窗口被关闭,重启...")
 94                 self.start_process()
 95                 continue
 96 
 97             if not psutil.pid_exists(self.pid) and self.running:
 98                 print("进程崩溃,重启...")
 99                 self.kill_process()
100                 self.start_process()
101                 continue
102 
103             # last_heartbeat = self.get_last_heartbeat()
104             # print("上次心跳:" + str(last_heartbeat))
105             # if time.time() - last_heartbeat > TIMEOUT and self.running:
106             #     print("进程卡死,重启...")
107             #     self.kill_process()
108             #     self.start_process()
109 
110 
111 if __name__ == "__main__":
112     import servicemanager
113 
114     if len(sys.argv) == 1:
115         try:
116             evtsrc_dll = os.path.abspath(servicemanager.__file__)
117             # 如果修改过名字,名字要统一
118             servicemanager.PrepareToHostSingle(Service)
119             # 如果修改过名字,名字要统一
120             servicemanager.Initialize('Service', evtsrc_dll)
121             servicemanager.StartServiceCtrlDispatcher()
122         except win32service.error as details:
123             import winerror
124 
125             if details == winerror.ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
126                 win32serviceutil.usage()
127     else:
128         # 如果修改过名字,名字要统一
129         win32serviceutil.HandleCommandLine(Service)

 

posted @ 2025-03-25 17:03  Swlymbcty  阅读(7)  评论(0)    收藏  举报