Тема: AHK: Заголовки колонок ListView из другого приложения
Заголовочный контрол в ListView — это окно класса SysHeader32. Для получения информации о заголовках он поддерживает сообщение HDM_GETITEM. Однако в этом сообщении требуется указать адрес, куда он должен поместить запрошенную информацию, и так как контрол находится в другом процессе по отношению к скрипту, подсовывать ему адреса переменных скрипта бессмысленно. Подробнее см. в теме AHK: Доступ к памяти других процессов.
Пример ниже читает текст заголовков на вкладке "Процессы" в окне Диспетчера задач. С помощью API-функции VirtualAllocEx выделяется память в адресном пространстве процесса под структуру HDITEM и строку заголовка. Потом строка читается оттуда через ReadProcessMemory. HDITEM сначала создаётся в самом скрипте, поскольку нужно заполнить некоторые её поля и я счёл, что проще сделать это в скрипте и потом скопировать структуру в память процесса одним вызовом WriteProcessMemory, нежели чем писать туда каждое поле по-отдельности.
If not WinExist("Диспетчер задач Windows") {
Run, Taskmgr.exe
WinWait, Диспетчер задач Windows
}
; Переключиться на вкладку "Процессы" (взято из справки).
SendMessage, 0x1330, 1,, SysTabControl321 ; 0x1330 is TCM_SETCURFOCUS.
Sleep, 0
SendMessage, 0x130C, 1,, SysTabControl321 ; 0x130C is TCM_SETCURSEL.
ControlGet, hwndHeader, Hwnd,, SysHeader321 ; Хэндл заголовочного контрола.
Headers := HeaderGetText(hwndHeader) ; Список заголовков.
MsgBox, % Headers
; ======== Функция, возвращающая список заголовков =======
HeaderGetText(hWnd)
{
MaxName := 100 ; Максимальная длина заголовка.
Delimiter := "`n" ; Разделитель заголовков в списке.
PROCESS_VM_OPERATION := 0x8, PROCESS_VM_READ := 0x10
PROCESS_VM_WRITE := 0x20, MEM_COMMIT := 0x1000
MEM_DECOMMIT := 0x4000, PAGE_READWRITE = 0x4
HDI_TEXT := 0x2, HDM_GETITEMCOUNT := 0x1200
HDM_GETITEMA := 0x1203
VarSetCapacity(Buf, MaxName, 0) ; Буфер под заголовок.
VarSetCapacity(hdi, 48, 0) ; Структура HDITEM, её размер 48 байт.
; В неё будет скидываться информация о каждом
; заголовке в ответ на сообщение HDM_GETITEM.
; PID процесса, создавшего заголовочный контрол.
DllCall("GetWindowThreadProcessId", "uint", hWnd
, "uint *", PID)
hProcess := DllCall("OpenProcess", "uint", PROCESS_VM_READ
| PROCESS_VM_WRITE
| PROCESS_VM_OPERATION
, "int", FALSE
, "uint", PID)
If (hProcess = 0) {
MsgBox, Не удалось открыть процесс.
Return
}
; Выделение памяти в адресном пространстве процесса
; под структуру HDITEM и строку заголовка.
phdi := DllCall("VirtualAllocEx", "uint", hProcess
, "uint", 0
, "uint", MaxName + 48
, "uint", MEM_COMMIT
, "uint", PAGE_READWRITE)
If (phdi = 0) {
MsgBox, Ошибка выделения памяти в процессе.
Goto, Close
}
; Запись нужных значений в структуру.
NumPut(HDI_TEXT, hdi), NumPut(phdi+48, hdi, 8), NumPut(MaxName, hdi, 16)
; Структура копируется в память процесса.
Ret := DllCall("WriteProcessMemory", "uint", hProcess
, "uint", phdi
, "uint", &hdi
, "uint", 48
, "uint", 0)
If (Ret = 0) {
MsgBox, Ошибка записи в память процесса.
Goto, Free
}
; Запрос количества заголовков.
Count := DllCall("SendMessageA", "uint", hWnd
, "uint", HDM_GETITEMCOUNT
, "uint", 0
, "uint", 0)
If (Count < 0) {
MsgBox, Ошибка определения количества заголовков.
Goto, Free
}
Loop, % Count ; Запрос информации о каждом заголовке.
{
DllCall("SendMessageA", "uint", hWnd
, "uint", HDM_GETITEMA
, "uint", A_Index - 1
, "uint", phdi)
; Заголовок копируется из памяти процесса в местный буфер.
Ret := DllCall("ReadProcessMemory", "uint", hProcess
, "uint", phdi+48
, "uint", &Buf
, "uint", MaxName
, "uint", 0)
If (Ret = 0) {
MsgBox, Ошибка чтения из памяти процесса.
Goto, Free
}
VarSetCapacity(Buf, -1) ; Обновление переменной.
Headers .= Buf . (A_Index != Count ? Delimiter : "")
}
Free: ; Освобождение памяти в процессе.
DllCall("VirtualFreeEx", "uint", hProcess
, "uint", phdi
, "uint", MaxName+48
, "uint", MEM_DECOMMIT)
Close: ; Освобождение хэндла процесса.
DllCall("CloseHandle", "uint", hProcess)
Return Headers
}