Различную информацию об иконках системного трея можно получить, отправляя им сообщение TB_GETBUTTON, и считывая данные из структуры TBBUTTON. (Чтобы считать данные из удалённого процесса (explorer.exe), нужно выделить буфер в его адресном пространстве, считать данные в него, а затем скопировать их в буфер из адресного пространства вызывающего процесса.) В составе этой структуры есть член dwData, описанный в MSDN как "application-defined value". Порыскав по просторам интернета, я, наконец, нашёл (pdf) его более подробное описание:
The dwData member of the TBBUTTON structure gives us a pointer to the TRAYDATA structure for the button.
public struct TRAYDATA
{
public IntPtr hwnd;
public uint uID;
public uint uCallbackMessage;
private uint Reserved;
private uint Reserved2;
public IntPtr hIcon;
}
Возвращаясь к поставленной задаче — удалить "зависшие" иконки убитых приложений и обновить трей — можно получить хэндл родительского окна иконки (первый член структуры TRAYDATA) и проверить, существует ли его процесс (вариант с проверкой существования окна почему-то не срабатывает), затем удалить ненужную иконку, отправив ей TB_DELETEBUTTON. После этого нужно обновить трей с помощью WM_SETTINGCHANGE, иначе в области уведомлений останется пустое место.
#NoTrayIcon
SetBatchLines, -1
PROCESS_VM_OPERATION := (0x8)
PROCESS_VM_READ := (0x10)
PROCESS_QUERY_INFORMATION := (0x400)
SizeOfTBBUTTON := 20, SizeOfTRAYDATA := 24
MEM_COMMIT := 0x1000
MEM_RELEASE := 0x8000
PAGE_READWRITE := 0x4
WM_USER := 0x400
TB_BUTTONCOUNT := (WM_USER + 24)
TB_GETBUTTON := (WM_USER + 23)
WM_WININICHANGE := 0x1A
TB_DELETEBUTTON := (WM_USER + 22)
WM_SETTINGCHANGE := WM_WININICHANGE
; нам нужно найти хэндл окна класса ToolbarWindow32, которое является дочерним окна
; с классом SysPager, которое является дочерним окна с классом TrayNotifyWnd, которое,
; в свою очередь, является дочерним окна с классом Shell_TrayWnd (панели задач)
hShell_TrayWnd := WinExist("ahk_class Shell_TrayWnd")
ChildClasses = TrayNotifyWnd|SysPager|ToolbarWindow32 ; классы окон в порядке вложенности
Loop, parse, ChildClasses, |
hChild := DllCall("FindWindowEx", UInt, A_Index = 1 ? hShell_TrayWnd : hChild
, UInt, 0, Str, A_LoopField, UInt, 0)
; последнее вложенное — искомое окно — ToolbarWindow32 делаем последним найденным
WinWait, ahk_id %hChild%
WinGet, PID, PID
hProcess := DllCall("OpenProcess", UInt, PROCESS_VM_OPERATION|PROCESS_VM_READ, UInt, 0, UInt, PID)
lpData := DllCall("VirtualAllocEx", UInt, hProcess, UInt, 0
, UInt, SizeOfTBBUTTON
, UInt, MEM_COMMIT
, UInt, PAGE_READWRITE)
DetectHiddenWindows, On
VarSetCapacity(TBBUTTON, SizeOfTBBUTTON)
VarSetCapacity(TRAYDATA, SizeOfTRAYDATA)
VarSetCapacity(FilePath, A_IsUnicode ? 600 : 300)
Index = 0
SendMessage, TB_BUTTONCOUNT
Loop % IconCount := ErrorLevel
{
SendMessage, TB_GETBUTTON, Index, lpData
DllCall("ReadProcessMemory", UInt, hProcess, UInt, lpData
, UInt, &TBBUTTON
, UInt, SizeOfTBBUTTON, UInt, 0)
DllCall("ReadProcessMemory", UInt, hProcess, UInt, NumGet(TBBUTTON, 12)
, UInt, &TRAYDATA
, UInt, SizeOfTRAYDATA, UInt, 0)
WinGet, PID, PID, % "ahk_id " NumGet(TRAYDATA)
if !PID
SendMessage, TB_DELETEBUTTON, Index-- ; в случае удаления индексы уменьшаются на 1
Index++
}
if (Index < IconCount)
SendMessage, WM_SETTINGCHANGE,,,, ahk_class Shell_TrayWnd ; иначе останется пустое место
DllCall("VirtualFreeEx", UInt, hProcess , UInt, lpData, UInt, 0, UInt, MEM_RELEASE)
DllCall("CloseHandle", UInt, hProcess)
(Чёрт, подсветку доделать никак руки не доходят!)
И напоследок, код определяющий исполнимые файлы процессов, создавших иконки в трее (нужно учитывать, что часть иконок скрыта).
#NoTrayIcon
SetBatchLines, -1
PROCESS_VM_OPERATION := (0x8)
PROCESS_VM_READ := (0x10)
PROCESS_QUERY_INFORMATION := (0x400)
SizeOfTBBUTTON := 20, SizeOfTRAYDATA := 24
MEM_COMMIT := 0x1000
MEM_RELEASE := 0x8000
PAGE_READWRITE := 0x4
WM_USER := 0x400
TB_BUTTONCOUNT := (WM_USER + 24)
TB_GETBUTTON := (WM_USER + 23)
WM_WININICHANGE := 0x1A
TB_DELETEBUTTON := (WM_USER + 22)
WM_SETTINGCHANGE := WM_WININICHANGE
hShell_TrayWnd := WinExist("ahk_class Shell_TrayWnd")
ChildClasses = TrayNotifyWnd|SysPager|ToolbarWindow32 ; классы окон в порядке вложенности
Loop, parse, ChildClasses, |
hChild := DllCall("FindWindowEx", UInt, A_Index = 1 ? hShell_TrayWnd : hChild
, UInt, 0, Str, A_LoopField, UInt, 0)
; последнее вложенное — искомое окно — ToolbarWindow32 делаем последним найденным
WinWait, ahk_id %hChild%
WinGet, PID, PID
hProcess := DllCall("OpenProcess", UInt, PROCESS_VM_OPERATION|PROCESS_VM_READ, UInt, 0, UInt, PID)
lpData := DllCall("VirtualAllocEx", UInt, hProcess, UInt, 0
, UInt, SizeOfTBBUTTON
, UInt, MEM_COMMIT
, UInt, PAGE_READWRITE)
DetectHiddenWindows, On
VarSetCapacity(TBBUTTON, SizeOfTBBUTTON)
VarSetCapacity(TRAYDATA, SizeOfTRAYDATA)
VarSetCapacity(FilePath, A_IsUnicode ? 600 : 300)
Index := 0
SendMessage, TB_BUTTONCOUNT
Loop % IconCount := ErrorLevel
{
SendMessage, TB_GETBUTTON, Index, lpData
DllCall("ReadProcessMemory", UInt, hProcess, UInt, lpData
, UInt, &TBBUTTON
, UInt, SizeOfTBBUTTON, UInt, 0)
DllCall("ReadProcessMemory", UInt, hProcess, UInt, NumGet(TBBUTTON, 12)
, UInt, &TRAYDATA
, UInt, SizeOfTRAYDATA, UInt, 0)
WinGet, PID, PID, % "ahk_id " NumGet(TRAYDATA)
if !PID
SendMessage, TB_DELETEBUTTON, Index-- ; в случае удаления индексы уменьшаются на 1
Else
{
hProc := DllCall("OpenProcess", UInt, PROCESS_QUERY_INFORMATION, UInt, 0, UInt, PID)
if !DllCall("GetProcessImageFileName" . (A_IsUnicode ? "W" : "A")
, UInt, hProc
, Str, FilePath, UInt, 300)
DllCall("Psapi\GetProcessImageFileName" . (A_IsUnicode ? "W" : "A")
, UInt, hProc
, Str, FilePath, UInt, 300)
FilePaths .= FilePath . "`n"
DllCall("CloseHandle", UInt, hProc)
}
Index++
}
if (Index < IconCount)
SendMessage, WM_SETTINGCHANGE,,,, ahk_class Shell_TrayWnd
DllCall("VirtualFreeEx", UInt, hProcess , UInt, lpData, UInt, 0, UInt, MEM_RELEASE)
DllCall("CloseHandle", UInt, hProcess)
MsgBox, % SubStr(FilePaths, 1, -1)
Оба варианта кода рассчитаны на 32-разрядную версию Windows.
Разработка AHK-скриптов:
e-mail
dfiveg@mail.ruTelegram
jollycoder