修QQ:raw_lines作用域bug+UIA优先生成窗口+剪贴板行数检测

This commit is contained in:
youpaishidifu
2026-07-25 21:39:23 +08:00
parent 3da88da267
commit 85b790cb81
+67 -37
View File
@@ -204,6 +204,33 @@ class MessageMonitor:
pass
return None
def _find_qq_window_fresh(self):
"""通过类名重新查找 QQ/TIM 窗口,绕过 UIA 缓存(比 ControlFromHandle 更彻底)。"""
try:
# 先按窗口名称查找
for name in ["QQ", "TIM"]:
try:
w = auto.WindowControl(Name=name)
if w.Exists(maxSearchSeconds=0.2):
rect = w.BoundingRectangle
if rect.width() > 200 and rect.height() > 100:
return w
except Exception:
pass
# 按 TXGuiFoundation 类名深度查找
root = auto.GetRootControl()
for child in root.GetChildren():
try:
if child.ClassName == "TXGuiFoundation":
rect = child.BoundingRectangle
if rect.width() > 200 and rect.height() > 100:
return child
except Exception:
pass
except Exception:
pass
return None
def _force_uia_refresh(self, platform: str):
"""通过交互强制窗口更新 UIA 控件树。"""
hwnd = self._cached_hwnd.get(platform)
@@ -401,6 +428,7 @@ class MessageMonitor:
pass
texts = []
raw_lines = [] # 预初始化,防止 content 为空时 UnboundLocalError
try:
# 确保窗口在前台(失败则返回空,避免发送到错误窗口)
# QQ 剪贴板优先路径需要更可靠的前台保证(与发送路径 retries 一致)
@@ -793,47 +821,49 @@ class MessageMonitor:
def _read_messages(self, window, platform: str = "") -> list:
"""每轮重新定位 ListControl。"""
if platform == "qq":
# QQ NT UIA ListControl 缓存永不更新新消息(返回几天前的旧数据)
# 必须剪贴板优先(键盘导航 Enter+Ctrl+End+Ctrl+A/C 读实时消息)
# QQ NT: UIA 优先(但重建窗口绕过缓存)+ 剪贴板兜底
# 第一步:用新窗口流式重建 UIA 树(不用 ControlFromHandle 缓存)
fresh_win = self._find_qq_window_fresh()
if fresh_win:
texts = self._read_message_list(fresh_win)
if texts:
return texts
texts = self._read_conversation_list(fresh_win)
if texts:
return texts
try:
rect = fresh_win.BoundingRectangle
boundary = rect.left + rect.width() * 0.40
for lst in self._list_controls(fresh_win):
try:
cr = lst.BoundingRectangle
if cr.left >= boundary:
texts = self._extract_last_line(lst)
if texts:
logger.debug(f"[S-C] ListControl(x={cr.left}) → {len(texts)} items")
return texts
except Exception:
pass
except Exception:
pass
try:
texts = self._collect_filtered_texts(fresh_win, "qq", max_depth=5)
if texts:
logger.debug(f"[S-D] 全窗文本 → {len(texts)} items")
return texts
except Exception:
pass
# 剪贴板兜底:仅当 UIA 完全没读到内容时启用
texts = self._read_via_clipboard_once(platform)
if texts:
# <=5 行的剪贴板很可能是输入框内容(非消息列表)→ 丢弃
if texts and len(texts) > 5:
return texts
texts = self._read_via_clipboard(platform)
if texts:
if texts and len(texts) > 5:
return texts
# UIA 兜底:先强制刷新生效,再读取
self._force_uia_refresh(platform)
self._force_scroll_bottom(platform)
time.sleep(0.5)
texts = self._read_message_list(window)
if texts:
return texts
texts = self._read_conversation_list(window)
if texts:
return texts
try:
rect = window.BoundingRectangle
boundary = rect.left + rect.width() * 0.40
for lst in self._list_controls(window):
try:
cr = lst.BoundingRectangle
if cr.left >= boundary:
texts = self._extract_last_line(lst)
if texts:
logger.debug(f"[S-C] ListControl(x={cr.left}) → {len(texts)} items")
return texts
except Exception:
pass
except Exception:
pass
try:
texts = self._collect_filtered_texts(window, "qq", max_depth=5)
if texts:
logger.debug(f"[S-D] 全窗文本 → {len(texts)} items")
return texts
except Exception:
pass
# 最终兜底:win32 文本
# 最终兜底
texts = self._read_win32_texts(platform)
if texts:
return texts