From 8477c4cb22b9ea738a80c11ad0405457f9831167 Mon Sep 17 00:00:00 2001 From: liuwei Date: Mon, 31 Mar 2025 14:13:04 +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 | 160 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 115 insertions(+), 45 deletions(-) diff --git a/win_click.py b/win_click.py index e6a6fd5..7b6166d 100644 --- a/win_click.py +++ b/win_click.py @@ -30,40 +30,114 @@ def kill_process_by_name(process_name): def kill_current_python_process(): - """结束当前Python程序""" + """结束当前Python程序及相关进程""" current_pid = os.getpid() parent_pid = psutil.Process(current_pid).ppid() print(f"当前Python进程PID: {current_pid}, 父进程PID: {parent_pid}") - + # 收集需要在退出前结束的进程 pids_to_kill = [] - - # 查找所有可能与当前进程相关的Python进程 + pid_to_process = {} # 存储PID到进程对象的映射 + + # 查找所有Python进程 for proc in psutil.process_iter(['pid', 'name', 'cmdline']): try: # 如果是Python进程并且不是当前进程 - if ('python' in proc.info['name'].lower() and - proc.info['pid'] != current_pid and - proc.info['pid'] != parent_pid): - # 检查命令行参数,看是否与当前脚本相关 - cmdline = ' '.join(proc.info['cmdline']).lower() - script_name = os.path.basename(__file__).lower() - if script_name in cmdline or "win_click.py" in cmdline: - pids_to_kill.append(proc.info['pid']) - print(f"将结束Python进程: {proc.info['name']} (PID: {proc.info['pid']})") + 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) + + # 检查是否为我们系统的进程(通过命令行或父进程关联) + is_related = False + + # 1. 检查命令行是否与当前脚本相关 + 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() + + 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. 检查进程树关系 + try: + if proc_pid == parent_pid: # 是当前进程的父进程 + is_related = True + + # 检查是否为相同进程树的进程 + 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 - - # 注册程序退出时要杀死的进程 - def on_exit(): - for pid in pids_to_kill: - try: - os.kill(pid, signal.SIGTERM) - except: - pass - - # 返回退出函数,由调用者决定何时执行 - return on_exit + + # 按进程优先级排序(子进程先结束) + def get_process_depth(pid): + depth = 0 + try: + p = psutil.Process(pid) + while p.pid != 1: # 1是系统进程 + depth += 1 + try: + p = psutil.Process(p.ppid()) + except: + break + except: + pass + return depth + + # 将进程按深度排序(深度大的先终止) + pids_sorted = sorted(pids_to_kill, key=get_process_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"强制终止进程: {proc.name()} (PID: {proc.pid})") + proc.kill() + except Exception as e: + print(f"强制终止进程 {proc.pid} 时出错: {e}") + except Exception as e: + print(f"终止进程时发生错误: {e}") + + # 返回处理函数 + return terminate_processes def find_wechat_window(): @@ -215,38 +289,34 @@ def find_and_click_wechat_login(max_retries=3, retry_interval=5): def update_system(wait_time=15): - """完整的系统更新流程 - - Args: - wait_time: 等待微信启动的秒数,默认15秒 - """ + """完整的系统更新流程""" print("=== 开始系统更新流程 ===") - + # 步骤1: 结束微信进程 print("步骤1: 正在结束微信进程...") if kill_process_by_name("WeChat"): print("微信进程已终止") else: print("未找到运行中的微信进程") - - # 步骤2: 注册结束当前Python进程的函数 - exit_handler = kill_current_python_process() - + + # 步骤2: 获取终止当前Python进程的函数 + terminate_processes = kill_current_python_process() + # 步骤3: 准备重启系统 print("步骤3: 准备重启系统...") - + # 查找bot_start.bat文件 current_dir = os.path.dirname(os.path.abspath(__file__)) bat_path = os.path.join(current_dir, "bot_start.bat") - + if not os.path.exists(bat_path): # 向上查找一级目录 parent_dir = os.path.dirname(current_dir) bat_path = os.path.join(parent_dir, "bot_start.bat") - + if os.path.exists(bat_path): print(f"找到启动脚本: {bat_path}") - + # 创建一个临时的启动脚本,用于在当前进程结束后启动系统 temp_bat = os.path.join(os.environ.get('TEMP', os.getcwd()), "restart_system.bat") with open(temp_bat, "w") as f: @@ -261,20 +331,20 @@ echo 尝试登录微信... start "" "python" "{os.path.abspath(__file__)}" --login-only exit """) - + # 启动临时脚本 print("启动临时脚本执行重启...") # 使用subprocess.Popen而不是run,这样不会等待它完成 - subprocess.Popen(["cmd", "/c", temp_bat], - shell=True, - creationflags=subprocess.CREATE_NEW_CONSOLE) - + subprocess.Popen(["cmd", "/c", temp_bat], + shell=True, + creationflags=subprocess.CREATE_NEW_CONSOLE) + # 等待一小段时间确保脚本开始运行 time.sleep(1) - + # 结束当前Python相关进程并退出 print("结束当前进程...") - exit_handler() + terminate_processes() sys.exit(0) else: print(f"未找到启动脚本 bot_start.bat,无法自动重启系统")