Python打包的.exe在无Python机器运行异常,抛wmi.py错误求助
进程超时终止工具跨机器运行问题排查与解决方案
问题背景
- 开发Windows平台可反复调用的.exe工具,用于终止运行超时(卡顿)的指定进程,需传入3个命令行参数:超时分钟数、进程所有者名称、进程全名
- 开发环境:Python 3.11 + VS Code,依赖
wmi库获取进程信息,通过PyInstaller打包为单文件.exe - 本地测试(含Python环境)运行正常,但无Python环境的同事机器上出现两类问题:
- 传入正确参数后,程序运行后无输出、无操作、无异常退出
- 取消进程打印注释后,机器会列出部分进程,随后程序终止或抛出异常,未执行目标进程终止逻辑
异常日志
Traceback (most recent call last): File "wmi.py", line 458, in __call__ File "<COMObject <unknown>>", line 2, in ExecMthod_ pywintypes.com_error: (-2147352567, 'Exception occured.', (0, 'SWbemObjectEx', 'Not found', None, 0, -2147217406), None) During handling of the above exception, another exception occured: Traceback (most recent call last): File "ShutDownTheProcess.py", line 27, in <module> File "wmi.py", line 473, in __call__ File "wmi.py", line 258, in handle_com_error wmi.x_wmi: <x_wmi: Unexpected COM Error (-2147352567, 'Exception occured.', (0, 'SWbemObjectEx', 'Not found', None, 0, -2147217406), None)> [20212] Failed to execute script 'ShutDownProcess' due to unhandled exception!
原始代码
# Command line parameters: num_of_minutes user_name process_name import sys import wmi from datetime import datetime from datetime import timedelta # Controll and taking arguments, if they are wrong or missing, the program ends if len(sys.argv)<4: print('Wrong number of arguments, expecting parameters number_of_minutes owner process_name') sys.exit() else: try: in_num_of_minutes = int(sys.argv[1]) in_user_name = str(sys.argv[2]) in_process_name = str(sys.argv[3]) except: print('Program has difficulties reading the arguments, expecting parameters number_of_minutes owner process_name') sys.exit() # Initialize wmi connection c = wmi.WMI() # Go through all running processes for process in c.Win32_Process(): # print(f' {process.ProcessId:<10} {process.Name} {process.GetOwner()}') if (process.Name == in_process_name) and (in_user_name in process.GetOwner()): created = process.CreationDate.split('.')[0] # When was it created, it is in a weard format how_long_dt = datetime.now() - datetime.strptime(created, '%Y%m%d%H%M%S') # How long has it been running, datetime format minutes = how_long_dt.total_seconds()/60 # Kolik minut běží # print(minutes) if minutes>in_num_of_minutes: print(f'Closing process with ID {process.ProcessId} and name {process.Name} run by {process.GetOwner()[2]}, that was running for {round(minutes,4)} minutes.') process.Terminate() # Shout down the process
打包命令
pyinstaller --onefile ShutDownProcess.py
解决方案建议
- 捕获WMI操作异常:遍历进程时,部分系统核心进程无法获取所有者信息或执行终止操作,会触发未处理异常导致程序崩溃。需在循环内部添加异常捕获逻辑,跳过异常进程继续执行:
for process in c.Win32_Process(): try: if (process.Name == in_process_name) and (in_user_name in process.GetOwner()): created = process.CreationDate.split('.')[0] how_long_dt = datetime.now() - datetime.strptime(created, '%Y%m%d%H%M%S') minutes = how_long_dt.total_seconds()/60 if minutes>in_num_of_minutes: print(f'Closing process with ID {process.ProcessId} and name {process.Name} run by {process.GetOwner()[2]}, that was running for {round(minutes,4)} minutes.') process.Terminate() except (wmi.x_wmi, pywintypes.com_error): continue - 优化进程查询逻辑:避免遍历所有进程,直接通过WMI条件查询过滤目标进程,减少不必要的COM调用,降低异常概率:
# 替换原遍历逻辑,直接查询指定名称的进程 target_processes = c.Win32_Process(Name=in_process_name) for process in target_processes: # 后续逻辑同上(需加异常捕获) - 调整PyInstaller打包参数:单文件打包可能导致WMI依赖的COM组件加载异常,尝试添加隐藏导入参数或改用多文件打包:
# 添加隐藏依赖打包 pyinstaller --onefile --hidden-import wmi --hidden-import pywintypes ShutDownProcess.py # 或多文件打包(兼容性更好) pyinstaller --onedir ShutDownProcess.py - 改用psutil库替代wmi:psutil库兼容性更强,跨平台且打包时问题更少,替换核心逻辑示例:
- 安装依赖:
pip install psutil - 核心代码替换:
import sys import psutil from datetime import datetime # 参数处理逻辑不变... for proc in psutil.process_iter(['pid', 'name', 'create_time', 'username']): try: proc_info = proc.info # 匹配进程名称和所有者(注意格式为DOMAIN\USER或.\USER) if proc_info['name'] == in_process_name and (in_user_name in proc_info['username']): create_time = datetime.fromtimestamp(proc_info['create_time']) minutes = (datetime.now() - create_time).total_seconds()/60 if minutes > in_num_of_minutes: print(f'Closing process with ID {proc_info["pid"]} and name {proc_info["name"]} run by {proc_info["username"]}, that was running for {round(minutes,4)} minutes.') proc.terminate() except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): # 跳过无法访问的进程 continue
- 安装依赖:
- 权限适配:确保同事以管理员权限运行.exe,部分系统进程或高权限进程需要管理员权限才能查询或终止。
内容的提问来源于stack exchange,提问作者Andy Kučerová
相关产品推荐
相关产品推荐

