From 9dcc7ead821706d02000ed2fba6e15186ddf3856 Mon Sep 17 00:00:00 2001 From: liuwei Date: Mon, 31 Mar 2025 14:17:37 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=9B=B4=E6=96=B0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- win_click.py | 201 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 83 deletions(-) diff --git a/win_click.py b/win_click.py index 7b6166d..e9ed3ac 100644 --- a/win_click.py +++ b/win_click.py @@ -35,108 +35,129 @@ def kill_current_python_process(): parent_pid = psutil.Process(current_pid).ppid() print(f"当前Python进程PID: {current_pid}, 父进程PID: {parent_pid}") - # 收集需要在退出前结束的进程 - pids_to_kill = [] - pid_to_process = {} # 存储PID到进程对象的映射 + # 标记当前进程自己 + this_process = psutil.Process(current_pid) - # 查找所有Python进程 + # 收集需要结束的进程列表 + processes_to_kill = [] + + # 查找所有Python进程和cmd进程 + print("查找所有相关进程...") for proc in psutil.process_iter(['pid', 'name', 'cmdline']): try: - # 如果是Python进程并且不是当前进程 proc_pid = proc.info['pid'] proc_name = proc.info['name'].lower() - if ('python' in proc_name and proc_pid != current_pid): - # 获取完整进程对象以便后续操作 - full_proc = psutil.Process(proc_pid) + # 跳过当前进程 + if proc_pid == current_pid: + continue - # 检查是否为我们系统的进程(通过命令行或父进程关联) - is_related = False - - # 1. 检查命令行是否与当前脚本相关 + # 检查是否是Python相关进程 + if 'python' in proc_name or proc_name.endswith('.exe'): try: - cmdline = ' '.join(proc.info.get('cmdline', [])).lower() - script_name = os.path.basename(__file__).lower() - base_path = os.path.dirname(os.path.abspath(__file__)) - project_name = os.path.basename(base_path).lower() + # 获取完整进程对象和命令行 + full_proc = psutil.Process(proc_pid) + cmdline = ' '.join(full_proc.cmdline()).lower() if full_proc.cmdline() else '' - if any(x in cmdline for x in [script_name, "win_click.py", "robot.py", "main.py", project_name]): - is_related = True - except: - pass - - # 2. 检查进程树关系 + # 检查是否是相关进程 + if ('python' in cmdline or + 'wechatrobot' in cmdline or + 'robot.py' in cmdline or + 'main.py' in cmdline or + 'win_click.py' in cmdline): + processes_to_kill.append(full_proc) + print(f"找到相关Python进程: {full_proc.name()} (PID: {proc_pid}), 命令行: {cmdline[:50]}...") + except Exception as e: + print(f"检查进程 {proc_pid} 时出错: {e}") + + # 检查是否是cmd进程或与bot_start相关的进程 + if proc_name in ['cmd.exe', 'powershell.exe', 'conhost.exe'] or 'cmd' in proc_name: try: - if proc_pid == parent_pid: # 是当前进程的父进程 - is_related = True + full_proc = psutil.Process(proc_pid) + cmdline = ' '.join(full_proc.cmdline()).lower() if full_proc.cmdline() else '' + + # 检查是否是与bot_start或restart_system相关的cmd进程 + if ('bot_start' in cmdline or 'restart_system' in cmdline): + processes_to_kill.append(full_proc) + print(f"找到相关CMD进程: {full_proc.name()} (PID: {proc_pid}), 命令行: {cmdline[:50]}...") + except Exception as e: + print(f"检查CMD进程 {proc_pid} 时出错: {e}") - # 检查是否为相同进程树的进程 - p = full_proc - while p.pid != 1: # 1是系统进程 - if p.ppid() == current_pid or p.ppid() == parent_pid: - is_related = True - break - try: - p = psutil.Process(p.ppid()) - except: - break - except: - pass - - if is_related: - pids_to_kill.append(proc_pid) - pid_to_process[proc_pid] = full_proc - print(f"将结束Python相关进程: {proc.info['name']} (PID: {proc_pid})") except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass + continue - # 按进程优先级排序(子进程先结束) - def get_process_depth(pid): + # 确保不杀死当前进程和系统进程 + processes_to_kill = [p for p in processes_to_kill + if p.pid != current_pid and p.pid != 1 and p.pid != parent_pid] + + # 按进程树层级排序(子进程先杀) + def get_tree_depth(process): depth = 0 try: - p = psutil.Process(pid) - while p.pid != 1: # 1是系统进程 + parent_id = process.ppid() + while parent_id != 0 and parent_id != 1: depth += 1 - try: - p = psutil.Process(p.ppid()) - except: - break + parent = psutil.Process(parent_id) + parent_id = parent.ppid() except: pass return depth - # 将进程按深度排序(深度大的先终止) - pids_sorted = sorted(pids_to_kill, key=get_process_depth, reverse=True) + # 排序进程,子进程先终止 + processes_to_kill.sort(key=get_tree_depth, reverse=True) - # 终止进程函数 + # 定义终止进程的函数 def terminate_processes(): - # 首先尝试温和终止 - try: - for pid in pids_sorted: - if pid != current_pid and pid != parent_pid: - try: - proc = pid_to_process.get(pid) or psutil.Process(pid) - print(f"温和终止进程: {proc.name()} (PID: {pid})") - proc.terminate() - except Exception as e: - print(f"温和终止进程 {pid} 时出错: {e}") - - # 等待进程终止 - gone, alive = psutil.wait_procs([pid_to_process.get(pid) or psutil.Process(pid) - for pid in pids_sorted if pid != current_pid and pid != parent_pid], - timeout=3) - - # 强制终止仍在运行的进程 - for proc in alive: - try: + print(f"开始终止 {len(processes_to_kill)} 个相关进程...") + killed_pids = [] + + # 首先尝试温和终止所有进程 + for proc in processes_to_kill: + try: + if proc.is_running() and proc.pid not in killed_pids: + print(f"温和终止进程: {proc.name()} (PID: {proc.pid})") + proc.terminate() + killed_pids.append(proc.pid) + except Exception as e: + print(f"温和终止进程 {proc.pid} 时出错: {e}") + + # 等待一段时间让进程自行终止 + print("等待进程自行终止...") + time.sleep(2) + + # 强制终止仍然存活的进程 + for proc in processes_to_kill: + try: + if proc.is_running() and proc.pid not in killed_pids: print(f"强制终止进程: {proc.name()} (PID: {proc.pid})") proc.kill() + killed_pids.append(proc.pid) + except Exception as e: + print(f"强制终止进程 {proc.pid} 时出错: {e}") + + # 确保进程终止 + still_running = [] + for proc in processes_to_kill: + try: + if proc.is_running(): + still_running.append(proc) + except: + pass + + # 使用操作系统级命令终止顽固进程 + if still_running: + print(f"仍有 {len(still_running)} 个进程未终止,尝试使用系统命令终止...") + for proc in still_running: + try: + # 使用taskkill强制终止进程 + os.system(f"taskkill /F /PID {proc.pid}") + print(f"使用taskkill终止进程: PID {proc.pid}") except Exception as e: - print(f"强制终止进程 {proc.pid} 时出错: {e}") - except Exception as e: - print(f"终止进程时发生错误: {e}") + print(f"使用taskkill终止进程 {proc.pid} 时出错: {e}") + + print("进程终止操作完成") - # 返回处理函数 + # 返回终止进程的函数 return terminate_processes @@ -322,30 +343,44 @@ def update_system(wait_time=15): with open(temp_bat, "w") as f: f.write(f"""@echo off echo 等待旧进程结束... -timeout /t 2 /nobreak > nul +timeout /t 3 /nobreak > nul +echo 强制终止残留Python进程... +taskkill /F /IM python.exe /T +taskkill /F /IM pythonw.exe /T echo 重新启动系统... start "" "{bat_path}" echo 等待系统启动和微信加载 ({wait_time}秒)... timeout /t {wait_time} /nobreak > nul echo 尝试登录微信... start "" "python" "{os.path.abspath(__file__)}" --login-only -exit +echo 清理临时脚本... +(goto) 2>nul & del "%~f0" & exit """) # 启动临时脚本 - print("启动临时脚本执行重启...") + print(f"启动临时脚本执行重启: {temp_bat}") + # 使用startupinfo隐藏cmd窗口,防止重复窗口 + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + # 使用subprocess.Popen而不是run,这样不会等待它完成 subprocess.Popen(["cmd", "/c", temp_bat], shell=True, - creationflags=subprocess.CREATE_NEW_CONSOLE) + creationflags=subprocess.CREATE_NEW_CONSOLE, + startupinfo=startupinfo) - # 等待一小段时间确保脚本开始运行 - time.sleep(1) + # 等待脚本启动 + print("已启动临时脚本,等待2秒...") + time.sleep(2) # 结束当前Python相关进程并退出 print("结束当前进程...") terminate_processes() - sys.exit(0) + + # 确保进程退出 + print("强制退出当前进程") + os._exit(0) # 使用os._exit确保立即退出 else: print(f"未找到启动脚本 bot_start.bat,无法自动重启系统") return False