-
... в чужом процессе?
Начну с преамбулы.
В общем задача кликать и заполнять поля в чужом приложении. Приложение написано на Delphi 2006. Моя программа на Java (а точнее на Kotlin)
Сначала я думал, что это будет обычный набор SendMessage, но в процессе возникает TMessageForm (стандартные диалоги), а с помощью WinApi считать с них текст нельзя (разве что только Ctrl+C посылать). Плюс, наверняка нельзя понять какой Edit мне использовать, т.к. не к чему привязаться.
Немного покопав я смог залезть в процесс этого приложения (да да, на яве) и достать из него список компонентов и контролов формы с названием классов, именем и текстом, а также со списком дочерних контролов. Делаю это простым чтением памяти процесса и переходя по указателям.
Но вот знаний устройства классовой модели delphi не хватает: список контролов я могу получить, а понять есть ли у компонента этот самый список контролов или нет - не могу. И спрашиваю.
Пока есть еще такая идея: найти на форме все дочерние окна, на основе них составить список WinControl-ов, а потом просто проверять есть ли компонент в списке или нет. Может проще есть?
P.S. Занимаюсь этим очередным извращением на работе.
-
Use SendInput, Luke
Я несколько не понимаю, зачем тебе именно WinControl, обычно, для автоматизированного заполнения окон хватает API Windows. И да, на сайте есть много разделов форума и сообщения желательно писать не только в Прочее.
-
> DayGaykin © (12.08.17 21:24)
> проверять есть ли компонент в списке или нет. Может проще > есть?
Натравить на приложение Winsight (ws32.exe) или прочий Spy+.
Ну, или EnumChildWindows с получением классов окон и прочей информации, позволяющей идентифицировать окно ввода. Кста, это могут быть и координаты окна. Наверное, еще потребуется знать про юникодность.
Ну, а потом решать, чем именно заполнять, бо SendInput|keybd_event - оно совсем на край.
-- Regards, LVT.
-
> Кста, это могут быть и координаты окна. >
По координатам не очень удобно, потому что приложение "растягивается", а наблюдать за ним будут по RDP и я боюсь последствий разных разрешений.
> Winsight
У меня плохие ассоциации с этой программой. Я помню что она работала ровно один раз между перезагрузками:) Я Сашиной программой WndInfo пользуюсь
. > EnumChildWindows с получением классов окон
Я примерно так сделал. Получил список окон, потом с помощью user32.GetProp('Delphi...') получил указатель на экземпляр класса. А затем с помощью нескольких ReadProcessMemory считал имя компонента. Тем самым я получил список WinControl-ов на форме. Затем аналогичным образом я считываю список всех контролов формы (нахожу по смещение поле FControls и FWinControls и считываю).
Мне кажется я выбрал не самый простой путь, но да ладно.
Спасибо за помощь.
-
TWinControl это класс библиотеки VCL. В чужом процессе никаких TWinControl нету. Даже если чужая написана на Делфи - не факт что на абсолютно идентичной версии VCL.
Если вам надо работать с чужой программой просто берите EnumThreadWindows(), EnumChildWindows(), GetWindowRect(), GetClientRect(), GetClassName(), GetWindowText(), SendMessage(). Нафига вам ReadProcessMemory()??
-
> но в процессе возникает TMessageForm (стандартные диалоги), а с помощью WinApi считать с них текст нельзя
Я не помню как именно реализовано TMessageForm (не факт что все через "стандартные"), но на WinApi считать можно что угодно и откуда угодно.
-
> Я не помню как именно реализовано TMessageForm (не факт > что все через "стандартные"), но на WinApi считать можно > что угодно и откуда угодно
Попробуйте.
-
> Попробуйте.
Я пробовал, получилось.
-
> DayGaykin © (14.08.17 12:15) [3]
> Тем самым я получил список WinControl-ов на форме.
IMHO, перечисленное путем EnumChildWindows и есть TWinControl, бо у TControl окон нет (всякие AllocHwnd не в счет - они верхнего уровня).
> Мне кажется я выбрал не самый простой путь, но да ладно.
Мне не очень ясно, причем тут TMessageForm, т.к. у него Edit'ов нет, а лежащий на нем TLabel is TControl, т.е. читать его путем SendMessage никак не получится.
Про Edit мне непонятно, то, что как при таком уровне идентификации (есть доступ к самому экземпляру контрола) остаются к.л. трудности чтения/установки его текста.
-- Regards, LVT.
-
Была бы более конкретная постановка задачи - попробовал бы. А так не особо понятно - что именно пробовать-то...
-
> D7 (15.08.17 19:55) [9] > Была бы более конкретная постановка задачи - попробовал > бы. А так не особо понятно - что именно пробовать-то... > >
Сделай программу, состоящую из ShowMessage('Hello World'), запусти, а потом с помощью другой программы попробуй считать текст всплывшего окна.
> Я пробовал, получилось.
Если можно в двух словах, напишите, как?
> Мне не очень ясно, причем тут TMessageForm, т.к. у него > Edit'ов нет, > а лежащий на нем TLabel is TControl, т.е. читать его путем > SendMessage никак не получится.
Так мне как раз текст этого TLabel и нужно читать (в том числе), чтобы понять что у меня спрашивает программа.
> Про Edit мне непонятно, то, что как при таком уровне идентификации > (есть доступ к самому экземпляру контрола) остаются к.л. > трудности > чтения/установки его текста.
С Edit трудностей нет. WM_SETTEXT работает отлично.
Еще вопрос: На форме есть PageControl, при открытии вкладки он подгружает ее содержимое (создает вложенную форму). Но из своей программы я никак не могу открыть эту вкладу. TCM_SETCURSEL просто переключает "страничку", но сама страница не показывается. А пока она не показывается, у TabSheet нет окна, чтобы ему сделать Show.
TCM_GETITEMRECT, а потом кликнуть - тоже не работает, т.к. судя по всему Windows не "маршалит" "RECT structure" этого сообщения. Горячей клавиши на вкладке нет.
Подскажите, что можно сделать?
-
> Если можно в двух словах, напишите, как?
Через Clipboard
-
Ага. Имело место быть некоторое недопонимание. Из-за того что вопрос был:
> Как определить является ли класс потомком TWinControl...
А выяснилось что на самом деле нужно:
> Так мне как раз текст этого TLabel и нужно читать
А как раз TLabel же не является TWinControl, у него нет дескриптора. Оно является просто неким рисунком на клиентской области окна... Тогда да, уже сложнее, надо хорошенько подумать.
> А пока она не показывается, у TabSheet нет окна, чтобы ему сделать Show.
А вот TabSheet это уже TWinControl, у него есть дескриптор/окно. И всегда есть. Просто может оказаться что оно "NOT WS_VISIBLE", и всё что на нём лежит тоже всегда есть но может быть "NOT WS_VISIBLE". Попробуйте TCM_SETCURFOCUS или TCN_SELCHANGE.
-
Потыкал палочкой в VCL. Пробуйте вариант #1:
var NMHdr: tagNMHDR; begin NMHdr.hwndFrom:=PageControlHandle; SendMessage(NMHdr.hwndFrom, TCM_SETCURSEL, TabIndex, 0); NMHdr.code:=TCN_SELCHANGE; SendMessage(NMHdr.hwndFrom, WM_NOTIFY, NMHdr.hwndFrom, Integer(@NMHdr)); Пробуйте вариант #2:
SendMessage(PageControlHandle, TCM_SETCURFOCUS, TabIndex, 0);
Но если всё же требуется не "показывать/переключать", а просто доставать информацию - это и без листания вкладок можно, все контролы существуют и доступны, просто невидимые.
-
> DayGaykin © (16.08.17 20:27) [10]
> Если можно в двух словах, напишите, как?
Самое простое как Игорь написал. У TMessageForm есть метод WriteToClipBoard, вызываемый при нажатии ctrl-C.
> из своей программы я никак не могу открыть эту вкладу. TCM_SETCURSEL > просто переключает "страничку", но сама страница не показывается.
Видимо, создание вкладки просходит на OnChange, but MSDN "A tab control does not send a TCN_SELCHANGING or TCN_SELCHANGE notification message when a tab is selected using this message."
Похоже, что нужно как у D7 (16.08.17 23:14) [13] вариант #2
-- Regards, LVT.
-
> Игорь Шевченко © (16.08.17 20:48) [11] > > Если можно в двух словах, напишите, как? > Через Clipboard
В [0] я это упоминал.
> Похоже, что нужно как у D7 (16.08.17 23:14) [13] вариант > #2
> Пробуйте вариант #2: > ? > 1 > SendMessage(PageControlHandle, TCM_SETCURFOCUS, TabIndex, > 0);
TCM_SETCURFOCUS сработало. Спасибо.
-
Настоящее такое, от-душевное спасибо. Помогли не смотря на сумбурность изложения и сомнительность идеи.
-
Новая проблема: PopupMenu
После нажатия на определенную кнопку возникает меню, где мне надо выбрать второй пункт меню. Изучив немного понял, что нужно отправить WM_COMMAND окну, которое передается в параметре hwnd функции TrackPopupMenu. Изучив генофонд, увидел, что там специально созданное для этого окно и найти его не понятно как. Как все-таки нажать на второй пункт меню?
-
> DayGaykin © (17.08.17 20:04) [17]
> Как все-таки нажать на второй пункт меню?
Нажать-то просто: PostMessage(PopupList.Window), WM_COMMAND, PopupMenu1.Items[1].Command, 0);
Окно PopupList имеет класс TPUtilWindow. Однако, такой класс могут иметь и другие служебные окна, скажем, таймер.
Поэтому, придется хукнуть или сабклассировать все такие окна и смотреть, кто получает менюшные сообщения, а заодно прояснив IID пункта.
Возможно, что проще снова эмулировать нажатие стрелки вниз и ентер.
-- Regards, LVT.
-
-
> DayGaykin © (18.08.17 19:37) [19]
> - Нахожу окно #32768. > - Получаю по нему HMENU (MN_GETHMENU).
Хорошая находка. Записал в мемориз, спасибо.
-- Regards, LVT,
-
Вчера запустились. Всем спасибо за помощь!
-
P.S. Слава богу обошлось без DBGrig-ов :)
|