Вообщем пока только кликами мыши.
Клавиша "1".
Клавиша "2".
Указатели в массиве Path кроме последнего должны быть подменю, последний пунктом. Последний может являтся массивом из 2 пунктов. Если первый пункт отмечен или выключен, то будет выбран второй. Таким образом одним хоткеем можно сделать переключатель. Пункты можно указать как порядковые номера (разделитель тоже считается) с амперсандом в конце, например в скайпе так и пришлось указать, по неизвестной мне причине в подменю текст пунктов не читается. Если в имени пункта есть амперсанд, он должен быть удалён из указателя.
#SingleInstance Force
#NoEnv
#NoTrayIcon
1::
; Path := ["Ограничение приёма", "150 КБ/с"]
; Path := ["Ограничение приёма", "Неограниченно"]
Path := ["Ограничение приёма", ["Неограниченно", "150 КБ/с"]]
Title = ahk_class µTorrent4823DF041B09 ahk_exe uTorrent.exe
MenuSelect(Path, Title)
Return
2::
Path := ["Сетевой статус", ["1&", "3&"]]
Title = ahk_class tSkMainForm ahk_exe Skype.exe
MenuSelect(Path, Title)
Return
Esc:: ExitApp
MenuSelect(Path, Title) {
WinGet, PID, PID, % "ahk_id" hWnd := WinExist(Title)
PostMessage2TrayIconProcID(PID, WM_RBUTTONDOWN := 0x204, WM_RBUTTONUP := 0x205)
WinWait, % "ahk_class #32768 ahk_pid" PID, , 0.5
If ErrorLevel
Return
SendMessage, 0x1E1 ; MN_GETHMENU
If !(hMenu := ErrorLevel)
Return
DllCall("GetCursorPos", "int64P", pt)
X := pt << 32 >> 32
Y := pt >> 32
MenuSelectPath(hMenu, hWnd, Path)
DllCall("SetCursorPos", "Uint", X, "Uint", Y)
}
MenuSelectPath(hMenu, hWnd, Path, Key = 1) {
Static MF_BYPOSITION := 0x400, MF_CHECKED := 0x00000008, MF_DISABLED := 0x00000002, MF_GRAYED := 0x00000001
, MF_MENUBARBREAK := 0x00000020, MF_MENUBREAK := 0x00000040, MF_SEPARATOR := 0x00000800
Item := Path[Key]
If IsObject(Item)
Item := Path[Key][1]
If (Item ~= "^\d+&$")
ItemNum := SubStr(Item, 1, -1) - 1
Loop, % DllCall("GetMenuItemCount", "Ptr", hMenu)
{
idx := A_Index - 1
If (Item ~= "^\d+&$")
{
If (ItemNum != idx)
Continue
If (Key < Path.MaxIndex())
{
MenuSetCursor(hMenu, hWnd, idx)
Return MenuSelectPath(DllCall("GetSubMenu", "Ptr", hMenu, "Uint", idx), hWnd, Path, Key + 1)
}
Find := 1
Break
}
Else
{
idn := DllCall("GetMenuItemID", "Ptr", hMenu, "int", idx)
nSize++ := DllCall("GetMenuString", "Ptr", hMenu, "Uint", idx, "Uint", 0, "Uint", 0, "Uint", MF_BYPOSITION)
nSize := (nSize * (A_IsUnicode ? 2 : 1))
VarSetCapacity(sString, nSize)
DllCall("GetMenuString", "Ptr", hMenu, "Uint", idx, "Str", sString, "Uint", nSize, "Uint", MF_BYPOSITION) ; MF_BYPOSITION
StringReplace, String, sString, &, , 1
If (Item != String)
Continue
If (Key < Path.MaxIndex())
{
MenuSetCursor(hMenu, hWnd, idx)
Return MenuSelectPath(DllCall("GetSubMenu", "Ptr", hMenu, "Uint", idx), hWnd, Path, Key + 1)
}
Find := 1
Break
}
}
If !Find
Return 0
If IsObject(Path[Key])
{
State := DllCall("GetMenuState", "Ptr", hMenu, "UInt", idx, "UInt", MF_BYPOSITION)
If (State & MF_CHECKED || State & MF_DISABLED || State & MF_GRAYED || State & MF_MENUBARBREAK || State & MF_MENUBREAK || State & MF_SEPARATOR)
Return MenuSelectPath(hMenu, hWnd, [Path[Key][2]], 1)
}
MenuSetCursor(hMenu, hWnd, idx)
Return 1
}
MenuSetCursor(hMenu, hWnd, idx) {
VarSetCapacity(pt, 16, 0)
DllCall("GetMenuItemRect"
, "Ptr", hWnd
, "Ptr", hMenu
, "UInt", idx
, "UInt", &pt)
x := NumGet(pt, 0), y := NumGet(pt, 4)
w := NumGet(pt, 8) - x, h := NumGet(pt, 12) - y
DllCall("SetCursorPos", "Uint", x + w // 2, "Uint", y + h // 2)
Click
Sleep 100
}
Return
PostMessage2TrayIconProcID(ProcessPID, messages*)
{
/*
Структура TRAYDATA
typedef TRAYDATA
{
IntPtr hwnd;
uint uID;
uint uCallbackMessage;
uint Reserved;
uint Reserved2;
IntPtr hIcon;
}
*/
Static WM_USER := 0x400, TB_BUTTONCOUNT := WM_USER + 24, TB_GETBUTTON := WM_USER + 23
, PtrSize := A_Is64bitOS ? 8 : 4, SizeOfTBBUTTON := 8 + PtrSize*3, SizeOfTRAYDATA := 16 + PtrSize*2
DHW_Prev := A_DetectHiddenWindows
DetectHiddenWindows, On
Loop 2
{
ControlGet, hWnd, hwnd,, ToolbarWindow321, % "ahk_class " . (A_Index = 1 ? "Shell_TrayWnd" : "NotifyIconOverflowWindow")
WinExist("ahk_id" hWnd)
WinGet, PID, PID
if (A_Index = 1 && !IsObject(RemoteBuff := New RemoteBuffer(PID, SizeOfTRAYDATA))) {
DetectHiddenWindows, %DHW_Prev%
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось создать удалённый буфер`nОшибка " A_LastError, Str, "", UInt, 0)
}
SendMessage, TB_BUTTONCOUNT
Loop % ErrorLevel
{
SendMessage, TB_GETBUTTON, A_Index - 1, RemoteBuff.ptr
if ! ( pTBBUTTON := RemoteBuff.Read(SizeOfTBBUTTON) )
|| !pTRAYDATA := RemoteBuff.Read(SizeOfTRAYDATA, NumGet(pTBBUTTON + 8 + PtrSize) - RemoteBuff.ptr) {
DetectHiddenWindows, %DHW_Prev%
Return
}
WinGet, PID, PID, % "ahk_id" hWnd := NumGet(pTRAYDATA+0)
if (PID = ProcessPID)
{
uID := NumGet(pTRAYDATA + PtrSize, "UInt")
uCallbackMessage := NumGet(pTRAYDATA + PtrSize + 4, "UInt")
; FocusTrayIcon(hWnd, uID)
for i, message in messages
PostMessage, uCallbackMessage, uID, message,, % "ahk_id" hWnd
DetectHiddenWindows, %DHW_Prev%
Return
}
}
}
DetectHiddenWindows, %DHW_Prev%
MsgBox, % "Иконка процесса " . ProcessPID . " не найдена"
}
FocusTrayIcon(hWnd, uID)
{
VarSetCapacity(NOTIFYICONDATA, size := A_PtrSize = 8 ? 848 : A_IsUnicode? 828 : 444, 0)
NumPut(size, NOTIFYICONDATA, "UInt")
NumPut(hWnd, NOTIFYICONDATA, A_PtrSize)
NumPut(uID, NOTIFYICONDATA, 2*A_PtrSize)
DllCall("shell32\Shell_NotifyIcon", "UInt", NIM_SETFOCUS := 0x00000003, Ptr, &NOTIFYICONDATA)
Return
}
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", UInt, this.hProc, UInt, 0, UInt, 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(size, offset = 0)
{
static LocalBuff
VarSetCapacity(LocalBuff, size, 0)
if !DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, size, UInt, 0)
Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось прочитать данные`nОшибка " A_LastError, Str, "", UInt, 0)
VarSetCapacity(LocalBuff, -1)
Return &LocalBuff
}
Write(pLocalBuff, size, offset = 0)
{
res := DllCall("WriteProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, pLocalBuff, UInt, size, UInt, 0)
Return res ? res : 0, (!res) ? DllCall("MessageBox", Ptr, 0, Str, "Не удалось записать данные`nОшибка " A_LastError, Str, "", UInt, 0)
}
}