- 删除旧 _collect_all_texts(被 _collect_filtered_texts 替代) - 删除 _read_message_list_deep(旧QQ路径,已废弃) - 微信fallback改走 _collect_filtered_texts - 添加 _click_message_area:剪贴板前先点击中右区域聚焦消息区 - TIM Electron 通过鼠标点击消息区再Ctrl+A→Ctrl+C - 添加 win32api import(用于 SetCursorPos+mouse_event)
207 lines
6.2 KiB
Python
207 lines
6.2 KiB
Python
"""诊断:dump TIM 窗口控件树,找消息区路径"""
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
import uiautomation as auto
|
|
import win32gui
|
|
import logging
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
log = logging.getLogger("debug")
|
|
|
|
OUTPUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "tim_dump.txt")
|
|
|
|
|
|
def find_tim_window():
|
|
"""找 TIM 主窗口(TXGuiFoundation 类名)"""
|
|
root = auto.GetRootControl()
|
|
for child in root.GetChildren():
|
|
try:
|
|
cn = child.ClassName or ""
|
|
name = child.Name or ""
|
|
rect = child.BoundingRectangle
|
|
if cn == "TXGuiFoundation" and rect.width() > 200 and rect.height() > 100:
|
|
log.info(f"找到 TIM: Name={name} ClassName={cn} rect=({rect.left},{rect.top},{rect.right},{rect.bottom})")
|
|
return child
|
|
except:
|
|
pass
|
|
# 也试试 "TIM" 名称
|
|
try:
|
|
w = auto.WindowControl(Name="TIM")
|
|
if w.Exists(maxSearchSeconds=0.5):
|
|
log.info(f"找到 TIM(ByName): {w.Name} ClassName={w.ClassName}")
|
|
return w
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
|
|
def dump_tree(ctrl, depth=0, max_depth=5, file=None):
|
|
"""递归 dump 控件树"""
|
|
if depth > max_depth:
|
|
return
|
|
indent = " " * depth
|
|
try:
|
|
cn = ctrl.ControlTypeName or "?"
|
|
name = (ctrl.Name or "")[:80]
|
|
cls = ctrl.ClassName or ""
|
|
rect = ctrl.BoundingRectangle
|
|
has_scroll = False
|
|
try:
|
|
sp = ctrl.GetScrollPattern()
|
|
has_scroll = sp is not None
|
|
except:
|
|
pass
|
|
line = f"{indent}{cn} Class={cls} Name=\"{name}\" rect=({rect.left},{rect.top},{rect.right},{rect.bottom}) scroll={has_scroll}"
|
|
if file:
|
|
file.write(line + "\n")
|
|
else:
|
|
print(line)
|
|
except Exception as e:
|
|
line = f"{indent}[error] {e}"
|
|
if file:
|
|
file.write(line + "\n")
|
|
else:
|
|
print(line)
|
|
return
|
|
|
|
try:
|
|
for child in ctrl.GetChildren():
|
|
dump_tree(child, depth + 1, max_depth, file)
|
|
except:
|
|
pass
|
|
|
|
|
|
def dump_flat_texts(window, max_depth=6):
|
|
"""模拟 _collect_all_texts 的输出"""
|
|
texts = []
|
|
def walk(ctrl, depth):
|
|
if depth > max_depth:
|
|
return
|
|
try:
|
|
cr = ctrl.BoundingRectangle
|
|
if cr.width() < 20 or cr.height() < 20:
|
|
return
|
|
except:
|
|
pass
|
|
try:
|
|
name = ctrl.Name
|
|
if name and name.strip():
|
|
texts.append((depth, name.strip()[:100]))
|
|
except:
|
|
pass
|
|
try:
|
|
for child in ctrl.GetChildren():
|
|
walk(child, depth + 1)
|
|
except:
|
|
pass
|
|
try:
|
|
for child in ctrl.GetChildren():
|
|
walk(child, 1)
|
|
except:
|
|
pass
|
|
return texts
|
|
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("TIM 控件树诊断")
|
|
print("=" * 60)
|
|
|
|
win = find_tim_window()
|
|
if not win:
|
|
print("未找到 TIM 窗口")
|
|
return
|
|
|
|
hwnd = win.NativeWindowHandle
|
|
print(f"\nHWND: {hwnd}")
|
|
print(f"ClassName: {win.ClassName}")
|
|
print(f"Name: {win.Name}")
|
|
print()
|
|
|
|
# 1. 深度 4 级控件树
|
|
print("-" * 60)
|
|
print("控件树 (depth<=4):")
|
|
print("-" * 60)
|
|
with open(OUTPUT, "w", encoding="utf-8") as f:
|
|
f.write("TIM 控件树诊断\n")
|
|
f.write(f"HWND={hwnd} ClassName={win.ClassName} Name={win.Name}\n\n")
|
|
f.write("--- 控件树 (depth<=4) ---\n")
|
|
dump_tree(win, 0, 4, f)
|
|
|
|
# 2. 当前 _collect_all_texts 抓到的全部文本
|
|
print("\n" + "-" * 60)
|
|
print("全窗深度遍历文本 (depth<=6):")
|
|
print("-" * 60)
|
|
all_texts = dump_flat_texts(win, 6)
|
|
with open(OUTPUT, "a", encoding="utf-8") as f:
|
|
f.write("\n--- 全窗文本 (depth<=6) ---\n")
|
|
for d, t in all_texts:
|
|
f.write(f" d={d} \"{t}\"\n")
|
|
for d, t in all_texts:
|
|
print(f" d={d} \"{t}\"")
|
|
|
|
# 3. 找 ListControl
|
|
print("\n" + "-" * 60)
|
|
print("ListControl 列表:")
|
|
print("-" * 60)
|
|
def find_list(ctrl, depth=0, max_d=8):
|
|
if depth > max_d:
|
|
return
|
|
try:
|
|
if ctrl.ControlTypeName == "ListControl":
|
|
name = (ctrl.Name or "")[:60]
|
|
rect = ctrl.BoundingRectangle
|
|
print(f" depth={depth} Name=\"{name}\" rect=({rect.left},{rect.top},{rect.right},{rect.bottom})")
|
|
# dump items
|
|
for item in ctrl.GetChildren():
|
|
try:
|
|
iname = (item.Name or "")[:80]
|
|
irec = item.BoundingRectangle
|
|
print(f" item: \"{iname}\" rect=({irec.left},{irec.top},{irec.right},{irec.bottom})")
|
|
except:
|
|
pass
|
|
except:
|
|
pass
|
|
try:
|
|
for child in ctrl.GetChildren():
|
|
find_list(child, depth + 1, max_d)
|
|
except:
|
|
pass
|
|
with open(OUTPUT, "a", encoding="utf-8") as f:
|
|
f.write("\n--- ListControl ---\n")
|
|
find_list(win)
|
|
|
|
# 4. 找所有 Text 或 Edit 控件(搜索框特征)
|
|
print("\n" + "-" * 60)
|
|
print("EditControl / 搜索框:")
|
|
print("-" * 60)
|
|
def find_edit(ctrl, depth=0, max_d=8):
|
|
if depth > max_d:
|
|
return
|
|
try:
|
|
ct = ctrl.ControlTypeName or ""
|
|
if "Edit" in ct or "Text" in ct:
|
|
name = (ctrl.Name or "")[:60]
|
|
cls = ctrl.ClassName or ""
|
|
rect = ctrl.BoundingRectangle
|
|
print(f" depth={depth} type={ct} class={cls} Name=\"{name}\" rect=({rect.left},{rect.top},{rect.right},{rect.bottom})")
|
|
with open(OUTPUT, "a", encoding="utf-8") as f:
|
|
f.write(f" depth={depth} type={ct} class={cls} Name=\"{name}\"\n")
|
|
except:
|
|
pass
|
|
try:
|
|
for child in ctrl.GetChildren():
|
|
find_edit(child, depth + 1, max_d)
|
|
except:
|
|
pass
|
|
find_edit(win, 0, 8)
|
|
|
|
print(f"\n完整诊断已保存到: {OUTPUT}")
|
|
print("请把该文件内容发给我分析")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|