You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pywin32 Windows服务无法调用主函数,启动超时卡住问题排查

Windows服务启动超时问题排查与修复

核心问题分析

  • 函数调用错误:svc_manager.py中从scamwatch_main.py导入的是scamwatch_main函数而非模块,调用scamwatch_main.scamwatch_main()会触发AttributeError,但因日志捕获逻辑问题未显示该错误。
  • 服务环境提权失效:scamwatch_main中的管理员权限检查在Windows服务环境下无法正常工作——服务进程无法弹出UAC对话框,run_as_admin()直接失败,且sys.exit()会导致服务进程异常终止,使服务卡在"正在启动"状态。
  • 重复执行逻辑冲突:scamwatch_main启动守护线程后立即返回,导致svc_manager的循环每隔3秒重复调用该函数,造成端口阻塞和监控线程重复初始化,引发资源浪费。

解决方案

1. 修复函数调用错误

在svc_manager.py的main方法中,将错误调用:

scamwatch_main.scamwatch_main()

改为直接调用导入的函数:

scamwatch_main()

2. 适配服务环境的权限处理

Windows服务需提前配置管理员账户运行,无需动态提权。修改scamwatch_main.py,拆分命令行与服务环境的权限逻辑:

  • 命令行运行时保留原提权逻辑
  • 服务环境下跳过提权,避免进程异常终止

3. 修改服务运行逻辑,避免重复执行

svc_manager.py的main方法只需调用一次scamwatch_main,无需循环调用,让主函数内部保持运行状态以等待服务停止信号。

4. 配置服务运行账户

安装服务后,通过services.msc找到ScamWatch Service,在属性→登录选项卡中选择管理员账户,保存后重启服务。

修改后的完整代码示例

svc_manager.py

# this file handles the pywin32 config for using ScamWatch as a service

import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
from scamwatch_main import scamwatch_main
import logging
import os

class SWWinService(win32serviceutil.ServiceFramework):
    _svc_name_ = "ScamWatchService"
    _svc_display_name_ = "ScamWatch Service"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(180)

        log_dir = 'Log Directory Path'  # 替换为实际日志目录
        os.makedirs(log_dir, exist_ok=True)
        log_file = os.path.join(log_dir, 'ScamWatch.log')

        logging.basicConfig(
            filename=log_file,
            level=logging.DEBUG,
            format='%(asctime)s %(levelname)s: %(message)s'
        )
        logging.info("ScamWatch initialized Successfully")

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        logging.info('ScamWatch service stopping')

    def SvcRun(self):
        try:
            servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                (self._svc_name_, ''))
            
            self.main()
        except Exception as e:
            logging.error(f"Exception in SvcRun: {e}")
            servicemanager.LogErrorMsg(f"Exception in SvcRun: {e}")
            self.SvcStop()
    
    def main(self):
        logging.info('ScamWatch service running')
        try:
            logging.info("calling main from scamwatch_main.py")
            scamwatch_main()
        except Exception as e:
            logging.error(f"Exception in main loop: {e}")
        # 无限等待停止信号
        win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
        logging.info('Service stopped by exiting loop in svc_manager!')


if __name__ == '__main__':
    servicemanager.Initialize()
    servicemanager.PrepareToHostSingle(SWWinService)
    servicemanager.StartServiceCtrlDispatcher()

scamwatch_main.py

# This is the main executable for ScamWatch.
import threading
import time
import sys
import logging
import os
from process_blocking import monitor_process, one_time_connection
from elevate import is_admin, run_as_admin
from port_blocking import block_all_ports

def scamwatch_main():
    logging.info("ScamWatch is running!")

    try:
        # Check and block common remote connection ports
        logging.info("Blocking remote connection ports if needed...")
        block_all_ports()

        # Begin monitoring processes for common RCA App exes
        logging.info("Now monitoring for RCA applications. Don't worry, you're safe :)")
        monitor_thread = threading.Thread(target=monitor_process)
        monitor_thread.daemon = True
        monitor_thread.start()

        # 保持主进程运行,直到服务停止
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        logging.info("ScamWatch has been stopped by user!")
    except Exception as e:
        logging.error(f"Something went wrong! Here's the error info: {e}")


# Run this script as an executable, not as a file to import
if __name__ == "__main__":
    # 命令行运行时保留管理员权限检查
    if not is_admin() and "--elevated" not in sys.argv:
        logging.info("Attempting to restart with administrative privileges...")
        if run_as_admin():
            logging.info("Restarted with administrative privileges.")
            sys.exit(0)
        else:
            logging.error("Failed to restart with administrative privileges.")
            sys.exit(1)
    scamwatch_main()

内容的提问来源于stack exchange,提问作者alilprogrammerappears

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:06:00