公司动态
影刀RPA 进程与窗口管理:系统级操作
影刀RPA 进程与窗口管理系统级操作作者林焱写RPA流程时经常需要和系统打交道启动一个外部程序、关闭卡死的进程、切换到指定窗口、等待某个程序启动完成。这些操作超出了网页自动化的范围需要用到Windows系统级的进程和窗口管理。这篇文章讲清楚影刀RPA里怎么管理进程和窗口——启动程序、关闭进程、窗口切换、窗口状态控制。一、什么情况用什么场景推荐方法说明启动外部程序【启动程序】指令或Python如启动Excel、计算器等关闭指定程序Python psutil按进程名或PID关闭切换到指定窗口【激活窗口】指令把目标窗口置顶等待程序启动完成轮询检查进程确保程序完全启动后再操作窗口最大化/最小化【窗口操作】指令控制窗口状态获取所有打开的窗口Python win32gui列出所有窗口标题检查程序是否在运行Python psutil避免重复启动强制结束卡死的进程Python os/taskkill应急处理二、怎么做2.1 启动外部程序用影刀指令启动影刀有【启动程序】指令直接配置程序路径和参数即可【启动程序】 程序路径C:\Program Files\Google\Chrome\Application\chrome.exe 参数--new-window https://www.example.com 等待方式不等待异步启动用Python启动importsubprocessimportos# 方式一使用subprocess推荐defstart_program(exe_path,args,waitFalse):启动外部程序cmdf{exe_path}{args}ifwait:# 等待程序执行完毕resultsubprocess.run(cmd,shellTrue,capture_outputTrue,textTrue)print(f返回码:{result.returncode})ifresult.stdout:print(f输出:{result.stdout[:200]})ifresult.stderr:print(f错误:{result.stderr[:200]})else:# 异步启动不等待procsubprocess.Popen(cmd,shellTrue)print(f已启动PID:{proc.pid})returnTrue# 启动Chromestart_program(rC:\Program Files\Google\Chrome\Application\chrome.exe,--new-window https://www.example.com)# 启动Excel并打开文件start_program(rC:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE,rD:\data\report.xlsx)# 启动并等待完成如运行一个命令行工具start_program(ping,www.baidu.com -n 4,waitTrue)2.2 检查进程是否存在拼多多店群自动化报活动上架在启动程序前先检查是否已经在运行importpsutildefcheck_process_running(process_name):检查指定进程是否在运行forprocinpsutil.process_iter([name,pid]):try:ifprocess_name.lower()inproc.info[name].lower():return{found:True,pid:proc.info[pid],name:proc.info[name]}except(psutil.NoSuchProcess,psutil.AccessDenied):continuereturn{found:False}# 检查Chrome是否在运行resultcheck_process_running(chrome)ifresult[found]:print(fChrome正在运行PID:{result[pid]})else:print(Chrome未运行)2.3 关闭进程正常关闭推荐importpsutildefclose_process(process_name,forceFalse):关闭指定进程closed_count0forprocinpsutil.process_iter([name,pid]):try:ifprocess_name.lower()inproc.info[name].lower():print(f正在关闭:{proc.info[name]}(PID:{proc.info[pid]}))ifforce:proc.kill()# 强制结束else:proc.terminate()# 正常终止# 等待进程结束try:proc.wait(timeout5)closed_count1print(f已关闭 (PID:{proc.info[pid]}))exceptpsutil.TimeoutExpired:ifnotforce:print(f正常终止超时强制结束...)proc.kill()closed_count1else:print(f强制结束失败 (PID:{proc.info[pid]}))except(psutil.NoSuchProcess,psutil.AccessDenied)ase:print(f操作失败:{e})continueprint(f共关闭{closed_count}个进程)returnclosed_count# 正常关闭Excelclose_process(excel)# 强制关闭卡死的程序# close_process(notepad, forceTrue)用taskkill命令关闭备用方案importsubprocessdeftaskkill(process_name,forceTrue):用Windows命令行关闭进程flag/Fifforceelsecmdftaskkill{flag}/IM{process_name}resultsubprocess.run(cmd,shellTrue,capture_outputTrue,textTrue)ifresult.returncode0:print(f成功关闭:{process_name})else:print(f关闭失败:{result.stderr})returnresult.returncode0# taskkill(chrome.exe)2.4 窗口管理用影刀指令管理窗口影刀提供了窗口操作相关的指令【激活窗口】→ 把指定窗口置顶 【窗口操作】→ 最大化/最小化/恢复 【获取窗口标题】→ 获取当前活动窗口标题 【关闭窗口】→ 关闭指定窗口用Python管理窗口importwin32guiimportwin32condeflist_all_windows():列出所有可见窗口windows[]defcallback(hwnd,extra):ifwin32gui.IsWindowVisible(hwnd):titlewin32gui.GetWindowText(hwnd)iftitle:# 只列出有标题的窗口windows.append({hwnd:hwnd,title:title,class:win32gui.GetClassName(hwnd)})win32gui.EnumWindows(callback,None)returnwindows# 列出所有窗口all_windowslist_all_windows()forwinall_windows:print(f标题:{w[title]}, 类名:{w[class]})激活指定窗口importwin32guiimportwin32conimporttimedefactivate_window(window_title):激活指定标题的窗口target_hwndNonedefcallback(hwnd,extra):nonlocaltarget_hwndifwin32gui.IsWindowVisible(hwnd):titlewin32gui.GetWindowText(hwnd)ifwindow_title.lower()intitle.lower():target_hwndhwnd win32gui.EnumWindows(callback,None)iftarget_hwnd:# 恢复窗口如果最小化了win32gui.ShowWindow(target_hwnd,win32con.SW_RESTORE)time.sleep(0.2)# 置顶窗口win32gui.SetForegroundWindow(target_hwnd)time.sleep(0.3)titlewin32gui.GetWindowText(target_hwnd)print(f已激活窗口:{title})returnTrueelse:print(f未找到窗口:{window_title})returnFalse# 激活Chrome窗口# activate_window(Chrome)窗口最大化/最小化importwin32guiimportwin32condefcontrol_window(window_title,action): 控制窗口状态 action: maximize / minimize / restore target_hwndNonedefcallback(hwnd,extra):nonlocaltarget_hwndifwin32gui.IsWindowVisible(hwnd):titlewin32gui.GetWindowText(hwnd)ifwindow_title.lower()intitle.lower():target_hwndhwnd win32gui.EnumWindows(callback,None)iftarget_hwnd:ifactionmaximize:win32gui.ShowWindow(target_hwnd,win32con.SW_MAXIMIZE)elifactionminimize:win32gui.ShowWindow(target_hwnd,win32con.SW_MINIMIZE)elifactionrestore:win32gui.ShowWindow(target_hwnd,win32con.SW_RESTORE)print(f窗口{window_title}→{action})returnTruereturnFalse# control_window(Excel, maximize)2.5 等待程序启动完成启动程序后需要等它完全启动才能操作importpsutilimporttimedefwait_for_process(process_name,timeout30):等待指定进程启动start_timetime.time()whiletime.time()-start_timetimeout:forprocinpsutil.process_iter([name]):try:ifprocess_name.lower()inproc.info[name].lower():print(f进程已启动:{process_name})# 额外等待确保窗口完全加载time.sleep(2)returnTrueexcept(psutil.NoSuchProcess,psutil.AccessDenied):continuetime.sleep(0.5)print(f等待超时:{process_name}({timeout}秒))returnFalse# 启动Excel并等待# subprocess.Popen(rC:\Program Files\...\EXCEL.EXE)# wait_for_process(excel, timeout30)2.6 获取窗口位置和大小有时候需要知道窗口在屏幕上的位置才能精确点击importwin32guidefget_window_rect(window_title):获取窗口的位置和大小target_hwndNonedefcallback(hwnd,extra):nonlocaltarget_hwndifwin32gui.IsWindowVisible(hwnd):titlewin32gui.GetWindowText(hwnd)ifwindow_title.lower()intitle.lower():target_hwndhwnd win32gui.EnumWindows(callback,None)iftarget_hwnd:rectwin32gui.GetWindowRect(target_hwnd)# rect (left, top, right, bottom)widthrect[2]-rect[0]heightrect[3]-rect[1]result{left:rect[0],top:rect[1],right:rect[2],bottom:rect[3],width:width,height:height}print(f窗口位置: 左{rect[0]}, 上{rect[1]})print(f窗口大小:{width}x{height})returnresultreturnNone# get_window_rect(计算器)三、有什么坑坑1SetForegroundWindow失败Windows不允许后台程序随意把窗口置顶SetForegroundWindow可能失败。解决先发送一个ALT键欺骗系统再置顶importwin32guiimportwin32conimportwin32apiimporttimedefforce_activate_window(hwnd):强制激活窗口# 发送ALT键win32api.keybd_event(win32con.VK_MENU,0,0,0)win32api.keybd_event(win32con.VK_MENU,0,win32con.KEYEVENTF_KEYUP,0)# 恢复窗口win32gui.ShowWindow(hwnd,win32con.SW_RESTORE)time.sleep(0.1)# 置顶win32gui.SetForegroundWindow(hwnd)time.sleep(0.3)坑2进程名不匹配chrome.exe和Chrome可能匹配不上。进程名包含.exe后缀。TEMU店群矩阵自动化运营核价报活动解决统一用小写比较并同时检查带和不带.exe的情况defcheck_process_flexible(name):灵活匹配进程名name_lowername.lower()forprocinpsutil.process_iter([name]):try:proc_nameproc.info[name].lower()ifname_lowerinproc_nameorname_lower.replace(.exe,)inproc_name:returnTrueexcept:continuereturnFalse坑3关闭进程后资源未释放有些程序如Excel关闭后后台进程可能还在运行占用文件锁。解决关闭后等待几秒确认进程完全退出defclose_and_verify(process_name,timeout10):关闭进程并确认退出close_process(process_name,forceTrue)starttime.time()whiletime.time()-starttimeout:ifnotcheck_process_flexible(process_name):print(f{process_name}已完全退出)returnTruetime.sleep(0.5)print(f{process_name}仍在运行)returnFalse坑4权限不足无法操作进程某些系统进程或以管理员权限运行的程序普通权限无法操作。解决以管理员权限运行影刀客户端。或者在Python里用runas提权不推荐硬编码密码。坑5多显示器下窗口位置异常多显示器环境下窗口的坐标可能为负数在左边的显示器上或者超出主屏幕范围。解决获取窗口位置后检查是否在可见范围内importwin32apidefis_window_visible_on_screen(rect):检查窗口是否在屏幕可见范围内# 获取所有显示器的边界monitorswin32api.EnumDisplayMonitors()formonitorinmonitors:m_rectmonitor[2]# (left, top, right, bottom)# 检查窗口是否和显示器有交集if(rect[0]m_rect[2]andrect[2]m_rect[0]andrect[1]m_rect[3]andrect[3]m_rect[1]):returnTruereturnFalse总结进程和窗口管理的核心要点启动前先检查——避免重复启动同一个程序关闭用terminate不用kill——除非卡死了才强制kill窗口操作用win32gui——比模拟鼠标点击更精确启动后等待——不要启动完立即操作等2秒确保窗口加载完成多显示器注意坐标——窗口可能在负坐标或超范围区域