Более интересный пример. Создаём своё меню для иконок трея и NotifyIconOverflowWindow. Меню показывается по Ctrl + RButton над иконкой.
#NoEnv
SetBatchLines, -1
Return
Esc:: ExitApp
#If oResult := IsMouseOverTray()
^RButton Up::ShowMyMenu(oResult), oResult := ""
ShowMyMenu(oMousePosInfo)
{
oIconInfo := GetTrayIconInfoFromPos(oMousePosInfo)
Icon := Func("GetIcon").Bind(oIconInfo)
Show := Func("ShowInfo").Bind(oIconInfo)
Open := Func("OpenFolder").Bind(oIconInfo)
Close := Func("CloseProcess").Bind(oIconInfo)
Menu, MyMenu, UseErrorLevel
Menu, MyMenu, DeleteAll
Menu, MyMenu, Add, Завершить процесс, % Close
Menu, MyMenu, Add, Показать иконку в своём окне, % Icon
Menu, MyMenu, Add, Показать информацию, % Show
Menu, MyMenu, Add, Открыть папку, % Open
Menu, MyMenu, Show
}
GetTrayIconInfoFromPos(oMousePosInfo)
{
static WM_USER := 0x400, TB_BUTTONCOUNT := WM_USER + 24, TB_GETBUTTON := WM_USER + 23, TB_GETITEMRECT := WM_USER + 29
, PtrSize := A_Is64bitOS ? 8 : 4, SizeOfTBBUTTON := 8 + PtrSize*3, SizeOfTRAYDATA := 16 + PtrSize*2, SizeOfiString := 256
WinExist("ahk_id" oMousePosInfo.hToolbar)
WinGet, PID, PID
if !IsObject(RemoteBuff := New RemoteBuffer(PID, SizeOfTBBUTTON))
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось создать удалённый буфер`nОшибка " A_LastError, Str, "", UInt, 0)
WinGetPos, xTB, yTB
POINT := (oMousePosInfo.X - xTB) | (oMousePosInfo.Y - yTB) << 32
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel {
SendMessage, TB_GETITEMRECT, A_Index - 1, RemoteBuff.ptr
RemoteBuff.Read(RECT, 16)
if !DllCall("PtInRect", Ptr, &RECT, Int64, POINT)
continue
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
RemoteBuff.Read(TBBUTTON, SizeOfTBBUTTON)
TraydataOffset := NumGet(&TBBUTTON + 8 + PtrSize) - RemoteBuff.ptr
TooltipOffset := NumGet(&TBBUTTON + 8 + PtrSize*2) - RemoteBuff.ptr
RemoteBuff.Read(TRAYDATA, SizeOfTRAYDATA, TraydataOffset)
RemoteBuff.Read(String, SizeOfiString, TooltipOffset)
Tooltip := StrGet(&String, "UTF-16")
hWnd := NumGet(TRAYDATA, PtrSize = 4 ? "UInt" : "UInt64")
uID := NumGet(TRAYDATA, PtrSize, "UInt")
nMsg := NumGet(TRAYDATA, PtrSize + 4, "UInt")
hIcon := NumGet(TRAYDATA, PtrSize + 16, PtrSize = 4 ? "UInt" : "UInt64")
DHW_Prev := A_DetectHiddenWindows
DetectHiddenWindows, On
WinExist("ahk_id" hWnd)
WinGet, Name, ProcessName
WinGet, ProcessPath, ProcessPath
WinGet, PID, PID
DetectHiddenWindows, %DHW_Prev%
Return { ProcessName: Name, PID: PID, ProcessPath: ProcessPath, hWnd: Format("{:#x}", hWnd), idx: A_Index - 1
, CommandLine: ComObjGet("winmgmts:").Get("Win32_Process.Handle=" . PID).CommandLine, hTB: oMousePosInfo.hToolbar
, Tooltip: Tooltip, ID: Format("{:#x}", uID), Msg: Format("{:#x}", nMsg), hIcon: Format("{:#x}", hIcon) }
}
}
IsMouseOverTray()
{
CoordMode, Mouse
MouseGetPos, X, Y, hWnd, hControl, 2
WinGetClass, Class, ahk_id %hWnd%
if Class not in NotifyIconOverflowWindow,Shell_TrayWnd
Return
if (Class = "NotifyIconOverflowWindow")
ControlGet, hToolbar, hwnd,, ToolbarWindow321, ahk_id %hWnd%
else {
for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
hToolbar := DllCall("FindWindowEx", Ptr, k = 1 ? hWnd : hToolbar, Ptr, 0, Str, v, UInt, 0, Ptr)
if (hToolbar != hControl)
Return
}
Return {hToolbar: hToolbar, X: X, Y: Y}
}
Class RemoteBuffer
{
__New(PID, size)
{
static PROCESS_VM_OPERATION := 0x8, PROCESS_VM_WRITE := 0x20, PROCESS_VM_READ := 0x10
, MEM_COMMIT := 0x1000, PAGE_READWRITE := 0x4
if !(this.hProc := DllCall("OpenProcess", UInt, PROCESS_VM_OPERATION|PROCESS_VM_READ|PROCESS_VM_WRITE, Int, 0, UInt, PID, Ptr))
Return
if !(this.ptr := DllCall("VirtualAllocEx", Ptr, this.hProc, UInt, 0, Ptr, size, UInt, MEM_COMMIT, UInt, PAGE_READWRITE, Ptr))
Return, "", DllCall("CloseHandle", Ptr, this.hProc)
}
__Delete()
{
DllCall("VirtualFreeEx", Ptr, this.hProc, Ptr, this.ptr, UInt, 0, UInt, MEM_RELEASE := 0x8000)
DllCall("CloseHandle", Ptr, this.hProc)
}
Read(ByRef LocalBuff, size, offset = 0)
{
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, Ptr, size, Ptr, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка " A_LastError, Str, "", UInt, 0)
Return 1
}
}
CloseProcess(oInfo)
{
DHW_Prev := A_DetectHiddenWindows
DetectHiddenWindows, On
WinExist("ahk_id" oInfo.hWnd)
WinClose
WinWaitClose,,, 2
close := !ErrorLevel
DetectHiddenWindows, % DHW_Prev
if close
Return
MsgBox, 4,, % "Не удалось корректно завершить процесс " oInfo.ProcessName ". Убить его?"
IfMsgBox, No
Return
Process, Close, % oInfo.PID
Process, WaitClose, % oInfo.PID, 2
if ErrorLevel {
MsgBox, % "Не удалось завершить процесс " oInfo.ProcessName
Return
}
SendMessage, TB_DELETEBUTTON := 0x416, oInfo.idx,,, % "ahk_id" oInfo.hTB
SendMessage, WM_SETTINGCHANGE := 0x1A,,,, % "ahk_id" oInfo.hTB
}
ShowInfo(oInfo)
{
for k, v in ["PID", "ProcessName", "ProcessPath", "CommandLine", "Msg", "ID", "hWnd", "hIcon", "ToolTip"]
list .= v " = " oInfo[v] "`r`n"
Run, notepad.exe,,, PID
WinWait, ahk_pid %PID%
SendMessage, WM_SETICON := 0x80, ICON_SMALL := 0, oInfo.hIcon ; иконка в строку заголовка окна
if script := RegExMatch(oInfo.CommandLine, "i)\.exe.*\.ahk")
ScriptName := RegExReplace(oInfo.CommandLine, "i)(*UCP).*\W(\w+\.ahk).*", "$1")
title := script ? ScriptName : oInfo.ProcessName
SendMessage, WM_SETTEXT := 0xC,, &title
ControlSetText, Edit1, % Trim(list, "`r`n")
}
OpenFolder(oInfo)
{
if !RegExMatch(oInfo.CommandLine, "i)\.exe.*\.ahk")
FilePath := oInfo.ProcessPath
else {
if !FileExist(FilePath := RegExReplace(oInfo.CommandLine, "i).*([a-z]:\\.*\.ahk).*", "$1")) {
MsgBox, 4,, Папка скрипта не найдена. Открыть папку исполнимого файла?
IfMsgBox, Yes
FilePath := oInfo.ProcessPath
else
FilePath := ""
}
}
if FilePath
Run, % "Explorer /select`, """ . FilePath . """"
}
GetIcon(oInfo)
{
static WM_SETICON := 0x80, ICON_SMALL := 0, SS_ICON := 0x3, STM_SETIMAGE := 0x172, IMAGE_ICON := 1
Gui, MyGui:New, +LastFound -DPIScale
Gui, Add, Pic, x80 y40 w32 h32 %SS_ICON%
hIcon := oInfo.hIcon
SendMessage, WM_SETICON, ICON_SMALL, hIcon ; иконка в строку заголовка окна
PostMessage, STM_SETIMAGE, IMAGE_ICON, hIcon, Static1 ; иконка в контрол Picture
WinGetPos, X, Y,,, % "ahk_id" oInfo.hTB
Gui, Show, % "w192 h112 x" X " y" Y - 160, Tray Icon
}
MyGuiClose()
{
Gui, MyGui:Destroy
}
Можно также создавать разные меню для каждой иконки, ориентируясь на её ProcessName.