Получить состояние элементов в CheckListBox с помощью API SendMessage - PullRequest
1 голос
/ 13 апреля 2020

Я использую программное обеспечение для манипулирования другим, например, средой тестирования программного обеспечения, с целью получения некоторых значений и выполнения некоторых задач. Для этого я использую SendMessage API, и он работает очень хорошо почти во всех Windows элементах управления, кроме CheckListBox. «Подчиненное» программное обеспечение было создано в Delphi, поэтому я не уверен, является ли CheckListBox стандартным Windows элементом управления, в любом случае, это документация от MS:
https://docs.microsoft.com/en-us/windows/win32/controls/list-boxes
В этом элементе управления я могу получить количество элементов, тексты, которые были выбраны, и много другой информации, но я не могу узнать, проверены элементы или нет. Я пытался использовать некоторые инструменты, такие как «Accessibility Insights for Windows» и AutoIt, но безрезультатно.
Есть идеи, как получить состояние флажков внутри Delphi CheckListBox?
Заранее спасибо .

Ответы [ 2 ]

2 голосов
/ 13 апреля 2020

Delphi сохраняет проверенную информацию в объекте TCheckListBoxDataWrapper. Указатель на этот объект хранится в обычном «элементе данных» элемента флажка. Затем этот объект имеет логическое свойство State, которое вы найдете по смещению 8.

Примечание. Если выясняется, что в конкретной версии Delphi вашей программы оно имеет смещение, отличное от 8 , попробуйте что-нибудь поблизости - 4, 12, ... - это не зашло далеко ...

Чтобы узнать, отмечен ли пункт, вам нужно:

  1. Получить указатель на TCheckListBoxDataWrapper этого предмета. Это можно сделать, отправив сообщение LB_GETITEMDATA. Если вы вернете ноль назад, он также будет считаться не проверенным.
  2. Поскольку этот указатель указывает на память в другом процессе, а не на ваш, вы не можете просто разыменовать его. Вместо этого вам нужно будет использовать ReadProcessMemory. Зная, что свойство State имеет смещение 8, вы можете вызвать ReadProcessMemory(hProcess, itemData + 8, &checked, 1, NULL), чтобы прочитать 1 байт в переменную checked. (Сначала нужно открыть целевой процесс, используя OpenProcess.)

Тогда вы получите проверенное состояние элемента в checked! 1 для проверенного, 0 для непроверенного.


В случае, если вам позже потребуется также получить доступ к какому-либо другому внутреннему состоянию, еще один совет: есть свойство окна ControlOfsXXXXXXXXYYYYYYYY с X HINSTANCE (базовый адрес) владельца окна (обычно 00400000) и Y - (шестнадцатеричный) идентификатор потока владельца окна (вы можете использовать GetWindowThreadProcessId для его получения). Вы можете использовать GetProp, чтобы получить значение этого свойства, которое будет указателем на управляющий объект (в этом примере - TCheckListBox). Затем вы можете поэкспериментировать с ReadProcessMemory, чтобы получить другие данные по мере необходимости. Вам нужно знать о смещениях (но вы можете использовать отладчик, чтобы попытаться выяснить их). Очень полезно скомпилировать тестовую программу с той же версией Delphi и выполнить там функции, которые обращаются к этим свойствам, тогда вы можете вместо этого отладить свою собственную тестовую программу и легче определить смещения.

Еще Продвинутый метод, который включает в себя вставку пользовательской библиотеки DLL (записанной в той же версии Delphi) в целевой процесс, позволяет более непосредственно получать доступ к данным (чтение и запись). Я написал статью об этом долгое время go.

1 голос
/ 14 апреля 2020

Как сообщает @CherryDT, это способ доступа к состоянию CheckListBox. Я использовал AutoIt для проверки наконечника, потому что на данный момент это самый быстрый способ проверить его.
Код:

;------------------------------------------------------------------------------
;   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
...