You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:22:41