与系统托盘图标对应的Windows可执行文件?

2 投票
2 回答
1525 浏览
提问于 2025-04-17 02:29

有没有办法用Python来获取系统托盘(右下角)中所有图标的列表,以及它们对应的进程和可执行文件?

这里有一个关于如何使用AHK来实现这个功能的讨论:

http://www.autohotkey.com/forum/topic17314.html

不过这个方法主要依赖于AHK。

需要注意的是,我对这个功能是在Windows XP/7系统上感兴趣。

谢谢!

2 个回答

1

看起来这个问题更依赖于Windows的API,而不是AHK(AutoHotkey)。AHK可能只是一个包装工具,因为你提到的网站上的参数看起来有点奇怪,比如hWnd、101、0、0、0、0,这些通常是Windows API中的结构类型的东西。考虑到这是一个比较特殊的目标,而且非常针对Windows,我很怀疑你能随便用什么东西就能搞定。如果你想自己实现,Tim Golden的网站可能会给你很多帮助。如果有人之前做过这件事,我想他就是其中之一:

http://timgolden.me.uk/python/wmi/index.html

Mark Hammond和Python的核心Windows模块也是不错的搜索关键词,但奇怪的是,关于这些内容的直接文档还是相对较少,我想2000年的书现在可能也帮不上太多忙。

另外,你可以调用AHK能做的任何操作,但这需要你去阅读MSDN(微软开发者网络)。如果你大致知道你想从AHK脚本中得到什么,那你的工作量会减少很多。但这依然会让人感到烦恼。

如果你想看看如何实现Windows API调用的一些例子,Grayhat Python的前几章可能会对你有帮助(比如创建结构、调用函数等)。我发现这些比直接来自Python-Windows小组的材料要有用得多,那些材料对我来说确实有点难。

祝你好运!

2

这段内容涉及到比较复杂的win32编程。最开始我用的是pywin32这个库,但后来我换成了ctypes,因为这样更方便处理一些数据结构。需要注意的是,我是在32位的Windows XP系统上写的这个代码。如果你在64位系统上使用,就需要修改TBBUTTON的定义,具体可以参考这个链接。我不太确定用户账户控制(UAC)会对这个有什么影响(比如,你能否在另一个进程中分配内存?)。

import commctrl, win32con
from ctypes import *

# represent the TBBUTTON structure
# note this is 32 bit, 64 bit padds 4 more reserved bytes
class TBBUTTON(Structure):
    _pack_ = 1
    _fields_ = [
        ('iBitmap', c_int),
        ('idCommand', c_int),
        ('fsState', c_ubyte),
        ('fsStyle', c_ubyte),
        ('bReserved', c_ubyte * 2),
        ('dwData', c_ulong),
        ('iString', c_int),
    ]

# get the handle to the sytem tray
hWnd = windll.user32.FindWindowA("Shell_TrayWnd", None)
hWnd = windll.user32.FindWindowExA(hWnd, None, "TrayNotifyWnd", None)
hWnd = windll.user32.FindWindowExA(hWnd, None, "SysPager", None)
hWnd = windll.user32.FindWindowExA(hWnd, None, "ToolbarWindow32", None)

# get the count of icons in the tray
numIcons = windll.user32.SendMessageA(hWnd, commctrl.TB_BUTTONCOUNT, 0, 0)

# allocate memory within the system tray
pid = c_ulong();
windll.user32.GetWindowThreadProcessId(hWnd, byref(pid))
hProcess = windll.kernel32.OpenProcess(win32con.PROCESS_ALL_ACCESS, 0, pid)
lpPointer = windll.kernel32.VirtualAllocEx(hProcess, 0, sizeof(TBBUTTON), win32con.MEM_COMMIT, win32con.PAGE_READWRITE)

# init our tool bar button and a handle to it
tbButton = TBBUTTON()
butHandle = c_int()

for i in range(numIcons):
    # query the button into the memory we allocated
    windll.user32.SendMessageA(hWnd, commctrl.TB_GETBUTTON, i, lpPointer)
    # read the memory into our button struct
    windll.kernel32.ReadProcessMemory(hProcess, lpPointer, addressof(tbButton), 20, None)
    # read the 1st 4 bytes from the dwData into the butHandle var
    # these first 4 bytes contain the handle to the button
    windll.kernel32.ReadProcessMemory(hProcess, tbButton.dwData, addressof(butHandle), 4, None)

    # get the pid that created the button
    butPid = c_ulong()
    windll.user32.GetWindowThreadProcessId(butHandle, byref(butPid))

    # i leave it to you to get the process from the pid
    # that should be trivial...
    print butPid

撰写回答