简体   繁体   中英

Get the state of the items in a CheckListBox using SendMessage API

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:

  1. 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.
  2. 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)
   Else
      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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM