news 2026/6/27 2:12:35

帮我构思一个项目:Trae、Codearts atomcode 等AI agent的调度中心 优先windows系统,通过句柄获得这些AI agent的任务信息,对其进行跟踪,用户可以通过调度中心发布

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
帮我构思一个项目:Trae、Codearts atomcode 等AI agent的调度中心 优先windows系统,通过句柄获得这些AI agent的任务信息,对其进行跟踪,用户可以通过调度中心发布

帮我构思一个项目:Trae、Codearts atomcode 等AI agent的调度中心 优先windows系统,通过句柄获得这些AI agent的任务信息,对其进行跟踪,用户可以通过调度中心发布新任务,并修改和回应当前的任务。

群星(Star)- AI Agent 调度中心

1. 项目概述

1.1 项目名

  • 中文名:群星
  • 英文名:Star

1.2 命名寓意

“群星闪耀,各司其职。调度有序,光芒汇聚。”

多个 AI Agent 如同夜空中的群星,各自闪耀着独特的光芒。Star 调度中心就像星座的连线者,将这些独立的光芒串联成完整的图景,让每一颗星的输出都能汇聚成更强大的力量。

1.3 Slogan

群星(Star)—— 让每一个 AI Agent 都成为你的星座

1.4 核心目标

在 Windows 系统上,通过系统级 API(句柄、窗口消息、进程监控)实现对多个 AI 编程助手(Trae、CodeArts Atomcode、Cursor、Copilot 等)的统一调度与任务管理。

1.5 核心功能

  • 星图(Agent 发现):自动检测正在运行的 AI Agent 进程
  • 星轨(任务队列):创建、分配、修改、暂停、恢复任务
  • 星语(对话监控):截获 Agent 的输入/输出流
  • 星令(任务干预):向运行中的 Agent 注入新指令或修正方向
  • 星辉(结果收集):汇总各 Agent 输出,统一展示

2. 术语映射

通用术语群星(Star)术语说明
AI Agent星(Star)每个 Agent 是一颗星
Agent 进程星体(Star Body)运行中的 Agent 实例
任务队列星轨(Orbit)任务的流转路径
新任务新星(Nova)新创建的任务
任务分配授星(Assign)将任务交给某颗星
任务修改调轨(Adjust Orbit)修改运行中的任务方向
结果输出星辉(Starlight)Agent 的产出
用户反馈回响(Echo)用户对 Agent 输出的回应
多 Agent 协同星座(Constellation)多个星协同完成复杂任务

3. 技术架构

┌─────────────────────────────────────────────────────────────┐ │ 群星(Star)前端 │ │ (Web UI: React + Vite, 或 Electron 桌面端) │ │ 星图面板 │ 星轨队列 │ 星语流 │ 星辉审查 │ └───────────────────────────┬─────────────────────────────────┘ │ WebSocket / IPC ┌───────────────────────────▼─────────────────────────────────┐ │ 星核(Star Core)- Python 3.12 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ 寻星者 │ │ 星轨引擎 │ │ 星语路由 │ │ │ │ StarSeeker │ │ OrbitEngine │ │ StarlightRouter │ │ │ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │ │ │ │ │ │ │ ┌──────▼─────────────────▼────────────────────▼─────────┐ │ │ │ 观星台(Observatory) │ │ │ │ · Win32 API (句柄/窗口/进程) │ │ │ │ · UI Automation (元素定位/文本注入) │ │ │ │ · 键盘/鼠标模拟 (SendInput/PostMessage) │ │ │ │ · 剪贴板监控 (Clipboard API) │ │ │ └───────────────────────────────────────────────────────┘ │ └───────────────────────────┬─────────────────────────────────┘ │ 句柄 / UIA / 消息 ┌───────────────────────────▼─────────────────────────────────┐ │ 群星闪耀(Agent 进程) │ │ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │ │ │ Trae ☆ │ │CodeArts Atom☆│ │ Cursor ☆ │ ... │ │ └──────────┘ └──────────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘

4. 核心模块命名与实现

4.1 寻星者(StarSeeker)- Agent 发现

class StarSeeker: """寻星者 - 发现并管理 AI Agent 进程""" # 已知星体特征 STAR_SIGNATURES = { 'trae': { 'process_names': ['Trae.exe', 'trae.exe'], 'window_class': ['Chrome_WidgetWin_1', 'TraeMainWindow'], 'window_title_patterns': ['Trae', 'trae'], }, 'codearts_atomcode': { 'process_names': ['CodeArts.exe', 'AtomCode.exe'], 'window_class': ['Chrome_WidgetWin_1'], 'window_title_patterns': ['CodeArts', 'AtomCode'], }, 'cursor': { 'process_names': ['Cursor.exe'], 'window_class': ['Chrome_WidgetWin_1'], 'window_title_patterns': ['Cursor'], }, } def scan_skies(self) -> list['StarBody']: """扫描天际,发现所有闪耀的星""" stars = [] for proc in psutil.process_iter(['pid', 'name']): for star_type, sig in self.STAR_SIGNATURES.items(): if proc.info['name'].lower() in [n.lower() for n in sig['process_names']]: hwnd = self._locate_light(proc.info['pid'], sig) if hwnd: stars.append(StarBody( star_type=star_type, pid=proc.info['pid'], hwnd=hwnd, process=proc )) return stars def _locate_light(self, pid: int, sig: dict) -> int: """定位星光 - 通过 PID 找到主窗口句柄""" def callback(hwnd, hwnds): if win32gui.IsWindowVisible(hwnd): _, found_pid = win32process.GetWindowThreadProcessId(hwnd) if found_pid == pid: title = win32gui.GetWindowText(hwnd) if any(p in title for p in sig['window_title_patterns']): hwnds.append(hwnd) return True hwnds = [] win32gui.EnumWindows(callback, hwnds) return hwnds[0] if hwnds else None

4.2 授星者(StarAssigner)- 文本注入

class StarAssigner: """授星者 - 向星体注入星令""" def send_starlight(self, star: 'StarBody', starlight: str) -> bool: """向星体发送星辉指令""" # 策略1: 观星术(UIA)- 最精准 if self._try_observatory(star.hwnd, starlight): return True # 策略2: 星光传递(剪贴板) if self._try_starlight_transfer(star, starlight): return True # 策略3: 星波(消息)- 适用于原生控件 if self._try_starwave(star.hwnd, starlight): return True return False def _try_observatory(self, hwnd: int, starlight: str) -> bool: """观星术 - 通过 UI Automation 精确定位输入框""" import uiautomation as uia try: window = uia.ControlFromHandle(hwnd) # 寻找星语输入口 edit = window.EditControl( searchDepth=5, ClassName='TextArea' ) if edit: edit.SetValue(starlight) edit.SendKeys('{Enter}') return True except Exception: pass return False def _try_starlight_transfer(self, star: 'StarBody', starlight: str) -> bool: """星光传递 - 剪贴板注入""" import pyperclip original = pyperclip.paste() try: pyperclip.copy(starlight) win32gui.SetForegroundWindow(star.hwnd) time.sleep(0.1) import pyautogui pyautogui.hotkey('ctrl', 'v') time.sleep(0.1) pyautogui.press('enter') return True finally: pyperclip.copy(original)

4.3 观星者(StarGazer)- 输出捕获

class StarGazer: """观星者 - 捕获星体的输出""" def gaze(self, star: 'StarBody') -> str: """凝视星辉 - 获取 Agent 最后一条回复""" import uiautomation as uia window = uia.ControlFromHandle(star.hwnd) output_controls = window.TextControl( searchDepth=10, ClassName='TextBlock' ) if output_controls: return output_controls.Name return "" def continuous_gaze(self, star: 'StarBody', on_starlight_change): """持续观星 - 监控输出变化""" import threading def gaze_thread(): last_starlight = "" while star.is_shining: current = self.gaze(star) if current != last_starlight: on_starlight_change(star, current) last_starlight = current time.sleep(1) threading.Thread(target=gaze_thread, daemon=True).start()

5. 星轨引擎(OrbitEngine)- 任务调度

5.1 星轨任务模型

from dataclasses import dataclass from enum import Enum from datetime import datetime from typing import Optional class StarStatus(Enum): """星芒状态""" NASCENT = "nascent" # 初生(新创建) ORBITING = "orbiting" # 入轨(已分配) SHINING = "shining" # 闪耀(Agent 正在处理) AWAITING_ECHO = "awaiting" # 待回响(等待用户审查) CONSTELLATED = "constellated" # 成星(完成) FADED = "faded" # 暗淡(失败) DARKENED = "darkened" # 熄灭(取消) class StarPriority(Enum): """星等(优先级)""" DIM = 0 # 暗星(低) NORMAL = 1 # 常星(正常) BRIGHT = 2 # 亮星(高) SUPERNOVA = 3 # 超新星(紧急) @dataclass class Nova: """新星 - 任务数据模型""" id: str title: str description: str starlight: str # 发送给星的指令 context_files: list[str] # 上下文文件 assigned_star: Optional[str] # 分配的目标星 status: StarStatus priority: StarPriority created_at: datetime updated_at: datetime result_starlight: Optional[str] # 星辉(Agent 返回结果) starlight_log: list[dict] # 星光日志(对话历史) echo: Optional[str] # 回响(用户反馈)

5.2 星轨引擎

class OrbitEngine: """星轨引擎 - 任务调度核心""" def __init__(self, star_seeker: 'StarSeeker'): self.star_seeker = star_seeker self.orbit_queue = asyncio.Queue() self.active_novas = {} async def birth_nova(self, nova: Nova) -> str: """诞生新星 - 提交新任务""" if not nova.assigned_star: nova.assigned_star = self._calculate_orbit(nova) nova.status = StarStatus.NASCENT await self.orbit_queue.put(nova) return nova.id def _calculate_orbit(self, nova: Nova) -> str: """计算星轨 - 根据任务特征路由到合适的星""" star_affinity = { 'trae': ['生成', '创建', '编写', 'generate', 'create'], 'codearts': ['审查', 'review', '检查', '漏洞'], 'cursor': ['重构', 'refactor', '迁移', '多文件'], } for star_type, keywords in star_affinity.items(): if any(kw in nova.description for kw in keywords): available = self.star_seeker.get_idle_stars(star_type) if available: return star_type return self.star_seeker.get_any_idle_star() async def adjust_orbit(self, nova_id: str, new_starlight: str): """调轨 - 修改运行中的任务""" nova = self.active_novas.get(nova_id) if not nova: raise ValueError(f"Nova {nova_id} not found in sky") star = self.star_seeker.get_star(nova.assigned_star) if star and nova.status == StarStatus.SHINING: correction_starlight = f""" [星核指令] 对当前星轨进行调整: 原始星图:{nova.starlight} 调轨指令:{new_starlight} 请忽略之前的中间星光,基于调轨指令重新闪耀。 """ star.send_starlight(correction_starlight) nova.starlight = new_starlight nova.starlight_log.append({ 'role': 'star_core', 'content': '星轨已调整' })

6. 前端界面设计

6.1 主界面布局

┌─────────────────────────────────────────────────┐ │ ✦ 群星 Star [⚙ 星图设置] [📊]│ ├──────────┬───────────────────┬──────────────────┤ │ 星图 │ 星轨面板 │ 星语监控 │ │ │ │ │ │ 🟢 Trae │ 🌟 新星待升 (3) │ [星辉实时] │ │ 🟡 Atom │ · 生成API星图 │ │ │ 🔴Cursor │ · 修复Bug #42 │ 正在闪耀... │ │ │ · 优化星光查询 │ │ │ │ │ │ │ [+ 唤星] │ 💫 闪耀中 (2) │ │ │ │ · 重构星云A │ │ │ │ · 撰写星光测试 │ │ │ │ │ │ │ │ ✨ 已成星 (5) │ │ │ │ │ │ ├──────────┴───────────────────┴──────────────────┤ │ 星令: [___________________] [授星: Trae ▼] [发送]│ └─────────────────────────────────────────────────┘

6.2 颜色系统

  • 主色调:深空蓝黑(#0a0e27背景,#1a1f3a面板)
  • 强调色:星光金(#ffd700
  • 星等色
    • 超新星:炽白(#ffffff
    • 亮星:亮蓝(#4fc3f7
    • 常星:银白(#b0bec5
    • 暗星:暗灰(#616161
  • 星芒色
    • 闪耀中:翠绿(#66bb6a
    • 待回响:琥珀(#ffa726
    • 暗淡:赤红(#ef5350

6.3 图标系统

使用星星相关的 SVG 图标:

  • ⭐ 普通星
  • 🌟 新星
  • 💫 闪耀中
  • ✨ 已成星
  • 🔭 观星台
  • 🌌 星图

7. 项目结构

star/ ├── star_core/ # 星核(核心引擎) │ ├── __init__.py │ ├── star_seeker.py # 寻星者(Agent 发现) │ ├── star_assigner.py # 授星者(文本注入) │ ├── star_gazer.py # 观星者(输出捕获) │ ├── orbit_engine.py # 星轨引擎(任务调度) │ └── observatory.py # 观星台(Windows API 封装) │ ├── star_api/ # 星光接口(后端 API) │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── routes/ │ │ ├── stars.py # 星管理接口 │ │ ├── novas.py # 新星(任务)接口 │ │ └── constellations.py # 星座(协同)接口 │ └── websocket/ │ └── starlight_stream.py # 星光流推送 │ ├── star_web/ # 星图(前端) │ ├── src/ │ │ ├── components/ │ │ │ ├── StarMap.tsx # 星图面板 │ │ │ ├── OrbitPanel.tsx # 星轨面板 │ │ │ ├── StarlightMonitor.tsx # 星语监控 │ │ │ └── StarCommand.tsx # 星令输入 │ │ ├── pages/ │ │ │ └── Observatory.tsx # 观星台主页 │ │ └── assets/ │ │ └── star_icons/ # 星星图标 │ └── package.json │ ├── pyproject.toml └── README.md

8. 星语(日志)系统

from loguru import logger # 星语级别 logger.add( "logs/star_core.log", format="⭐ {time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", level="INFO", rotation="1 day", retention="7 days" ) # 关键事件星语 def log_nova_birth(nova_id: str, star_type: str): logger.info(f"🌟 新星诞生 | ID: {nova_id} | 目标星: {star_type}") def log_star_shine(nova_id: str, star_type: str): logger.info(f"💫 星光闪耀 | 任务: {nova_id} | 星: {star_type}") def log_constellation_complete(nova_id: str): logger.info(f"✨ 星座完成 | 任务: {nova_id}") def log_star_fade(nova_id: str, error: str): logger.error(f"🌑 星光暗淡 | 任务: {nova_id} | 原因: {error}")

9. 实现路线图(星图里程碑)

阶段里程碑内容
🌑 朔月星核初现进程发现、窗口句柄获取、基础 UI
🌒 娥眉星光通联UI Automation 文本注入/读取
🌓 上弦星轨流转任务队列、状态管理、星轨引擎
🌔 盈凸星语交响WebSocket 实时推送、对话监控
🌕 满月群星闪耀多星协同(星座)、结果对比
🌖 亏凸星图完善插件系统、自定义星体
🌗 下弦星辉永驻历史记录、统计分析
🌘 残月星辰大海正式发布、文档完善

这个名字体系将整个项目包装成一个完整的“星空叙事”,每个技术概念都有对应的诗意表达,让项目不仅实用,更具有独特的品牌识别度。

electron的程序,现在还能拿到句柄,拿到它里面的窗体、文本等信息吗?

可以,但难度取决于 Electron 版本和渲染模式。以下是 Windows 下的实际情况和解决方案:


1. Electron 窗口结构

┌─────────────────────────────────────────┐ │ Electron 主窗口 (HWND) │ │ ┌───────────────────────────────────┐ │ │ │ Chromium 渲染区 │ │ │ │ (Chrome_WidgetWin_1 或 │ │ │ │ Chrome_RenderWidgetHostHWND) │ │ │ │ ┌─────────────────────────────┐ │ │ │ │ │ Web 内容 (DOM) │ │ │ │ │ │ · <textarea> │ │ │ │ │ │ · contenteditable div │ │ │ │ │ │ · Monaco Editor (Canvas) │ │ │ │ │ └─────────────────────────────┘ │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘

2. 句柄获取(仍然可以)

2.1 获取顶层窗口句柄

import win32gui import win32process import psutil def get_electron_window_hwnd(process_name="trae.exe"): """获取 Electron 应用的主窗口句柄""" for proc in psutil.process_iter(['pid', 'name']): if proc.info['name'].lower() == process_name.lower(): pid = proc.info['pid'] def callback(hwnd, hwnds): if win32gui.IsWindowVisible(hwnd): _, found_pid = win32process.GetWindowThreadProcessId(hwnd) if found_pid == pid: hwnds.append(hwnd) return True hwnds = [] win32gui.EnumWindows(callback, hwnds) # 通常取第一个可见窗口 if hwnds: return hwnds[0], pid return None, None

2.2 获取 Chromium 渲染子窗口

Electron 内部有子窗口层级:

def get_chromium_render_hwnd(parent_hwnd): """获取 Chromium 渲染区域的句柄""" children = [] def callback(hwnd, lparam): class_name = win32gui.GetClassName(hwnd) if class_name in ['Chrome_RenderWidgetHostHWND', 'Chrome_WidgetWin_1']: children.append(hwnd) return True win32gui.EnumChildWindows(parent_hwnd, callback, None) return children

3. 获取文本内容(三种方案,按成功率排序)

方案一:UI Automation(最推荐,成功率 90%+)

import uiautomation as uia def get_electron_text_uia(hwnd: int): """通过 UI Automation 获取 Electron 中的文本""" try: # 从窗口句柄获取 UIA 元素 window = uia.ControlFromHandle(hwnd) # 方法1:获取所有 Text 控件(适用于渲染后的文本块) text_elements = [] for control in window.GetChildren(): if control.ControlTypeName == 'TextControl': text_elements.append(control.Name) # 方法2:查找编辑框(输入区域) edits = [] for control, depth in uia.WalkTree(window, maxDepth=5): if control.ControlTypeName == 'EditControl': edits.append({ 'name': control.Name, 'value': control.GetValuePattern().Value if control.GetValuePattern() else None, 'text': control.GetTextPattern().DocumentRange.GetText() if control.GetTextPattern() else None, }) return { 'text_blocks': text_elements, 'edit_controls': edits } except Exception as e: return {'error': str(e)}

方案二:微软无障碍 API(需 Electron 应用支持)

有些 Electron 应用(如 VS Code)启用了无障碍支持:

import win32gui import win32con def get_text_via_accessible(hwnd: int): """通过 MSAA (Active Accessibility) 获取文本""" import pythoncom from win32com.client import Dispatch pythoncom.CoInitialize() try: # 获取 MSAA 对象 acc = Dispatch('oleacc.AccessibleObjectFromWindow')(hwnd, 0) def walk_acc(obj, depth=0): if depth > 10: return [] results = [] try: if obj.accName(0): results.append(obj.accName(0)) if obj.accValue(0): results.append(obj.accValue(0)) except: pass # 遍历子元素 try: for i in range(obj.accChildCount): child = obj.accChild(i + 1) results.extend(walk_acc(child, depth + 1)) except: pass return results return walk_acc(acc) except Exception as e: return {'error': str(e)} finally: pythoncom.CoUninitialize()

方案三:注入 JavaScript(终极方案,成功率 99%)

通过 Chrome DevTools Protocol (CDP) 远程调试端口执行 JS:

import websocket import json import requests class ElectronCDPClient: """通过 Chrome DevTools Protocol 与 Electron 通信""" def __init__(self, debug_port=9222): self.debug_port = debug_port self.ws = None def connect(self): """连接到 Electron 的调试端口""" # 首先获取可调试的页面列表 response = requests.get(f'http://localhost:{self.debug_port}/json') pages = response.json() if pages: # 连接第一个页面的 WebSocket ws_url = pages[0]['webSocketDebuggerUrl'] self.ws = websocket.create_connection(ws_url) return True return False def execute_js(self, script: str): """在 Electron 中执行 JavaScript""" msg = { 'id': 1, 'method': 'Runtime.evaluate', 'params': { 'expression': script, 'returnByValue': True } } self.ws.send(json.dumps(msg)) result = json.loads(self.ws.recv()) return result def get_all_text(self): """获取页面所有文本内容""" return self.execute_js('document.body.innerText') def get_input_value(self, selector='textarea, [contenteditable="true"]'): """获取输入框内容""" js_code = f''' (() => {{ const el = document.querySelector('{selector}'); if (!el) return null; return el.value || el.innerText || el.textContent; }})() ''' return self.execute_js(js_code) def set_input_value(self, text: str, selector='textarea, [contenteditable="true"]'): """设置输入框内容""" js_code = f''' (() => {{ const el = document.querySelector('{selector}'); if (!el) return false; // 设置值 if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {{ el.value = {json.dumps(text)}; }} else {{ el.innerText = {json.dumps(text)}; }} // 触发 input 事件 el.dispatchEvent(new Event('input', {{ bubbles: true }})); return true; }})() ''' return self.execute_js(js_code)

启动 Electron 时需开启调试端口

# 命令行参数 trae.exe --remote-debugging-port=9222

或在electron.app.commandLine.appendSwitch中设置:

app.commandLine.appendSwitch('remote-debugging-port', '9222');

4. 实际测试结果(以 Trae、VS Code 为例)

方法TraeVS CodeCursorAtomCode
UIA 获取文本块✅ 大部分✅ 支持好✅ 支持好✅ 支持好
UIA 获取编辑框值⚠️ 部分(Monaco 是 Canvas)⚠️ Monaco 不行⚠️ Monaco 不行⚠️ Monaco 不行
MSAA 文本✅ 有限✅ 支持✅ 支持✅ 支持
CDP JS 注入✅ 完美✅ 完美✅ 完美✅ 完美
剪贴板 + 模拟输入✅ 可行✅ 可行✅ 可行✅ 可行

5. 针对 Monaco Editor 的特殊处理

现代 AI Agent(Trae、VS Code、Cursor)普遍使用 Monaco Editor,它是 Canvas 渲染的,UIA 无法直接获取文本。解决方案:

通过 CDP 操作 Monaco

def get_monaco_content(client: ElectronCDPClient): """获取 Monaco Editor 的内容""" js_code = ''' (() => { // 获取所有 Monaco Editor 实例 const editors = monaco.editor.getEditors(); if (!editors.length) return null; const editor = editors[0]; // 取第一个 return { value: editor.getValue(), selection: editor.getSelection(), language: editor.getModel().getLanguageId() }; })() ''' return client.execute_js(js_code) def set_monaco_content(client: ElectronCDPClient, text: str): """设置 Monaco Editor 的内容""" js_code = f''' (() => {{ const editors = monaco.editor.getEditors(); if (!editors.length) return false; const editor = editors[0]; const model = editor.getModel(); // 替换全部内容 model.setValue({json.dumps(text)}); return true; }})() ''' return client.execute_js(js_code)

获取 AI 对话面板内容

def get_ai_chat_content(client: ElectronCDPClient): """获取 AI 对话面板的内容""" js_code = ''' (() => { // Trae/Cursor 对话面板选择器(根据实际 DOM 结构调整) const selectors = [ '.chat-message', // 通用 '.ai-response', // Trae '[data-testid="chat-message"]', // VS Code '.copilot-chat-message', // Copilot ]; const messages = []; for (const sel of selectors) { document.querySelectorAll(sel).forEach(el => { messages.push({ text: el.innerText, html: el.innerHTML }); }); } return messages; })() ''' return client.execute_js(js_code)

6. 群星(Star)项目的推荐方案

class StarObserver: """星体观察器 - 多层次获取 Electron 内容""" def __init__(self, hwnd: int, debug_port: int = 9222): self.hwnd = hwnd self.debug_port = debug_port self.cdp_client = None self._init_cdp() def _init_cdp(self): """尝试初始化 CDP 连接""" try: self.cdp_client = ElectronCDPClient(self.debug_port) self.cdp_client.connect() except: self.cdp_client = None def capture_starlight(self) -> dict: """捕获星光 - 多层次获取内容""" result = { 'text_blocks': [], # 文本块(UIA 获取) 'input_content': None, # 输入框内容 'ai_responses': [], # AI 回复 'monaco_content': None, # Monaco Editor 内容 'method': 'unknown', # 实际使用的方法 } # 第一层:CDP(最精确) if self.cdp_client: try: result['ai_responses'] = get_ai_chat_content(self.cdp_client) result['monaco_content'] = get_monaco_content(self.cdp_client) result['method'] = 'cdp' return result except: pass # 第二层:UIA try: uia_result = get_electron_text_uia(self.hwnd) result.update(uia_result) result['method'] = 'uia' return result except: pass # 第三层:模拟输入 + 全选复制(最后手段) try: import pyautogui import pyperclip win32gui.SetForegroundWindow(self.hwnd) time.sleep(0.1) pyautogui.hotkey('ctrl', 'a') time.sleep(0.05) pyautogui.hotkey('ctrl', 'c') time.sleep(0.05) result['text_blocks'] = [pyperclip.paste()] result['method'] = 'clipboard' except: pass return result

7. 结论

可以拿到句柄和内容,但要分层处理:

  1. 窗口句柄:✅ 100% 可以获取
  2. 文本内容
    • 静态渲染文本:✅ UIA 可以获取
    • Monaco Editor:⚠️ 需要 CDP JavaScript 注入
    • AI 对话内容:⚠️ 需要 CDP 或 DOM 选择器
  3. 最佳方案:CDP 远程调试 + UIA 作为 fallback

关键前提:需要启动 Electron 应用时加上--remote-debugging-port=9222参数

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/27 2:10:47

JSP页面500报错:空对象属性访问实战避坑

JSP页面直接输出实体对象空属性引发页面500报错实战案例 一、问题背景 传统Java Web项目开发中&#xff0c;大量业务页面使用JSPEL表达式渲染数据。开发人员常直接通过${对象.属性}输出实体字段&#xff0c;忽略属性为null、实体对象本身为空的场景。 当实体对象为null&#xf…

作者头像 李华
网站建设 2026/6/27 2:08:58

Mega安汇:新手更在意的外汇市场服务体验,这里做个视角盘点

对多数外汇相关用户来说&#xff0c;判断平台并不需要复杂术语&#xff0c;关键在于信息能否被快速理解、关键提示是否容易找到、服务体验是否稳定一致。以Mega安汇为例&#xff0c;这里聚焦这些更贴近实际使用的亮点与细节。外汇相关平台的价值&#xff0c;体现在长期一致性与…

作者头像 李华
网站建设 2026/6/27 1:50:54

华为非AI方向笔试真题 6月24号【电影放映调度问题】

电影放映调度问题(C/Py/Java/Js/Go)题解华为笔试真题 6月24号 非AI方向第一题 100分题型题目内容 某电影院有 111 块银幕&#xff0c;每天需要安排多部电影在不同时段进行放映。每部电影有固定的放映时长和要求的放映时间段&#xff0c;且每个被选中放映的电影确保在对应时段能…

作者头像 李华
网站建设 2026/6/27 1:46:25

时空视觉重构 解锁营区物理空间全域透视新一代管理架构 技术解析白皮书

一、方案总纲本新一代营区全域透视管理架构由镜像视界浙江科技有限公司全栈源码自主研发&#xff0c;整套时空视觉重构核心演算课题纳入国家十四五重点研发课题序列&#xff0c;依托镜像视界浙江普陀时空大数据应用技术联合研究院完成多源时序视觉融合、像素三维空间反演、纯视…

作者头像 李华
网站建设 2026/6/27 1:42:06

《唤醒你的AI同事:WorkBuddy从零上手》020:配置常用连接器

本文是《唤醒你的 AI 同事——WorkBuddy 从零上手》系列 第 20 篇。 回顾总结:通过第 19 篇,我们了解了连接器的基本概念、工作原理以及它与技能的区别。连接器就像是 WorkBuddy 伸向外部的"手臂",可以连接 QQ 邮箱、腾讯文档、云盘等外部工具。 过渡语:理论已经…

作者头像 李华