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

求助:获取Windows服务执行脚本的安装目录及配置读取问题

解决Windows服务工作目录为System32导致无法读取安装目录下INI文件的问题

嘿,这个问题我之前帮好几个开发者踩过坑——Windows服务默认会把%WinDir%\System32作为工作目录,所以你的服务用相对路径去读INI文件时,自然找不到安装目录下的目标文件。下面给你几个靠谱的解决思路,挑适合你的来:

方法1:从服务可执行文件路径推导安装目录

Windows服务的可执行文件路径是确定的,你可以通过代码获取当前服务进程的exe路径,然后从中提取出安装目录,再用绝对路径加载INI文件,这是最直接的办法。

示例代码:

import os
import sys
import win32serviceutil

def get_install_directory():
    # 判断当前是否以服务模式运行
    if win32serviceutil.IsWindowsService():
        # 获取服务的可执行文件路径(cx_freeze打包后的exe)
        service_name = win32serviceutil.GetServiceName()
        exe_path = win32serviceutil.GetServiceExecutablePath(service_name)
    else:
        # 调试模式下直接用当前脚本/ exe的路径
        exe_path = sys.executable
    
    # 提取安装目录(假设INI和exe在同一目录)
    return os.path.dirname(exe_path)

# 用绝对路径加载你的INI文件
install_dir = get_install_directory()
ini_path = os.path.join(install_dir, "your_config.ini")
# 接下来就是读取INI的逻辑了...

方法2:服务启动时切换工作目录到安装目录

在服务的核心运行方法开头,先把工作目录切换到安装目录,这样后续用相对路径读取文件就和你调试时一样了,不用改太多现有代码。

示例代码:

import os
import sys
import win32serviceutil
import win32service
import win32event

class MyCustomService(win32serviceutil.ServiceFramework):
    _svc_name_ = "MyCustomService"  # 服务名称
    _svc_display_name_ = "我的自定义服务"  # 显示名称

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        # 提前获取安装目录
        exe_path = win32serviceutil.GetServiceExecutablePath(self._svc_name_)
        self.install_dir = os.path.dirname(exe_path)

    def SvcDoRun(self):
        # 关键一步:切换工作目录到安装目录
        os.chdir(self.install_dir)
        # 现在用相对路径读INI就没问题了
        ini_path = "your_config.ini"
        # 这里写你的服务业务逻辑...
        
        # 等待停止信号
        win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        self.ReportServiceStatus(win32service.SERVICE_STOPPED)

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(MyCustomService)

方法3:打包时把安装目录写入注册表,服务启动时读取

如果你的服务场景比较复杂(比如需要在多个地方用到安装目录),可以在cx_freeze打包MSI时,把安装目录写入系统注册表,然后服务启动时从注册表读取这个路径,这种方式更稳定。

首先修改cx_freeze的setup.py,添加注册表配置:

from cx_Freeze import setup, Executable

# 定义要写入注册表的项:把MSI的安装目录写入HKLM\Software\你的服务名
registry_entries = [
    (
        "HKLM",  # 根键,用HKLM表示系统级注册表
        r"Software\MyCustomService",  # 注册表路径
        "InstallDirectory",  # 键名
        "TARGETDIR",  # 值,TARGETDIR是MSI内置变量,代表用户选择的安装目录
        "REG_SZ"  # 值类型,字符串
    )
]

# 定义可执行文件
executables = [Executable("service.py", base="Win32Service")]

setup(
    name="MyCustomService",
    version="1.0",
    executables=executables,
    options={
        "build_exe": {
            "include_files": ["your_config.ini"],  # 务必把INI文件打包进去
        },
        "bdist_msi": {
            "add_to_path": False,
            "registry_entries": registry_entries,
            # 可选:设置安装图标
            # "install_icon": "your_icon.ico",
        }
    }
)

然后在服务代码里读取注册表:

import winreg

def get_install_dir_from_reg():
    try:
        # 打开注册表项
        with winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"Software\MyCustomService",
            0,
            winreg.KEY_READ
        ) as reg_key:
            # 读取安装目录
            install_dir, _ = winreg.QueryValueEx(reg_key, "InstallDirectory")
            return install_dir
    except FileNotFoundError:
        # 调试时注册表可能不存在, fallback到当前exe目录
        return os.path.dirname(sys.executable)

几个小提醒

  • 测试时尽量在服务模式下运行!调试模式(直接双击exe)的工作目录是当前命令行目录,和服务模式不一样,很容易出现“调试正常,服务跑起来就报错”的情况。
  • 一定要确认INI文件被正确打包到MSI里!cx_freeze的include_files参数必须包含你的INI文件,不然安装后目录里根本没有这个文件,再怎么找也没用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:39:58