求助:获取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
相关产品推荐
相关产品推荐

