How can I get the text of the column header of a SysListView32? Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Alguimist
Posts: 428
Joined: 05 Oct 2015, 16:41
Contact:

Re: How can I get the text of the column header of a SysListView32?

Post by Alguimist » 01 Aug 2018, 19:21

SunAndSuch wrote:I can find out the number of the column, but I need the text of its header.
SunAndSuch wrote:Anyway, is there some other way of finding out which column number the mouse has clicked?
Are you able to determine which column was clicked?
SunAndSuch wrote:Then I check the coordinates and they are all zero.
You have to allocate a RECT in the remote process (OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory).
teadrinker wrote:Here is the wrong size, cbHDITEM is needed.
Thank you, problem solved :thumbup: . But I still don't understand why it is not needed in WinSpy.

Code: Select all

; Returns an object containing the text and width of each item of a remote SysHeader32 control
GetHeaderInfo(hHeader) {
    Static MAX_TEXT_LENGTH := 260
         , MAX_TEXT_SIZE := MAX_TEXT_LENGTH * (A_IsUnicode ? 2 : 1)

    WinGet PID, PID, ahk_id %hHeader%

    ; Open the process for read/write and query info.
    ; PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION
    If !(hProc := DllCall("OpenProcess", "UInt", 0x438, "Int", False, "UInt", PID, "Ptr")) {
        Return
    }

    ; Should we use the 32-bit struct or the 64-bit struct?
    If (A_Is64bitOS) {
        Try DllCall("IsWow64Process", "Ptr", hProc, "int*", Is32bit := true)
    } Else {
        Is32bit := True
    }

    RPtrSize := Is32bit ? 4 : 8
    cbHDITEM := (4 * 6) + (RPtrSize * 6)

    ; Allocate a buffer in the (presumably) remote process.
    remote_item := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0
                         , "uPtr", cbHDITEM + MAX_TEXT_SIZE
                         , "UInt", 0x1000, "UInt", 4, "Ptr") ; MEM_COMMIT, PAGE_READWRITE
    remote_text := remote_item + cbHDITEM

    ; Prepare the HDITEM structure locally.
    VarSetCapacity(HDITEM, cbHDITEM, 0)
    NumPut(0x3, HDITEM, 0, "UInt") ; mask (HDI_WIDTH | HDI_TEXT)
    NumPut(remote_text, HDITEM, 8, "Ptr") ; pszText
    NumPut(MAX_TEXT_LENGTH, HDITEM, 8 + RPtrSize * 2, "Int") ; cchTextMax

    ; Write the local structure into the remote buffer.
    DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "uPtr", cbHDITEM, "Ptr", 0)

    HDInfo := {}
    VarSetCapacity(HDText, MAX_TEXT_SIZE)

    SendMessage 0x1200, 0, 0,, ahk_id %hHeader% ; HDM_GETITEMCOUNT
    Loop % (ErrorLevel != "FAIL") ? ErrorLevel : 0 {
        ; Retrieve the item text.
        SendMessage, % (A_IsUnicode) ? 0x120B : 0x1203, A_Index - 1, remote_item,, ahk_id %hHeader% ; HDM_GETITEMW
        If (ErrorLevel == 1) { ; Success
            DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "uPtr", cbHDITEM + MAX_TEXT_SIZE, "Ptr", 0)
            DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_text, "Ptr", &HDText, "uPtr", MAX_TEXT_SIZE, "Ptr", 0)
        } Else {
            HDText := ""
        }

        HDInfo.Push({"Width": NumGet(HDITEM, 4, "UInt"), "Text": HDText})
    }

    ; Release the remote memory and handle.
    DllCall("VirtualFreeEx", "Ptr", hProc, "Ptr", remote_item, "UPtr", 0, "UInt", 0x8000) ; MEM_RELEASE
    DllCall("CloseHandle", "Ptr", hProc)

    Return HDInfo
}
SunAndSuch wrote:And unsurprisingly, the PtInRect also returns 0.
Try this:

Code: Select all

#SingleInstance Force
#NoEnv

Global hHeader

; Let's pretend this is a ListView from a remote process.
Gui Add, ListView, hWndhLV x0 y0 w410 h252, Column 1|Column 2
SendMessage 0x101F, 0, 0,, ahk_id %hLV% ; LVM_GETHEADER
hHeader := ErrorLevel

Gui Show, w620 h420, Window

; In your script you'll need to use a hotkey, not WM_LBUTTONDOWN.
OnMessage(0x201, "OnWM_LBUTTONDOWN")
Return

GuiEscape:
GuiClose:
    ExitApp

OnWM_LBUTTONDOWN(wParam, lParam, msg, hWnd) {

    If (hWnd == hHeader) {
        ;VarSetCapacity(PT, 8, 0)
        ;DllCall("GetCursorPos", "Ptr", &PT)

        CoordMode Mouse, Client
        MouseGetPos PX, PY
       
        Loop 2 { ; Column count

            VarSetCapacity(RECT, 16, 0)
            SendMessage 0x1207, A_Index - 1, &RECT,, ahk_id %hHeader% ; HDM_GETITEMRECT
            If (ErrorLevel && ErrorLevel != "FAIL") {
                /*
                ; Not working
                If (DllCall("PtInRect", "Ptr", &RECT, "Ptr", &PT)) {
                    MsgBox % A_Index
                    Break
                }
                */

                If (PtInRect(NumGet(RECT, 0, "Int")
                           , NumGet(RECT, 4, "Int")
                           , NumGet(RECT, 8, "Int")
                           , NumGet(RECT, 12, "Int")
                           , PX, PY)) {
                    MsgBox Column %A_Index%
                    Break
                }
            }
        }
    }

    Return 1
}

PtInRect(RLeft, RTop, RRight, RBottom, PX, PY) {
    Return (((PX >= RLeft) && (PX <= RRight)) && ((PY >= RTop) && (PY <= RBottom)))
}

SunAndSuch
Posts: 46
Joined: 05 Oct 2015, 12:11

Re: How can I get the text of the column header of a SysListView32?

Post by SunAndSuch » 02 Aug 2018, 00:34

jeeswg wrote:Are you using x32 or x64?
x64.
Does it have 18 columns?
Yes.
Does it work on any other listview controls?
Here with "Task Manager":

Code: Select all

SHhwnd:="0x10678" ;SysHeader321
LVhwnd:="0x30674" ;SysListView321
res.="""JEE_LVHGetText:""`r`n"JEE_LVHGetText(SHhwnd) "`r`n`r`n""JEE_LVGetColOrder:""`r`n"
oColOrd:=JEE_LVGetColOrder(LVhwnd)
Loop,31
	res.=oColOrd[A_Index] ","
The res =
  • "JEE_LVHGetText:"
    Image Name
    PID
    User Name
    Session ID
    CPU
    CPU Time
    Working Set (Memory)
    Peak Working Set (Memory)
    Working Set Delta (Memory)
    Memory (Private Working Set)
    Commit Size
    Paged Pool
    NP Pool
    Page Faults
    PF Delta
    Base Pri
    Handles
    Threads
    USER Objects
    GDI Objects
    I/O Reads
    I/O Writes
    I/O Other
    I/O Read Bytes
    I/O Write Bytes
    I/O Other Bytes
    Image Path Name
    Command Line
    UAC Virtualization
    Description
    Data Execution Prevention

    "JEE_LVGetColOrder:"
    1,27,28,2,3,4,5,6,7,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
Windows 10, Ahk v1 x64-bit.

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: How can I get the text of the column header of a SysListView32?

Post by teadrinker » 02 Aug 2018, 07:52

Alguimist wrote:

Code: Select all

DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "uPtr", cbHDITEM + MAX_TEXT_SIZE, "Ptr", 0)
It's not completely true, should be:

Code: Select all

DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "uPtr", cbHDITEM, "Ptr", 0)

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How can I get the text of the column header of a SysListView32?

Post by jeeswg » 02 Aug 2018, 12:19

- I tested JEE_LVGetColOrder using AHK x64 and AHK x32 against Task Manager (Windows 7), and it worked correctly each time.
- I might suggest ensuring that you run AutoHotkey as admin, and test the function on listview controls in other programs. Also, you might like to mention which OS you use.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

SunAndSuch
Posts: 46
Joined: 05 Oct 2015, 12:11

Re: How can I get the text of the column header of a SysListView32?

Post by SunAndSuch » 02 Aug 2018, 16:50

Alguimist wrote:Are you able to determine which column was clicked?
No, that's what I am trying to do with the RECT thing and functions from jeeswg.
Try this:
I don't know how to use your code for the external window. How do I use a hotkey instead of WM_LBUTTONDOWN? I bet it's not like this?

Code: Select all

#SingleInstance Force
#NoEnv
Global hHeader
ControlGet,hLV,hwnd,,SysListView321,%externalWinTitle%
SendMessage 0x101F, 0, 0,, ahk_id %hLV% ; LVM_GETHEADER
hHeader := ErrorLevel
CapsLock::
OnWM_LBUTTONDOWN(0, 0, 0, hHeader)
return
OnWM_LBUTTONDOWN(wParam, lParam, msg, hWnd) {
    If (hWnd == hHeader) {
        CoordMode Mouse, Client
        MouseGetPos PX, PY
        Loop 2 { ; Column count
            VarSetCapacity(RECT, 16, 0)
            SendMessage 0x1207, A_Index - 1, &RECT,, ahk_id %hHeader% ; HDM_GETITEMRECT
            If (ErrorLevel && ErrorLevel != "FAIL") {
                If (PtInRect(NumGet(RECT, 0, "Int")
                           , NumGet(RECT, 4, "Int")
                           , NumGet(RECT, 8, "Int")
                           , NumGet(RECT, 12, "Int")
                           , PX, PY)) {
                    MsgBox Column %A_Index%
                    Break
                }
            }
        }
    }
    Return 1
}
PtInRect(RLeft, RTop, RRight, RBottom, PX, PY) {
    Return (((PX >= RLeft) && (PX <= RRight)) && ((PY >= RTop) && (PY <= RBottom)))
}
I have stripped your code down to the core of my problem. When I run this:

Code: Select all

Gui Add, ListView, hWndhLV x0 y0 w410 h252, Column 1|Column 2
SendMessage 0x101F, 0, 0,, ahk_id %hLV% ; LVM_GETHEADER
hHeader := ErrorLevel
Gui Show, w620 h420, testWin1
SetTimer,sub1,1000
return
GuiEscape:
GuiClose:
    ExitApp
sub1:
	VarSetCapacity(RECT, 16, 0)
	SendMessage 0x1207, 1, &RECT,, ahk_id %hHeader% ; HDM_GETITEMRECT
	result:="""hHeader"":" hHeader "`r`n" NumGet(RECT, 0, "Int") "," NumGet(RECT, 4, "Int") "," NumGet(RECT, 8, "Int") "," NumGet(RECT, 12, "Int")
	ExitApp
the result is:
"hHeader":592322
56,0,406,24

When I save the following code into a different file and run it while the Gui from your code is displayed:

Code: Select all

ControlGet,hLV,hwnd,,SysListView321,testWin1
SendMessage 0x101F, 0, 0,, ahk_id %hLV% ; LVM_GETHEADER
hHeader:=ErrorLevel+0
VarSetCapacity(RECT, 16, 0)
SendMessage 0x1207, 1, &RECT,,% "ahk_id " hHeader ; HDM_GETITEMRECT
result:="""hHeader"":" hHeader "`r`n" NumGet(RECT, 0, "Int") "," NumGet(RECT, 4, "Int") "," NumGet(RECT, 8, "Int") "," NumGet(RECT, 12, "Int")
the result is:
"hHeader":592322
0,0,0,0

Also when I run:

Code: Select all

ControlGet,hLV,hwnd,,SysListView321,%externalWinTitle%
SendMessage 0x101F, 0, 0,, ahk_id %hLV% ; LVM_GETHEADER
hHeader := ErrorLevel
VarSetCapacity(RECT, 16, 0)
SendMessage 0x1207, 1, &RECT,, ahk_id %hHeader% ; HDM_GETITEMRECT
result:="""hHeader"":" hHeader "`r`n" NumGet(RECT, 0, "Int") "," NumGet(RECT, 4, "Int") "," NumGet(RECT, 8, "Int") "," NumGet(RECT, 12, "Int")
the result is:
"hHeader":some header number
0,0,0,0
jeeswg wrote:you might like to mention which OS you use.
It's Windows 7.
Windows 10, Ahk v1 x64-bit.

User avatar
Alguimist
Posts: 428
Joined: 05 Oct 2015, 16:41
Contact:

Re: How can I get the text of the column header of a SysListView32?  Topic is solved

Post by Alguimist » 03 Aug 2018, 19:24

SunAndSuch wrote:I don't know how to use your code for the external window.
As I said, you have to allocate a RECT, otherwise HDM_GETITEMRECT may crash the target application.

Steps:
1. Get the process PID (WinGet).
2. DllCall OpenProcess.
3. DllCall VirtualAllocEx.
4. Create the RECT structure.
5. DllCall WriteProcessMemory.
6. Loop HDM_GETITEMCOUNT.
7. SendMessage HDM_GETITEMRECT.
8. DllCall ReadProcessMemory.
9. NumGet the structure data.
X. DllCall VirtualFreeEx e CloseHandle.

Example:

Code: Select all

#SingleInstance Force
#NoEnv

Capslock::
    MouseGetPos X, Y,, hWnd, 2
    WinGetClass Class, ahk_id %hWnd%

    If (Class != "SysHeader32") {
        MsgBox Move the mouse over a SysHeader32 control.

    } Else {
        a := GetRemoteHeaderItemsRect(hWnd)
        Loop % a.Length() {
            MsgBox % a[A_Index].X . " " . a[A_Index].Y . " " . a[A_Index].W . " " . a[A_Index].H
        }
    }
Return

GetRemoteHeaderItemsRect(hHeader) {
    WinGet PID, PID, ahk_id %hHeader%

    If !(hProc := DllCall("OpenProcess", "UInt", 0x438, "Int", False, "UInt", PID, "Ptr")) {
        Return
    }

    remote_item := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "UPtr", 16, "UInt", 0x1000, "UInt", 4, "Ptr")

    VarSetCapacity(RECT, 16, 0)

    DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &RECT, "uPtr", 16, "Ptr", 0)

    aRECT := []

    SendMessage 0x1200, 0, 0,, ahk_id %hHeader% ; HDM_GETITEMCOUNT
    Loop % (ErrorLevel != "FAIL") ? ErrorLevel : 0 {
        SendMessage 0x1207, A_Index - 1, remote_item,, ahk_id %hHeader% ; HDM_GETITEMRECT
        If (ErrorLevel && ErrorLevel != "FAIL") {
            DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &RECT, "uPtr", 16, "Ptr", 0)        
        }

        X := NumGet(RECT, 0, "Int")
        Y := NumGet(RECT, 4, "Int")
        W := NumGet(RECT, 8, "Int")
        H := NumGet(RECT, 12, "Int")

        aRECT.Push({"X": X, "Y": Y, "W": W, "H": H})
    }
    
    DllCall("VirtualFreeEx", "Ptr", hProc, "Ptr", remote_item, "UPtr", 0, "UInt", 0x8000) ; MEM_RELEASE
    DllCall("CloseHandle", "Ptr", hProc)

    Return aRECT
}
After that, you'll need to convert the coordinates.

SunAndSuch
Posts: 46
Joined: 05 Oct 2015, 12:11

Re: How can I get the text of the column header of a SysListView32?

Post by SunAndSuch » 04 Aug 2018, 14:55

Thank you, that works.
Windows 10, Ahk v1 x64-bit.

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How can I get the text of the column header of a SysListView32?

Post by jeeswg » 04 Aug 2018, 16:53

- My JEE_LVGetColOrder/JEE_LVSetColOrder functions should be working fine if anyone would be kind enough to test them.
- Note: save any files before testing on programs.
- Note: perhaps for some programs you must run them as admin.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

SunAndSuch
Posts: 46
Joined: 05 Oct 2015, 12:11

Re: How can I get the text of the column header of a SysListView32?

Post by SunAndSuch » 05 Aug 2018, 09:01

Here are some tests I have done. The result of:

Code: Select all

Loop,% (oRes:=JEE_LVGetColOrder(hCtl)).Length()
	vRes.=oRes[A_Index] "`t"
...is in the tooltip above each header. I have reordered (manually, dragging by mouse) the columns and then run the code again. Sometimes, the result didn't match the new column order. The programs tested, as they appear on the picture: 1.7zip 2.ControlPanelPrograms 3.Services 4.TaskManager 5.XnView
1.7zip_2.ControlPanelPrograms_3.Services_4.TaskMan_5.XnView.png
1.7zip_2.ControlPanelPrograms_3.Services_4.TaskMan_5.XnView.png (77.8 KiB) Viewed 2764 times
As for the administrator's rights... my user is a "Message Capture User"... whatever that means, so not sure if admin.
Windows 10, Ahk v1 x64-bit.

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: How can I get the text of the column header of a SysListView32?

Post by jeeswg » 05 Aug 2018, 10:29

- Many thanks for your tests.
- You would put this at the top of a script to give you admin rights, or right-click a file and choose 'Run as administrator'.

Code: Select all

if !A_IsAdmin
	Run, % "*RunAs " (A_IsCompiled ? "" : A_AhkPath " ") """" A_ScriptFullPath """"
- Thanks for giving me the idea of checking 7-Zip.
- I found the problem, this needed to be replaced. I've fixed the script above (on the previous page).

Code: Select all

;before:
vSize := vPIs64?40:28
;after:
vSize := vCountCol*4
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

SunAndSuch
Posts: 46
Joined: 05 Oct 2015, 12:11

Re: How can I get the text of the column header of a SysListView32?

Post by SunAndSuch » 05 Aug 2018, 13:06

jeeswg wrote:Many thanks for your tests.
You're welcome! And thank you for your help :). Now it works fine.
Windows 10, Ahk v1 x64-bit.

User avatar
andymbody
Posts: 902
Joined: 02 Jul 2017, 23:47

Re: How can I get the text of the column header of a SysListView32?

Post by andymbody » 25 Nov 2022, 14:34

FYI...

I was getting same text for each column (all would have the text of column 0), width for each column was correct...
... to get the code to work, I had to change the buffer size data type to an unsigned INT, rather than a pointer

From

DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "uPtr", cbHDITEM + MAX_TEXT_SIZE, "Ptr", 0)
DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_text, "Ptr", &HDText, "uPtr", MAX_TEXT_SIZE, "Ptr", 0)

to this

DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "Uint", cbHDITEM + MAX_TEXT_SIZE, "Uint*", 0)
DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_text, "Ptr", &HDText, "Uint", MAX_TEXT_SIZE, "Uint*", 0)

Win7, AHK 1.1.26

Hope this helps others...
Andy

teadrinker
Posts: 4331
Joined: 29 Mar 2015, 09:41
Contact:

Re: How can I get the text of the column header of a SysListView32?

Post by teadrinker » 25 Nov 2022, 16:16

andymbody wrote: to this

DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_item, "Ptr", &HDITEM, "Uint", cbHDITEM + MAX_TEXT_SIZE, "Uint*", 0)
DllCall("ReadProcessMemory", "Ptr", hProc, "Ptr", remote_text, "Ptr", &HDText, "Uint", MAX_TEXT_SIZE, "Uint*", 0)

Win7, AHK 1.1.26
It's not correct, see the description of ReadProcessMemory() here. SIZE_T in AHK is Ptr, not Uint.
AHK 1.1.26 is outdated, the current version is 1.1.35.00.

User avatar
andymbody
Posts: 902
Joined: 02 Jul 2017, 23:47

Re: How can I get the text of the column header of a SysListView32?

Post by andymbody » 26 Nov 2022, 04:40

It's not correct, see the description of ReadProcessMemory() here. SIZE_T in AHK is Ptr, not Uint.
That was the only way for me to get it working... BUT
AHK 1.1.26 is outdated, the current version is 1.1.35.00.
Once I upgraded... this fixed the issue...

Thank you for pointing this out to me!

Post Reply

Return to “Ask for Help (v1)”