Что нового

[Элементы GUI] Чужой чекбокс недоступен

vitaliy4us

Новичок
Сообщения
158
Репутация
4
На форме приложения Делфи имеются чекбоксы. Win info видит их и распознает как CLASS:TFlatCheckBox. Отображается даже текст. А вот обратиться к чекбоксу (изменить его состояние или прочитать отмечен ли он) не получается. Не работает ни ClickControl, ни ControlCommand. В качестве идентификаторов пробовал использовать и CLASS с INSTANCE и текст контрола - ничего не работает. Единственное, чего удалось добиться, так это отметить (или снять отметку) чекбокс при помощи ControlSend, где в качестве строки передать Chr(32). Но это, в общем бесполезно, так как заранее не известно, что произойдет с контролом при этом (будут ли он отмечен, или наоборот - отметка снята) т.к. его состояние прочитать не получается. Можно ли как то лечить (может быть какие-то API или UDF)?


Добавлено:
Сообщение автоматически объединено:

А как вообще может быть, что для одного и того же чекбокса функция ControlGetText работает нормально, а ControlClick не работает?
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
vitaliy4us
Контролы всякие бывают, могут на стандартные действия не реагировать.
А размер и координаты получить удается? Тогда можно узнать, отмечено он или нет, например, с помощью PixelGetColor...
 

InnI

AutoIT Гуру
Сообщения
4,923
Репутация
1,432
Потому что это не стандартный контрол Microsoft, а самописный фирмы Borland. На какие-то сообщения, ему посылаемые, он отвечает, на какие-то - нет. Так решили разработчики. Работать с такими контролами приходится визуально: картинки, пиксели и т.д.
Кстати, на MouseClick(), мне кажется, он тоже должен реагировать. Но, как вы сказали "Но это, в общем бесполезно..."
 
Автор
V

vitaliy4us

Новичок
Сообщения
158
Репутация
4
Согласен со всем сказанным Вами, в силу особенностей данного элемента управления, остается только по пикселам определять его состояние, а кликать по координатам. Но хотелось бы узнать о возможностях применения API функций, которые могут этот чекбокс укротить. Например, то с чем не справляется AutoIt (в отношении элементов управления) может очень даже просто делать TestComplete.
 

Yuri

AutoIT Гуру
Сообщения
737
Репутация
282
vitaliy4us
Небольшой пример, возможно будет полезен
Демо простого скрипта на основе WinRAR (ru)
Ставим галки и читаем состояние - поставлена или нет
Код:
Run("C:\Program Files\WinRAR\WinRAR.exe") ;запустить WinRAR
$hWnd = WinWait("[CLASS:WinRarWindow]", "", 5) ;ожидать появления главного окна до 5 секунд
WinActivate($hWnd) ;активировать появившееся окно 
ControlSend($hWnd, "", "", "!a") ; !a = Ctrl + A = Команды - Добавить файлы в архив
$hWnd2 = WinWait("Имя и параметры архива", "", 5) ;ожидать появления дочернего окна до 5 секунд  
ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:8]", "Check") ;поставить галку - Создать SFX-архив
ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:11]", "Check") ;поставить галку - Добавить информацию для восстановления
ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:13]", "Check") ;поставить галку - Заблокировать архив

$Status1 = ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:8]", "IsChecked") ;состояние Создать SFX-архив
$Status2 = ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:11]", "IsChecked") ;состояние Добавить информацию для восстановления
$Status3 = ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:13]", "IsChecked") ;состояние Заблокировать архив
$Status4 = ControlCommand($hWnd2, "", "[CLASS:Button; INSTANCE:9]", "IsChecked") ;состояние Создать непрерывный архив

If $Status1 = 1 Then
   MsgBox(0, "Да", "Создать SFX-архив")
Else
   MsgBox(48, "Нет", "Создать SFX-архив")
EndIf

If $Status2 = 1 Then
   MsgBox(0, "Да", "Добавить информацию для восстановления")
Else
   MsgBox(48, "Нет", "Добавить информацию для восстановления")
EndIf

If $Status3 = 1 Then
   MsgBox(0, "Да", "Заблокировать архив")
Else
   MsgBox(48, "Нет", "Заблокировать архив")
EndIf

If $Status4 = 1 Then
   MsgBox(0, "Да", "Создать непрерывный архив")
Else
   MsgBox(48, "Нет", "Создать непрерывный архив")
EndIf

Еще есть такая штука InqSoft Window Scanner
В этой программе проверь, если получается ставить/снимать галки,
то ищи ошибки в скрипте.
 

InnI

AutoIT Гуру
Сообщения
4,923
Репутация
1,432
vitaliy4us [?]
может очень даже просто делать TestComplete
Ничего подобного. TestComplete с чужими приложениям работает также, как AutoIt - через WinAPI. Исключение составляют приложения, собранные со специальными заголовочными файлами TestComplete, и только в режиме отладки.
Поэтому, если TestComplete может прочитать состояние этого чекбокса, то и AutoIt может.
OffTopic:
Посмотрите в "Object Properties" TestComplete'а для этого чекбокса - есть ли свойство "wState"? Я думаю, что нет.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Незнаю как на счет TestComplete, через какие API он работает, но еще есть такая штука как Active Accessibility User Interface Services. С появлением возможности в Autoit создавать любые интерфейсы, можно запросто создать интерфейс IAccessible. Затем вы получаете для этого контрола или для всего окна объект IAccessible. (Поищите функцию _AccessibleObjectFromWindow) и дальше либо для контрола либо для дочерних элементов окна (через функцию _AccessibleChildren), вызываете метод get_accState - так вы получите статус этого чек бокса ( если статус будет STATE_SYSTEM_CHECKED - то галочка установлена. Все статусы можно глянуть здесь http://msdn.microsoft.com/en-us/library/windows/desktop/dd373609(v=vs.85).aspx). Ну а потом, можете отправлять enter или же вызывать метод accDoDefaultAction.

Когда я писал код, что бы переключатся по вкладкам internet explorera то делал все это, искал нужную вкладку и вызывал accDoDefaultAction. Вкладка становилась активной. Т.к действие по умолчанию для нее это "нажать". Если есть желание поглубже по разбираться в этом, то могу скинуть примеры.
 
Автор
V

vitaliy4us

Новичок
Сообщения
158
Репутация
4
Спасибо всем ответившим, буду разбираться. Отдельная благодарность Inververs за предложение по примерам. Буду благодарен, если отправите в личку.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Зачем в личку, мало ли кому еще понадобится:

Пример запускает калькулятор и выбирает шестнадцатеричный режим.
У меня windows 7, и калькулятор открывается "Программист", если у вас не так, поменяйте ControlGetHandle.


Код:
#include <winapi.au3>
#interface "IUnknown"
Local Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}"
; Definition
Local $dtagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _
		"AddRef dword();" & _
		"Release dword();"
;===============================================================================
;===============================================================================
#interface "IDispatch"
Local Const $sIID_IDispatch = "{00020400-0000-0000-C000-000000000046}"
; Definition
Local $dtagIDispatch = $dtagIUnknown & _
		"GetTypeInfoCount hresult(dword*);" & _
		"GetTypeInfo hresult(dword;dword;ptr*);" & _
		"GetIDsOfNames hresult(ptr;ptr;dword;dword;ptr);" & _
		"Invoke hresult(dword;ptr;dword;word;ptr;ptr;ptr;ptr);"
;===============================================================================
;===============================================================================
#interface "IAccessible"
Local Const $sIID_IAccessible = "{618736e0-3c3d-11cf-810c-00aa00389b71}"
; Definition
Local $dtagIAccessible = $dtagIDispatch & _
		"get_accParent hresult(ptr*);" & _
		"get_accChildCount hresult(int*);" & _
		"get_accChild hresult(int;int;int;int;ptr*);" & _
		"get_accName hresult(int;int;int;int;ptr*);" & _
		"get_accValue hresult(int;int;int;int;ptr*);" & _
		"get_accDescription hresult(int;int;int;int;ptr*);" & _
		"get_accRole hresult(int;int;int;int;ptr);" & _
		"get_accState hresult(int;int;int;int;ptr);" & _
		"get_accHelp hresult(int;int;int;int;ptr*);" & _
		"get_accHelpTopic hresult(ptr*;int;int;int;int;int*);" & _
		"get_accKeyboardShortcut hresult(int;int;int;int;ptr*);" & _
		"get_accFocus hresult(ptr);" & _
		"get_accSelection hresult(ptr);" & _
		"get_accDefaultAction hresult(int;int;int;int;ptr*);" & _
		"accSelect hresult(int;int;int;int;int);" & _
		"accLocation hresult(int*;int*;int*;int*;int;int;int;int);" & _
		"accNavigate hresult(int;int;int;int;int;ptr);" & _
		"accHitTest hresult(int;int;ptr);" & _
		"accDoDefaultAction hresult(int;int;int;int);" & _
		"put_accName hresult(int;int;int;int;ptr);" & _
		"put_accValue hresult(int;int;int;int;ptr);"
;===============================================================================


If Not WinExists('[CLASS:CalcFrame]') Then
	Run('calc.exe')
	WinWait('[CLASS:CalcFrame]')
EndIf
$hWnd = WinGetHandle('[CLASS:CalcFrame]')
$hCtrl = ControlGetHandle($hWnd, "", "[CLASS:Button; INSTANCE:1]")




$CHILDID_SELF = 0
$VT_I4 = 3
$tagVARIANT = "int[4];"
$tTabState = DllStructCreate($tagVARIANT)
$pTabState = DllStructGetPtr($tTabState)
$STATE_SYSTEM_CHECKED = 0x10
$OBJID_CLIENT = 0xFFFFFFFC

$hOLEACC = DllOpen("oleacc.dll")

$pAccessible = _AccessibleObjectFromWindow($hCtrl, $OBJID_CLIENT, $sIID_IAccessible)
$oAccObj = ObjCreateInterface($pAccessible, $sIID_IAccessible, $dtagIAccessible, False)

Local $vOut
$oAccObj.get_accName($VT_I4, $CHILDID_SELF, 0, 0, $vOut)
$sText = DllStructGetData(DllStructCreate("wchar[" & _WinAPI_StringLenW($vOut) & "]", $vOut), 1)
ConsoleWrite('> Имя: ' & $sText & @LF)


Local $vState
$oAccObj.get_accState($VT_I4, $CHILDID_SELF, 0, 0, $pTabState)
$vState = DllStructGetData($tTabState, 1, 3)
ConsoleWrite('> Статус: 0x' & Hex($vState, 8) & @LF)


If BitAND($vState, $STATE_SYSTEM_CHECKED) Then
	ConsoleWrite('+ Выбран' & @LF)
Else
	ConsoleWrite('- Не выбран. Выбираем..' & @LF)
	$oAccObj.accDoDefaultAction($VT_I4, $CHILDID_SELF, 0, 0)
EndIf
$pAccessible = 0 ;Если не присвоить 0 , то вылетит с ошибкой.
DllClose($hOLEACC)


Func _AccessibleObjectFromWindow($hWnd, $iObjId, $sRefIID)
	Local $aResult, $tRefIID
	$tRefIID = _WinAPI_GUIDFromString($sRefIID)
	$aResult = DllCall($hOLEACC, "long", "AccessibleObjectFromWindow", "hwnd", $hWnd, "uint", $iObjId, "ptr", DllStructGetPtr($tRefIID), "ptr*", 0)
	If @error Or $aResult[0] Then Return SetError(1, 0, 0)
	Return $aResult[4]
EndFunc   ;==>_AccessibleObjectFromWindow
 

InnI

AutoIT Гуру
Сообщения
4,923
Репутация
1,432
Не могу найти информацию по ключевому слову #interface :(

Уважаемый inververs. Ваш пример довольно интересен, но по представленному контролу информация легко получается и при помощи
Код:
ControlCommand($hWnd, "", $hCtrl, "IsChecked")
Перепробовал десяток Delphi-программ, начиная с OWL (BorRadio, BorCheck) и заканчивая последними (TTntGroupButton.UnicodeClass, TTntCheckBox.UnicodeClass). К сожалению, не нашёл ни одного, с которого бы невозможно было получить состояние при помощи ControlCommand().

Уважаемый vitaliy4us, не могли бы вы проверить код inververs на своём TFlatCheckBox ? Или сообщите, в каких (доступных) программах можно увидеть данный контрол.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
# - это внутренний коментарий. Так что #interface это обычный текст :smile:
 
Верх