Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm using a software to manipulate another one, like a software tester environment, with the objective of getting some values and executing some tasks. For this I'm using SendMessage API and it's working very well in almost all Windows controls, except in the CheckListBox. The "slave" software was created in Delphi, so, I'm not sure if that CheckListBox is a standard Windows control, anyway, this is the documentation from MS:
https://learn.microsoft.com/en-us/windows/win32/controls/list-boxes
In this control I can get the quantity of items, the texts, which one is selected, and a lot of other info, but I can not get to know if the items are checked or not. I tried to use some tools like "Accessibility Insights for Windows" and AutoIt, but no results.
Any idea on how to get the state of the check boxes inside a Delphi CheckListBox?
Thanks in advance.
–
–
–
Delphi stores the checked information in a
TCheckListBoxDataWrapper
object. A pointer to that object is stored in the regular "item data" of checkbox item. This object then has a boolean property
State
which you'll find at offset 8.
Note: If it turns out that your program's particular Delphi version has it at a different offset than 8, try something nearby - 4, 12, ... - it can't have gotten far...
To find out if an item is checked, you'd need to:
Get a pointer to the
TCheckListBoxDataWrapper
of that item. This can be done by sending an
LB_GETITEMDATA
message. If you get zero back, it also counts as not checked.
Since this pointer points to memory in the other process and not yours, you can't simply dereference it. Instead, you'd need to use
ReadProcessMemory
. Knowing that the
State
property is at offset 8, you can call
ReadProcessMemory(hProcess, itemData + 8, &checked, 1, NULL)
to read 1 byte into variable
checked
. (You first need to open the target process using
OpenProcess
.)
Then you'll have the checked state of the item in
checked
!
1
for checked,
0
for unchecked.
In case you later need to access some other internal state as well, another hint: There is a window property
ControlOfsXXXXXXXXYYYYYYYY
with
X
being the
HINSTANCE
(base address) of the window owner (usually
00400000
) and
Y
being the (hex) thread ID of the window owner (you can use
GetWindowThreadProcessId
to obtain it). You can use
GetProp
to get the value of that property, which will be a pointer to the control object (in this example the
TCheckListBox
itself). You can then poke around with
ReadProcessMemory
to get other data as needed. You will need to know the offsets though (but you can use a debugger to try and figure them out). It is very helpful to compile a test program with the same Delphi version and execute functions there that access those properties, then you can debug your own test program instead and figure out the offsets more easily.
A more advanced method that involves injecting a custom DLL (written in the same Delphi version) into the target process allows for accessing (reading and writing) this sort of data more directly. I wrote
an article about that
a long time ago.
–
–
–
–
–
As posted by @CherryDT, that is the way to access the state of the CheckListBox. I used AutoIt to test the tip because at this moment this is the fastest way I have to test it.
The code:
;------------------------------------------------------------------------------
; Retuns the state of the indicated item in the CheckListBox control.
; Parameter:
; $iPID: process ID (PID)
; $hWnd: the handle of the CheckListBox control
; $iIndex: index of the item in the list (0 based)
;------------------------------------------------------------------------------
Func CtrlListBox_GetState($iPID, $hWnd, $iIndex)
Local $hProc ; Handle of the process.
Local $pItem ; Pointer to the item.
Local $pData = DllStructCreate("byte") ; Data structure.
Local $iQty ; Size of data read.
Local Const $LB_STATE_SHIFT = 8 ; State position in the memory.
$hProc = _WinAPI_OpenProcess(0x1F0FFF, False, $iPID) ; 0x1F0FFF = PROCESS_ALL_ACCESS
$pItem = _GUICtrlListBox_GetItemData($hWnd, $iIndex) + $LB_STATE_SHIFT
_WinAPI_ReadProcessMemory($hProc, $pItem, DllStructGetPtr($pData), DllStructGetSize($pData), $iQty)
Return DllStructGetData($pData, 1)
EndFunc
;------------------------------------------------------------------------------
; Retuns the state of the indicated item in the CheckListBox control.
; Parameter:
; $iPID: process ID (PID)
; $hWnd: the handle of the CheckListBox control
; $iIndex: index of the item in the list (0 based)
; $bState: state deseired (true or false)
;------------------------------------------------------------------------------
Func CtrlListBox_SetState($iPID, $hWnd, $iIndex, $bState)
Local $hProc ; Handle of the process.
Local $pItem ; Pointer to the item.
Local $pData = DllStructCreate("byte") ; Data structure.
Local $iQty ; Size of data read.
Local Const $LB_STATE_SHIFT = 8 ; State position in the memory.
if($bState <> 0) Then
DllStructSetData($pData, 1, True)
DllStructSetData($pData, 1, False)
EndIf
$hProc = _WinAPI_OpenProcess(0x1F0FFF, False, $iPID) ; 0x1F0FFF = PROCESS_ALL_ACCESS
$pItem = _GUICtrlListBox_GetItemData($hWnd, $iIndex) + $LB_STATE_SHIFT
_WinAPI_WriteProcessMemory($hProc, $pItem, DllStructGetPtr($pData), DllStructGetSize($pData), $iQty)
Return DllStructGetData($pData, 1)
EndFunc
I was facing the similar problem while trying to get the status of checkbox inside the CheckedListBox of UI developed in VB.Net. I wanted to get the status(check/Uncheck) so that I can execute required event from my test automation framework developer in Python + Winium. This was especially required to not accidently uncheck the already checked item.
During my research, I got to know that it is not easily possible using Winium so I added a button on the UI for resetting all the checkboxes before checking the required ones.
Hope it might help.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.