1

Тема: AHK: Поиск в ListView

Поиск в контроле SysListView32 по словам, с выделением строки и подсветкой нужной колонки.

#NoEnv
SetBatchLines, -1

#If Control := ListViewFocus()
^vk46::  ; Ctrl + F
   hWnd := WinExist("A")
   Gui, Search:Default
   Gui, +Owner%hWnd% +AlwaysOnTop
   Gui, Margin,, 10
   Gui, Add, Text, x10 y10, Текст для поиска:
   Gui, Add, Edit, x+5 yp-3 w150 h21 vEdit, % Edit
   Gui, Add, Button, x+-125 y+10 w60 h21 gGo Default, Искать
   Gui, Add, Button, x+5 yp wp hp gCancel, Отмена
   Gui, Add, Radio, % "x15 yp+4 vRadio" (Radio != 0 ? " Checked" : ""), Вниз
   Gui, Add, Radio, % "x+5 yp" (Radio = 0 ? " Checked" : ""), Вверх
   WinGetPos, X, Y,,, A
   Gui, Show, % "x" X + 100 " y" Y + 50, Поиск в ListView
   return
   
Go:
   GuiControlGet, Edit, Search:
   if (Edit = "")
      Goto, Cancel
   
   ControlGet, hLv, hwnd,, % Control, ahk_id %hWnd%
   oLv := ""
   if !IsObject(oLv := New ListView(hLv, Edit))
   {
      MsgBox, 0x1030, Ошибка, Невозможно извлечь информацию из ListView!
      return
   }
   GuiControlGet, Radio, Search:
   Gui, Search:Destroy
   ControlGet, Focus, List, Count Focused,, % "ahk_id" oLv.h
   oFound := oLv.SearchWord(Focus, 1, Radio)
   if (oFound.state = "not_found")
      DllCall("MessageBox", Ptr, oLv.h, Str, "Текст """ Edit """ не найден!", Str, "Не найдено", UInt, 0x30)
   else if (oFound.state = "found")
   {
      CurrentRow := oFound.row, CurrentCol := oFound.col
      oLv.SetSel(CurrentRow), oLv.Highlight(CurrentRow, CurrentCol)
      Hotkey, If, % ListViewFocus()
      Hotkey, F3, Search, On
   }
   oFound := ""
   return
   
Search:
   (Radio && ++CurrentCol > oLv.CountCol) ? (CurrentCol := 1, ++CurrentRow)
   (!Radio && --CurrentCol = 0) ? (CurrentCol := oLv.CountCol, --CurrentRow)
   
   oFound := oLv.SearchWord(CurrentRow, CurrentCol, Radio)
   if (oFound.state != "found")
      Return 
   
   CurrentRow := oFound.row, CurrentCol := oFound.col
   oLv.SetSel(CurrentRow), oLv.Highlight(CurrentRow, CurrentCol)
   return
   
SearchGuiClose:
SearchGuiEscape:
Cancel:
   Gui, Search:Destroy
   return

Class ListView
{
   __New(hListView, word)
   {
      ControlGet, CountCol, List, Count Col,, ahk_id %hListView%
      if !CountCol
         Return ""
      
      WinGet, PID, PID, ahk_id %hListView%
      this.h := hListView
      this.pid := PID
      this.comp := this.CompareCapacity(PID)
      this.CountCol := CountCol
      this.word := word
   }
   
   CompareCapacity(PID)
   {
      if !A_Is64bitOS
         Return 1
      
      if !hProc := DllCall("OpenProcess", UInt, PROCESS_QUERY_INFORMATION := 0x400, Int, 0, UInt, PID, Ptr)
         Return

      VarSetCapacity(b, 4)
      DllCall("IsWow64Process", Ptr, hProc, Ptr, &b)
      DllCall("CloseHandle", Ptr, hProc)
      b := NumGet(b, "UInt")
      
      Return ((A_PtrSize = 4 && b) || (A_PtrSize = 8 && !b)) ? 1 : ""
   }
   
   SearchWord(StartRow, StartCol, Direction)
   {
      if this.comp
         Return this.SearchViaMessage(StartRow, StartCol, Direction)
      else
         Return this.SearchViaParsing(StartRow, StartCol, Direction)
   }
   
   SearchViaMessage(StartRow, StartCol, Direction)
   {
      static found
      Direction ? (CurrentRow := StartRow-1
         , CurrentCol := StartCol-1) : (CurrentRow := StartRow+1, CurrentCol := StartCol+1)
      
      RemoteBuff := New RemoteBuffer(this.pid, (A_PtrSize = 4 ? 60 : 68) + 260*(A_IsUnicode ? 2 : 1))
      Loop
      {
         ControlGet, RowsCount, List, Count,, % "ahk_id" this.h
         (Direction && ++CurrentRow > RowsCount) ? CurrentRow := 1
         (!Direction && --CurrentRow = 0) ? CurrentRow := RowsCount
         Loop
         {
            if (Direction && ++CurrentCol > this.CountCol && !(CurrentCol := 0))
               || (!Direction && --CurrentCol = 0 && (CurrentCol := this.CountCol + 1))
               break
            
            Text := this.GetItemText(CurrentRow, CurrentCol, RemoteBuff)
            if ((A_IsUnicode ? RegExMatch(Text, "i)(*UCP)\Q" this.word "\E") : InStr(Text, this.word)) && found := 1)
               Return {state: "found", row: CurrentRow, col: CurrentCol}
            
            if (board && CurrentRow = StartRow && CurrentCol = StartCol && !found)
               Return {state: "not_found"}
            
            ((Direction && CurrentRow = RowsCount && CurrentCol = this.CountCol)
               || (!Direction && CurrentRow = 1 && CurrentCol = 1)) ? board := 1
            (CurrentRow = StartRow && CurrentCol = StartCol) ? found := ""
         }
      }
   }
   
   SearchViaParsing(StartRow, StartCol, Direction)
   {
      static found, FirstItem
      
      StartIndex := StartRow * this.CountCol + StartCol
      ControlGet, Content, List,,, % "ahk_id" this.h
      Loop, parse, Content, `n
      {
         CurrentRow := A_Index
         Loop, parse, A_LoopField, % A_Tab
         {
            CurrentIndex := CurrentRow * this.CountCol + A_Index
            if (!WaitingNear && (A_IsUnicode ? RegExMatch(A_LoopField, "i)(*UCP)\Q" this.word "\E") : InStr(A_LoopField, this.word)))
               LastItem := CurrentIndex, !found ? (FirstItem := CurrentIndex, found := 1)
            
            if (CurrentIndex = StartIndex)
            {
               if (!Direction && found && !(found := 0))
                  Return this.CreateFromIndex(LastItem)
               Direction ? WaitingNear := 1
            }
               
            if (WaitingNear 
               && (A_IsUnicode ? RegExMatch(A_LoopField, "i)(*UCP)\Q" this.word "\E") : InStr(A_LoopField, this.word))
               && !(found := 0))
               Return this.CreateFromIndex(CurrentIndex)
         }
      }
      if !found
         Return {state: "not_found"}
      else if !(found := 0)
         Return this.CreateFromIndex(Direction ? FirstItem : LastItem)
   }
   
   CreateFromIndex(idx)
   {
      i := idx//this.CountCol, m := Mod(idx, this.CountCol)
      Return {state: "found", row: m ? i : i - 1, col: m ? m : this.CountCol}
   }
   
   GetItemText(item, col, RemoteBuff)
   {
      static LVIF_TEXT := 1, LVM_GETITEM := (A_IsUnicode ? 0x104B : 0x1005), LVITEM_SIZE := A_PtrSize = 4 ? 60 : 68
      
      VarSetCapacity(LVITEM, LVITEM_SIZE)
      NumPut(LVIF_TEXT, LVITEM, "UInt")
      NumPut(item-1, LVITEM, 4, "Int")
      NumPut(col-1, LVITEM, 8, "Int")
      NumPut(RemoteBuff.Ptr + LVITEM_SIZE, LVITEM, 16 + A_PtrSize, "Ptr")
      NumPut(260, LVITEM, 16 + A_PtrSize*2, "Int")
         
      RemoteBuff.Write(&LVITEM, LVITEM_SIZE)
      SendMessage, LVM_GETITEM, 0, RemoteBuff.Ptr,, % "ahk_id" this.h
      Return StrGet(RemoteBuff.Read(260*(A_IsUnicode ? 2 : 1), LVITEM_SIZE))
   }
   
   SetSel(row)
   {
      static LVIS_FOCUSED := 1, LVIS_SELECTED := 2, LVM_SETITEMSTATE := 0x102B, LVM_ENSUREVISIBLE := 0x1013

      VarSetCapacity(LVITEM, LVITEM_SIZE := A_PtrSize = 4 ? 60 : 68)
      NumPut(LVIS_FOCUSED | LVIS_SELECTED, LVITEM, 12)
      NumPut(LVIS_FOCUSED | LVIS_SELECTED, LVITEM, 16)
      
      RemoteBuff := New RemoteBuffer(this.pid, LVITEM_SIZE)
      RemoteBuff.Write(&LVITEM, LVITEM_SIZE)

      SendMessage, LVM_ENSUREVISIBLE, row - 1,,, % "ahk_id" this.h
      SendMessage, LVM_SETITEMSTATE, row - 1, RemoteBuff.Ptr,, % "ahk_id" this.h
   }
   
   Highlight(row, col)
   {
      static counter
      counter := 0, th := 3  ; толщина рамки
      Gui, Highlighting:Default
      Gui, Destroy
      
      VarSetCapacity(Init, 4)
      NumPut(col = 1 ? 1 : col - 1, Init, "UInt")
      RemoteBuff := New RemoteBuffer(this.pid, 16)
      RemoteBuff.Write(&Init, 4, 4)
      SendMessage, LVM_GETSUBITEMRECT := 0x1038, row - 1, RemoteBuff.Ptr,, % "ahk_id" this.h
      pAddr := RemoteBuff.Read(16)
      l := NumGet(pAddr+0, "UInt"), t := NumGet(pAddr+0, 4, "UInt")
      r := NumGet(pAddr+0, 8, "UInt"), b := NumGet(pAddr+0, 12, "UInt")
      RemoteBuff := ""
      (col = 1) ? (lprev := l, l := 0, r := lprev)
      WinGetPos, X, Y,,, % "ahk_id" this.h
      
      Gui, % "-Caption +ToolWindow +Owner" this.h " +hwndhGui"
      Gui, Color, Red
      Gui, Show, % "x" X + l + 1 " y" Y + t + 2 " w" (w := r - l) " h" (h := b - t) " Hide"
      Prev_DHW := A_DetectHiddenWindows
      DetectHiddenWindows, On
      WinSet, Region, %    "0-0 "             w "-0 "                w "-" h                 " 0-" h             " 0-0 " 
                       . th "-" th   " "   w - th "-" th   " "   w - th "-" h - th   " "   th "-" h - th   " "   th "-" th
                       , ahk_id %hGui%
      DetectHiddenWindows, %Prev_DHW%
      Gui, Show, NA
      SetTimer, Flashing, 300
      return
      
   Flashing:
      Gui, Highlighting:Default
      if (++counter = 1)
         Gui, Show, Hide
      if (counter = 2)
         Gui, Show, NA
      if (counter = 3)
      {
         Gui, Destroy
         SetTimer, Flashing, Off
      }
      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 ""
      
      this.size := size
   }
   
   __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, s := (size > (s := this.size - offset) ? s : size), 0)
      DllCall("ReadProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, &LocalBuff, UInt, s, UInt, 0)
      VarSetCapacity(LocalBuff, -1)
      Return &LocalBuff
   }
   
   Write(pLocalBuff, size, offset = 0)
   {
      return DllCall("WriteProcessMemory", Ptr, this.hProc, Ptr, this.ptr + offset, Ptr, pLocalBuff, UInt, size, UInt, 0)
   }
}

ListViewFocus()
{
   ControlGetFocus, Control, A
   if InStr(Control, "SysListView32") && !WinExist("Поиск в ListView ahk_class AutoHotkeyGUI")
      return Control
}

Пример использования.
Запускаем Диспетчер задач, открываем вкладку Процессы. Нажимаем Ctrl + F, в появившемся окне вводим autohotkey и кликаем "Искать", либо Enter. Далее жмём F3.
Контрол SysListView32 для обязательно должен быть в фокусе. В ANSI-версии кириллица регистрозависимая.
Скрипт довольно сложный, вопросы задавайте в этой теме.

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