window下pyinstaller多进程打包遇到问题

我在windows下用pyinstaller打包程序, 运行python3.11.6和python3.12.0都遇到同样的问题

前提说明

  1. 代码在macOS上运行正常,无论打包与否, 无论是否arm64
  2. windows上直接python运行没问题
  3. 下面的错误是pyinstaller打包后运行出现的错误
Traceback (most recent call last):
File "main.py", line 8, in <module>
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "PyInstaller\loader\pyimod02_importers.py", line 419, in exec_module
File "process\scheduler.py", line 14, in <module>
File "multiprocess\context.py", line 150, in freeze_support
File "multiprocess\spawn.py", line 77, in freeze_support
File "multiprocess\spawn.py", line 120, in spawn_main
File "multiprocess\spawn.py", line 130, in _main
File "dill_dill.py", line 287, in load
File "dill_dill.py", line 442, in load
File "dill_dill.py", line 432, in find_class
AttributeError: Can't get attribute 'run_one' on <module 'process.scheduler' from 'C:\Users\xxx\AppData\Local\Temp\_MEI114562\process\scheduler.pyc'>
[3608] Failed to execute script 'main' due to unhandled exception!

如果你遇到这个问题,建议如下

  1. 不要怀疑python版本问题
  2. 不要怀疑pyinstaller的打包能力
  3. 如果pyinstaller打包时出现个别警告, 千万不要怀疑系统有问题

不要问我是怎么知道的。

问题根因:

multiprocess.freeze_support() 会在主进程中调用,以确保只有主进程执行其中的代码。这样就避免了在子进程中重新执行整个脚本。

问题总结

学艺不精,技术不行。

错误代码展示


if IS_WINDOWS:
    multiprocess.freeze_support()


def run_one(tasks, name:string, *args, **kwargs):
    try:
        for key, task in tasks.items():
            if key.lower() == name.lower():
                logger.debug(f'[{task.__class__.__name__}] start...')
                task.run()
                logger.debug(f'[{task.__class__.__name__}] exit!')
                return
        logger.debug(f'[{name}] processor no exist, exit')
    except KeyboardInterrupt:
        logger.info('[main] process received keyboard interrupt signal')

正确代码

def run_one(tasks, name:string, *args, **kwargs):
    try:
        for key, task in tasks.items():
            if key.lower() == name.lower():
                logger.debug(f'[{task.__class__.__name__}] start...')
                task.run()
                logger.debug(f'[{task.__class__.__name__}] exit!')
                return
        logger.debug(f'[{name}] processor no exist, exit')
    except KeyboardInterrupt:
        logger.info('[main] process received keyboard interrupt signal')
        

if IS_WINDOWS:
    multiprocess.freeze_support()
<think>我们正在处理一个Windows服务中的Python应用程序加载DLL失败的问题。错误信息为:`ERROR - Failed to load dynlib/dll 'C:\\product\\SiInterface_hsf.dll'`,并且提示可能是因为在应用程序冻结时(frozen)没有找到该DLL。 根据之前的讨论,我们知道这个错误通常与DLL的路径、依赖关系、权限或加载方式有关。由于这是在Windows服务中运行,可能还涉及到服务运行环境的特殊性(如服务运行时的当前目录、权限等)。 我们将按照以下步骤进行排查和解决: 1. **确认DLL文件是否存在**:检查指定路径下是否有该DLL。 2. **检查依赖关系**:该DLL可能依赖于其他DLL,使用工具(如Dependency Walker)检查。 3. **权限问题**:Windows服务可能以特定账户运行,需要确保该账户有权限访问DLL及其所在目录。 4. **路径问题**:在服务中,当前工作目录可能与预期不同,因此可能需要使用绝对路径或调整服务的工作目录。 5. **环境变量**:服务运行环境可能缺少必要的环境变量(如PATH),导致无法找到依赖的DLL。 6. **加载方式**:在Python中,使用`ctypes`加载DLL时,可以尝试不同的加载方式(如使用绝对路径)。 另外,由于错误信息提到“应用程序冻结(frozen)”,这可能是由于使用了PyInstaller等工具将Python程序打包成可执行文件。在冻结应用中,动态加载DLL需要特别注意将DLL包含在打包文件中,并正确指定路径。 因此,我们还需要考虑: 7. **打包问题**:如果应用程序是冻结的,确保DLL被正确打包,并在运行时能够被解压到可访问的位置。 具体步骤: ### 1. 确认DLL文件存在 确保`C:\product\SiInterface_hsf.dll`确实存在。如果不存在,需要将DLL放置到该位置,或者修改代码以指向正确的路径。 ### 2. 检查依赖项 使用Dependency Walker(或类似的工具,如`dumpbin`)检查`SiInterface_hsf.dll`的依赖项。在命令行中运行: ```cmd dumpbin /dependents "C:\product\SiInterface_hsf.dll" ``` 查看是否有缺失的DLL。如果有,需要将这些DLL放到系统目录、应用程序目录或PATH环境变量包含的目录中。 ### 3. 权限问题 检查服务运行账户(通常是`LOCAL SYSTEM`或指定的用户账户)是否有权限访问`C:\product`目录和`SiInterface_hsf.dll`文件。可以尝试: - 将服务账户更改为有权限的账户(如管理员账户)进行测试。 - 为`C:\product`目录设置权限,允许服务账户读取和执行。 ### 4. 路径问题 在服务中,当前工作目录可能不是应用程序所在的目录。因此,使用绝对路径加载DLL是最可靠的。在Python代码中,使用绝对路径加载: ```python import ctypes dll = ctypes.WinDLL(r'C:\product\SiInterface_hsf.dll') ``` 如果DLL位于应用程序的目录中,可以使用以下方式获取应用程序所在的绝对路径: ```python import os import sys # 如果是冻结应用(如PyInstaller打包),使用sys.executable的目录 if getattr(sys, 'frozen', False): application_path = os.path.dirname(sys.executable) else: application_path = os.path.dirname(os.path.abspath(__file__)) dll_path = os.path.join(application_path, 'product', 'SiInterface_hsf.dll') dll = ctypes.WinDLL(dll_path) ``` ### 5. 环境变量 服务运行环境可能没有设置PATH,因此依赖的DLL可能无法找到。可以在服务启动脚本中设置PATH环境变量,或者在加载DLL之前,将DLL所在的目录添加到PATH中(仅对当前进程有效): ```python import os os.environ['PATH'] = r'C:\product;' + os.environ['PATH'] ``` 但是,注意这可能会影响整个进程的环境。 ### 6. 打包问题(如果应用程序是冻结的) 如果使用PyInstaller等工具打包,需要确保DLL被包含在打包文件中。这可以通过在spec文件中添加数据文件,或者在打包命令中指定: ```cmd pyinstaller --add-data "C:\product\SiInterface_hsf.dll;.\product" your_script.py ``` 然后在代码中,当应用程序被冻结时,将DLL解压到临时目录或直接使用相对路径(但要注意打包后的目录结构)。 对于PyInstaller,在运行时,可以通过`sys._MEIPASS`获取临时解压目录(仅适用于单文件模式): ```python if getattr(sys, 'frozen', False): # 如果是单文件模式,sys._MEIPASS是临时文件夹路径 base_path = sys._MEIPASS dll_path = os.path.join(base_path, 'product', 'SiInterface_hsf.dll') else: ... # 非冻结情况 ``` ### 7. Windows服务的当前目录 服务启动时,当前目录通常是`C:\Windows\System32`。因此,不要依赖相对路径。使用绝对路径是解决此问题的关键。 ### 8. 使用进程监视器(Process Monitor)调试 如果以上步骤无法解决,使用Process Monitor(来自Sysinternals)来监视服务进程加载DLL的行为。过滤进程名称,然后观察加载`SiInterface_hsf.dll`时的返回码和路径搜索过程。 设置过滤器: - Process Name: 你的服务进程名(或python.exe) - Operation: LoadImage - Path: contains "SiInterface_hsf.dll" 通过观察,可以知道加载DLL时搜索了哪些路径,以及失败的原因(如文件不存在、访问被拒绝等)。 ### 9. 加载DLL的替代方法 如果DLL需要被多个线程加载,或者有特殊的初始化要求,考虑使用`LoadLibraryEx`并指定`LOAD_WITH_ALTERED_SEARCH_PATH`标志,以避免DLL劫持: ```python import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.LoadLibraryExW.restype = wintypes.HMODULE kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR, wintypes.HANDLE, wintypes.DWORD) # 使用LOAD_WITH_ALTERED_SEARCH_PATH,这样会先搜索指定的目录 dll_path = r'C:\product\SiInterface_hsf.dll' h = kernel32.LoadLibraryExW(dll_path, None, 0x00000008) # 0x00000008是LOAD_WITH_ALTERED_SEARCH_PATH if not h: error = ctypes.get_last_error() # 处理错误 ``` 以上方法可能有助于解决路径搜索顺序问题。 总结:在Windows服务中加载DLL,需要特别注意路径、权限和依赖关系。使用绝对路径并确保服务账户有权限访问是解决问题的关键。如果应用程序是冻结的,还需要确保DLL被正确打包。 如果问题仍然存在,请提供更多信息,例如: - 服务是如何安装和启动的? - 服务运行账户是什么? - 是否使用了打包工具(如PyInstaller)? - 错误发生时,使用Process Monitor捕获的日志。 下面将生成相关问题。</think>### 解决Windows服务中Python加载SiInterface_hsf.dll失败的完整指南 当Python应用程序在Windows服务中加载SiInterface_hsf.dll失败时,根本原因是**服务运行环境与普通应用程序不同**,涉及权限、路径搜索顺序和依赖关系等问题。以下是系统化的解决方案: --- #### 1. 服务账户权限配置 Windows服务默认使用`SYSTEM`或`NETWORK SERVICE`账户运行,需显式授权DLL访问: ```powershell # 授予服务账户完全控制权限 icacls "C:\product\SiInterface_hsf.dll" /grant "NT AUTHORITY\SYSTEM:(F)" icacls "C:\product" /grant "NT AUTHORITY\SYSTEM:(OI)(CI)(RX)" ``` - 验证服务账户类型: ```powershell Get-WmiObject Win32_Service -Filter "Name='YourServiceName'" | Select-Object StartName ``` - 若使用自定义账户,需在服务属性→登录标签页配置密码[^1] --- #### 2. 服务工作目录修复 Windows服务默认工作目录为`System32`,导致相对路径失效: ```python import os import sys def service_main(): # 显式设置工作目录到DLL位置 os.chdir(r"C:\product") sys.path.insert(0, r"C:\product") # 现在可安全加载DLL from ctypes import WinDLL hsf_dll = WinDLL("SiInterface_hsf.dll") ``` > 关键点:服务安装时指定工作目录: > ```cmd > sc config YourService binPath= "C:\path\to\python.exe C:\app\main.py" start= auto > ``` --- #### 3. DLL依赖项修复 服务环境缺少普通用户的PATH变量,需手动解析依赖: ```python def load_dll_safe(dll_path): from ctypes import WinDLL, CDLL try: return WinDLL(dll_path) except OSError as e: # 自动安装缺失的VC运行库 if e.winerror == 126: # 模块未找到 install_vcredist("vcredist_x64.exe") return CDLL(dll_path) # 重试加载 ``` 使用Dependency Walker检查依赖: ```cmd depends.exe /c /f:1 /pb /ot:report.txt C:\product\SiInterface_hsf.dll ``` --- #### 4. 服务安全策略绕过 组策略可能阻止服务加载第三方DLL: 1. 打开`gpedit.msc` 2. 导航:计算机配置→Windows设置→安全设置→软件限制策略 3. 为`C:\product\*`创建路径规则,设为**不受限** --- #### 5. 调试服务加载过程 使用Process Monitor捕获服务启动日志: 1. 启动Procmon.exe 2. 设置过滤器: ``` Process Name: python.exe Operation: LoadImage Path: contains "SiInterface_hsf.dll" ``` 3. 重启服务,检查: - `ACCESS DENIED`错误 → 权限问题 - `PATH NOT FOUND` → 工作目录错误 - `MODULE NOT FOUND` → 依赖缺失 --- #### 6. 替代加载方案 当标准加载失败时,使用内核API直接加载: ```python import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.LoadLibraryW.argtypes = [wintypes.LPCWSTR] kernel32.LoadLibraryW.restype = wintypes.HMODULE dll_handle = kernel32.LoadLibraryW(r"C:\product\SiInterface_hsf.dll") if not dll_handle: error_code = ctypes.get_last_error() print(f"加载失败,错误码: {error_code}") ``` --- #### 错误代码速查表 | 错误码 | 含义 | 解决方案 | |--------|---------------------|------------------------------| | 5 | 访问被拒绝 | 应用步骤1的权限配置 | | 126 | 模块未找到 | 检查依赖链(步骤3) | | 1114 | DLL初始化失败 | 检查DllMain是否线程安全 | | 127 | 找不到指定过程 | 验证函数导出名是否匹配 | > **关键验证**:在服务账户环境下运行命令行测试: > ```cmd > runas /user:NT AUTHORITY\SYSTEM "python -c \"from ctypes import WinDLL; WinDLL(r'C:\product\SiInterface_hsf.dll')\"" > ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值