如何在Python中获取Windows 11系统托盘信息?原代码失效求助
解决Windows 11系统托盘遍历的ctypes适配问题
核心问题原因
Windows 11重构了系统托盘的UI框架,原Windows 10代码依赖的ToolbarWindow32控件不再是顶层托盘容器,导致你获取到的控件无有效按钮(i_buttons=0)。
修复步骤
1. 定位正确的托盘控件层级
Windows 11的系统托盘嵌套在多层控件中,需逐层查找:
- 主窗口:
Shell_TrayWnd - 子窗口:
TrayNotifyWnd - 新增容器:
Windows.UI.Core.CoreWindow - 最终目标:该容器内的
ToolbarWindow32控件
2. 修正控件查找逻辑的代码
替换原代码中直接查找ToolbarWindow32的部分,改为逐层遍历:
import ctypes from ctypes import wintypes user32 = ctypes.WinDLL('user32', use_last_error=True) kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) # 定义Windows API所需类型与参数 HWND = wintypes.HWND LPCSTR = wintypes.LPCSTR UINT = wintypes.UINT WPARAM = wintypes.WPARAM LPARAM = wintypes.LPARAM LRESULT = wintypes.LPARAM user32.FindWindowA.restype = HWND user32.FindWindowA.argtypes = [LPCSTR, LPCSTR] user32.FindWindowExA.restype = HWND user32.FindWindowExA.argtypes = [HWND, HWND, LPCSTR, LPCSTR] user32.SendMessageA.restype = LRESULT user32.SendMessageA.argtypes = [HWND, UINT, WPARAM, LPARAM] user32.EnumChildWindows.restype = wintypes.BOOL user32.EnumChildWindows.argtypes = [HWND, wintypes.WNDPROC, LPARAM] user32.GetClassNameA.restype = wintypes.INT user32.GetClassNameA.argtypes = [HWND, wintypes.LPSTR, wintypes.INT] TB_BUTTONCOUNT = 0x0418 # 逐层定位托盘工具栏控件 def find_tray_toolbar(): # 查找系统托盘主窗口 shell_tray = user32.FindWindowA(b"Shell_TrayWnd", None) if not shell_tray: return None # 查找TrayNotifyWnd子窗口 tray_notify = user32.FindWindowExA(shell_tray, None, b"TrayNotifyWnd", None) if not tray_notify: return None # 枚举子窗口找到Win11新增的CoreWindow core_window = None def enum_core_window(hwnd, lparam): nonlocal core_window class_name = ctypes.create_string_buffer(256) user32.GetClassNameA(hwnd, class_name, 256) if class_name.value == b"Windows.UI.Core.CoreWindow": core_window = hwnd return False return True user32.EnumChildWindows(tray_notify, wintypes.WNDPROC(enum_core_window), 0) if not core_window: return None # 获取CoreWindow内的ToolbarWindow32 return user32.FindWindowExA(core_window, None, b"ToolbarWindow32", None) # 测试获取按钮数量 toolbar_hwnd = find_tray_toolbar() if toolbar_hwnd: i_buttons = user32.SendMessageA(toolbar_hwnd, TB_BUTTONCOUNT, 0, 0) print(f"找到托盘图标数量: {i_buttons}") else: print("无法定位系统托盘控件")
3. 补充进程信息获取逻辑
拿到有效工具栏句柄后,通过TB_GETBUTTON获取图标关联的窗口句柄,再推导进程信息:
# 定义TBUTTON结构体 class TBUTTON(ctypes.Structure): _fields_ = [ ("iBitmap", wintypes.INT), ("idCommand", wintypes.UINT), ("fsState", wintypes.BYTE), ("fsStyle", wintypes.BYTE), ("bReserved", wintypes.BYTE * 2), ("dwData", wintypes.DWORD), ("iString", wintypes.INT) ] TB_GETBUTTON = 0x0417 user32.GetWindowThreadProcessId.restype = wintypes.DWORD user32.GetWindowThreadProcessId.argtypes = [HWND, wintypes.LPDWORD] kernel32.OpenProcess.restype = wintypes.HANDLE kernel32.OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD] kernel32.GetModuleBaseNameA.restype = wintypes.DWORD kernel32.GetModuleBaseNameA.argtypes = [wintypes.HANDLE, wintypes.HMODULE, wintypes.LPSTR, wintypes.DWORD] kernel32.CloseHandle.restype = wintypes.BOOL kernel32.CloseHandle.argtypes = [wintypes.HANDLE] # 遍历每个托盘图标获取进程信息 if toolbar_hwnd and i_buttons > 0: for idx in range(i_buttons): btn = TBUTTON() user32.SendMessageA(toolbar_hwnd, TB_GETBUTTON, idx, ctypes.byref(btn)) # dwData字段对应图标关联的窗口句柄 hwnd = HWND(btn.dwData) if hwnd: pid = wintypes.DWORD() user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid)) # 打开进程获取名称 h_process = kernel32.OpenProcess(0x1F0FFF, False, pid.value) if h_process: exe_name = ctypes.create_string_buffer(256) kernel32.GetModuleBaseNameA(h_process, None, exe_name, 256) print(f"图标{idx}: 进程名={exe_name.value.decode('gbk')}, PID={pid.value}") kernel32.CloseHandle(h_process)
注意事项
- Windows 11的系统托盘结构可能随版本更新微调,需在不同子版本测试
- 部分系统级托盘图标需要管理员权限才能获取完整进程信息
- 若仍无法找到控件,可尝试枚举所有
ToolbarWindow32控件,结合屏幕位置判断是否为目标托盘
内容的提问来源于stack exchange,提问作者hite404
相关产品推荐
相关产品推荐

