119 lines
4.1 KiB
Python
119 lines
4.1 KiB
Python
"""
|
||
企业微信 UI 完整诊断工具 v2
|
||
同时使用 uiautomation + win32gui 两套方式诊断窗口结构
|
||
运行方法:python inspect_tree.py
|
||
"""
|
||
import uiautomation as auto
|
||
import win32gui
|
||
import win32con
|
||
import win32api
|
||
|
||
WX_WINDOW_CLASS = 'WeWorkWindow'
|
||
|
||
|
||
def find_wx_window():
|
||
root = auto.GetRootControl()
|
||
for win in root.GetChildren():
|
||
if WX_WINDOW_CLASS in win.ClassName:
|
||
return win
|
||
return None
|
||
|
||
|
||
def print_uia_tree(ctrl, prefix="", depth=0, max_depth=3):
|
||
"""打印 UIA 控件树"""
|
||
try:
|
||
ct = ctrl.ControlTypeName
|
||
name = repr(ctrl.Name[:40]) if ctrl.Name else "''"
|
||
cls = ctrl.ClassName
|
||
aid = ctrl.AutomationId
|
||
bound = ctrl.BoundingRectangle
|
||
print(f"{prefix}[{ct}] Name={name} ClassName='{cls}' AutoId='{aid}' Rect=({bound.left},{bound.top},{bound.right},{bound.bottom})")
|
||
except Exception as e:
|
||
print(f"{prefix}[ERROR] {e}")
|
||
return
|
||
|
||
if depth >= max_depth:
|
||
return
|
||
try:
|
||
children = ctrl.GetChildren()
|
||
for child in children:
|
||
print_uia_tree(child, prefix + " ", depth + 1, max_depth)
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def enum_all_child_hwnds(parent_hwnd):
|
||
"""用 win32gui 递归枚举所有子窗口句柄"""
|
||
results = []
|
||
|
||
def callback(hwnd, _):
|
||
try:
|
||
class_name = win32gui.GetClassName(hwnd)
|
||
title = win32gui.GetWindowText(hwnd)
|
||
rect = win32gui.GetWindowRect(hwnd)
|
||
is_visible = win32gui.IsWindowVisible(hwnd)
|
||
results.append({
|
||
'hwnd': hwnd,
|
||
'class': class_name,
|
||
'title': title,
|
||
'rect': rect,
|
||
'visible': is_visible
|
||
})
|
||
except Exception:
|
||
pass
|
||
return True
|
||
|
||
win32gui.EnumChildWindows(parent_hwnd, callback, None)
|
||
return results
|
||
|
||
|
||
if __name__ == '__main__':
|
||
print("=" * 60)
|
||
print(" 企业微信 UI 完整诊断工具")
|
||
print("=" * 60)
|
||
|
||
# ── 1. 找主窗口 ──────────────────────────────────────────────
|
||
print("\n[1] 查找企业微信主窗口...")
|
||
wx = find_wx_window()
|
||
if not wx:
|
||
print(" 未找到!请确认企业微信主窗口已在桌面上显示。")
|
||
exit(1)
|
||
|
||
hwnd = wx.NativeWindowHandle
|
||
rect = wx.BoundingRectangle
|
||
print(f" ClassName : {wx.ClassName}")
|
||
print(f" Title : {wx.Name}")
|
||
print(f" HWND : {hwnd} (0x{hwnd:08X})")
|
||
print(f" 屏幕位置 : ({rect.left}, {rect.top}) - ({rect.right}, {rect.bottom})")
|
||
print(f" 窗口尺寸 : {rect.right - rect.left} x {rect.bottom - rect.top}")
|
||
|
||
# ── 2. UIA 控件树(2层快照)────────────────────────────────
|
||
print("\n[2] UIA 控件树(深度=2):")
|
||
print_uia_tree(wx, " ", max_depth=2)
|
||
|
||
# ── 3. Win32 子窗口枚举 ──────────────────────────────────────
|
||
print("\n[3] Win32 子窗口句柄枚举(仅显示可见窗口):")
|
||
children = enum_all_child_hwnds(hwnd)
|
||
visible = [c for c in children if c['visible']]
|
||
print(f" 共找到 {len(children)} 个子窗口句柄,其中 {len(visible)} 个可见。")
|
||
print()
|
||
|
||
# 按 ClassName 分组统计
|
||
class_count = {}
|
||
for c in children:
|
||
class_count[c['class']] = class_count.get(c['class'], 0) + 1
|
||
print(" ClassName 分布统计:")
|
||
for cls, cnt in sorted(class_count.items(), key=lambda x: -x[1]):
|
||
print(f" {cnt:3d}x {cls}")
|
||
|
||
print()
|
||
print(" 可见子窗口详细列表(前30个):")
|
||
for i, c in enumerate(visible[:30]):
|
||
title_info = f" Title='{c['title']}'" if c['title'] else ""
|
||
print(f" [{i:02d}] HWND=0x{c['hwnd']:08X} Class='{c['class']}'{title_info} Rect={c['rect']}")
|
||
|
||
print()
|
||
print("=" * 60)
|
||
print("[提示] 请将以上输出完整截图/复制,发给 AI 进行分析。")
|
||
print("=" * 60)
|