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

PyInstaller生成exe后,Shell脚本传递Windows服务环境参数失败

Windows服务启动时--env参数路径找不到的解决方法

问题概述

使用PyInstaller将service_file.py打包为service_file.exe后,通过PowerShell脚本安装并启动Windows服务:

& 'C:\\files\\service_file.exe' install 

# 启动服务时传递环境参数报错
& 'C:\\file\\service_file.exe' start --env "D:\\full_project\\device\\config\\windows.env"  

单独执行start命令正常,但传递--env参数时提示“路径未找到”错误。

核心原因

  1. 服务参数传递逻辑错误:Windows服务的启动参数不能直接在start命令后追加,必须通过install或update命令的--startupargs参数预先配置——服务由Windows服务控制管理器(SCM)启动,不会直接接收start命令后的参数。
  2. 代码参数解析时机错误:原代码在MyService.run()中调用argparse.parse_args(),但服务运行时的sys.argv是SCM启动服务的参数,并非执行start命令时传入的--env参数。

解决步骤

1. 修正服务启动脚本

通过--startupargs在安装/更新服务时传递环境参数,而非在start命令后追加:

# 首次安装服务并指定启动参数
& 'C:\\files\\service_file.exe' install --startupargs "--env D:\\full_project\\device\\config\\windows.env"

# 若服务已安装,更新启动参数(替换上面的install命令)
& 'C:\\files\\service_file.exe' update --startupargs "--env D:\\full_project\\device\\config\\windows.env"

# 启动服务
& 'C:\\files\\service_file.exe' start

2. 修改Python代码适配参数传递

调整代码逻辑,让服务框架正确获取并传递启动参数:

  • 新增sys导入(原代码缺失)
  • 在服务框架类中解析启动参数,传递给业务逻辑类
  • 移除MyService.run()中的argparse解析逻辑

修改后的完整代码:

import os
import sys
import win32serviceutil
import win32service
import servicemanager
import argparse

class MyService:
    def __init__(self, env_path):
        self.running = True
        self.env_path = env_path

    def stop(self):
        self.running = False

    def run(self):
        application_path = os.path.dirname(sys.executable)
        os.chdir(application_path)

        def load_env(envFile):
            # 提前校验路径存在性,输出明确错误信息
            if not os.path.exists(envFile):
                raise FileNotFoundError(f"环境文件未找到: {envFile}")
            env_obj = Environment(envFile)
            return env_obj

        env_obj = load_env(self.env_path)

        db_agent(env_obj)
        db_agent_download(env_obj)

        client_agent(env_obj)
        init_scheduler()

        app = rest_api(env_obj)
        app.run(port=env_obj.rest_api_port, host="0.0.0.0")


class MyServiceFramework(win32serviceutil.ServiceFramework):
    _svc_name_ = 'DeviceAgent'
    _svc_display_name_ = 'Device Agent Service'

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        # 解析从--startupargs传递来的服务启动参数
        parser = argparse.ArgumentParser()
        parser.add_argument("--env")
        parser.add_argument("--log")
        self.service_args = parser.parse_args(args[1:])

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        self.service_impl.stop()
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

    def SvcDoRun(self):
        self.service_impl = MyService(self.service_args.env)
        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
        self.service_impl.run()


def Main():
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(MyServiceFramework)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(MyServiceFramework)


if __name__ == '__main__':
    Main()

3. 额外注意事项

  • 权限检查:确保服务运行账户(默认是Local System)对环境文件路径有读取权限,若路径在用户私有目录,需手动添加权限。
  • 路径校验:在load_env函数中提前校验文件存在性,能输出更明确的错误信息,便于排查。
  • PyInstaller打包:打包时确保所有依赖(如win32serviceutil、argparse等)都被正确包含,可使用pyinstaller --onefile service_file.py命令打包。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:01:16