1

Тема: AHK: Получение данных об иконке в трее под курсором

Скрипт выводит данные об иконке в трее или в окне NotifyIconOverflowWindow по клику правой кнопкой над ней при удержании Ctrl.

^RButton::   ; Ctrl + RButton
   if !oTrayIconInfo := GetTrayIconInfoFromMousePos()  {
      MsgBox, Не удалось получить информацию!
      Return
   }

   MsgBox, % "PID = "         oTrayIconInfo.PID
         . "`nProcessName = " oTrayIconInfo.ProcessName
         . "`nProcessPath = " oTrayIconInfo.ProcessPath
         . "`nCommandLine = " oTrayIconInfo.CommandLine
         . "`nhWnd = "        oTrayIconInfo.hWnd
         . "`nTooltip = "     oTrayIconInfo.Tooltip
   Return
   
Esc:: ExitApp

GetTrayIconInfoFromMousePos()
{
/*
Недокументированная структура 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, TB_GETITEMRECT := WM_USER + 29
      , PtrSize := A_Is64bitOS ? 8 : 4, SizeOfTBBUTTON := 8 + PtrSize*3, SizeOfTRAYDATA := 16 + PtrSize*2, SizeOfiString := 256
   ; проверяем, находится ли курсор над треем или над окном NotifyIconOverflowWindow   
   if !hToolbar := ControlMousePos(XM, YM)
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Курсор не над треем", Str, "", UInt, 0)
   
   WinExist("ahk_id" hToolbar)
   WinGet, PID, PID
   ; создаём буфер в адресном пространстве процесса explorer.exe (которому принадлежит и окно трея)
   if !IsObject(RemoteBuff := New RemoteBuffer(PID, SizeOfTBBUTTON))
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Не удалось создать удалённый буфер`nОшибка " A_LastError, Str, "", UInt, 0)
   
   WinGetPos, xTB, yTB
   POINT := (XM - xTB) | (YM - yTB) << 32
   
   SendMessage, TB_BUTTONCOUNT
   Loop % ErrorLevel  {
      ; информация, возвращаемая сообщениями TB_GETITEMRECT и TB_GETBUTTON, находится в адресном пространстве
      ; процесса explorer.exe, поэтому для её чтения пользуемся созданным ранее буфером
      ; TB_GETITEMRECT возвращает в структуре RECT координаты иконки с индексом A_Index - 1 относительно окна трея или окна NotifyIconOverflowWindow
      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)
      
      DHW_Prev := A_DetectHiddenWindows
      DetectHiddenWindows, On
      ; получаем смещение структуры TRAYDATA относительно нашего буфера
      TraydataOffset := NumGet(&TBBUTTON + 8 + PtrSize) - RemoteBuff.ptr
      ; получаем смещение строки со всплывающей подсказкой иконки относительно нашего буфера
      TooltipOffset := NumGet(&TBBUTTON + 8 + PtrSize*2) - RemoteBuff.ptr
      RemoteBuff.Read(TRAYDATA, SizeOfTRAYDATA, TraydataOffset), WinExist("ahk_id" hWnd := NumGet(&TRAYDATA))
      RemoteBuff.Read(String, SizeOfiString, TooltipOffset), Tooltip := StrGet(&String, "UTF-16")
      WinGet, Name, ProcessName
      WinGet, ProcessPath, ProcessPath
      WinGet, PID, PID
      DetectHiddenWindows, %DHW_Prev%
      
      Return {ProcessName: Name, PID: PID, ProcessPath: ProcessPath, hWnd: Format("{:#x}", hWnd)
            , CommandLine: ComObjGet("winmgmts:").Get("Win32_Process.Handle=" . PID).CommandLine, Tooltip: Tooltip}
   }
}

ControlMousePos(ByRef X, ByRef Y)
{
   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
}

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
   }
}

Вопросы можно задать в этой теме.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg