Конференция "WinAPI" » Как определить является ли класс потомком TWinControl...
 
  • DayGaykin © (12.08.17 21:24) [0]
    ... в чужом процессе?

    Начну с преамбулы.

    В общем задача кликать и заполнять поля в чужом приложении. Приложение написано на Delphi 2006.
    Моя программа на Java (а точнее на Kotlin)

    Сначала я думал, что это будет обычный набор SendMessage, но в процессе возникает TMessageForm (стандартные диалоги), а с помощью WinApi считать с них текст нельзя (разве что только Ctrl+C посылать). Плюс, наверняка нельзя понять какой Edit мне использовать, т.к. не к чему привязаться.

    Немного покопав я смог залезть в процесс этого приложения (да да, на яве) и достать из него список компонентов и контролов формы с названием классов, именем и текстом, а также со списком дочерних контролов. Делаю это простым чтением памяти процесса и переходя по указателям.

    Но вот знаний устройства классовой модели delphi не хватает: список контролов я могу получить, а понять есть ли у компонента этот самый список контролов или нет - не могу. И спрашиваю.

    Пока есть еще такая идея: найти на форме все дочерние окна, на основе них составить список WinControl-ов, а потом просто проверять есть ли компонент в списке или нет. Может проще есть?

    P.S. Занимаюсь этим очередным извращением на работе.
  • Игорь Шевченко © (12.08.17 21:53) [1]
    Use SendInput, Luke

    Я несколько не понимаю, зачем тебе именно WinControl, обычно, для автоматизированного заполнения окон хватает API Windows.
    И да, на сайте есть много разделов форума и сообщения желательно писать не только в Прочее.
  • Leonid Troyanovsky © (13.08.17 10:11) [2]

    > DayGaykin ©   (12.08.17 21:24) 

    > проверять есть ли компонент в списке или нет. Может проще
    > есть?

    Натравить на приложение Winsight (ws32.exe) или прочий Spy+.

    Ну, или EnumChildWindows с получением классов окон и прочей
    информации, позволяющей идентифицировать окно ввода.
    Кста, это могут быть и координаты окна.
    Наверное, еще потребуется знать про юникодность.

    Ну, а потом решать, чем именно заполнять,
    бо SendInput|keybd_event - оно совсем на край.

    --
    Regards, LVT.
  • DayGaykin © (14.08.17 12:15) [3]

    > Кста, это могут быть и координаты окна.
    >

    По координатам не очень удобно, потому что приложение "растягивается", а наблюдать за ним будут по RDP и я боюсь последствий разных разрешений.

    > Winsight

    У меня плохие ассоциации с этой программой. Я помню что она работала ровно один раз между перезагрузками:) Я Сашиной программой WndInfo пользуюсь

    .
    > EnumChildWindows с получением классов окон

    Я примерно так сделал. Получил список окон, потом с помощью user32.GetProp('Delphi...') получил указатель на экземпляр класса.  А затем с помощью нескольких ReadProcessMemory считал имя компонента.
    Тем самым я получил список WinControl-ов на форме.
    Затем аналогичным образом я считываю список всех контролов формы (нахожу по смещение поле FControls и FWinControls и считываю).

    Мне кажется я выбрал не самый простой путь, но да ладно.

    Спасибо за помощь.
  • D7 (14.08.17 20:50) [4]
    TWinControl это класс библиотеки VCL. В чужом процессе никаких TWinControl нету.
    Даже если чужая написана на Делфи - не факт что на абсолютно идентичной версии VCL.

    Если вам надо работать с чужой программой просто берите EnumThreadWindows(), EnumChildWindows(), GetWindowRect(), GetClientRect(), GetClassName(), GetWindowText(), SendMessage().
    Нафига вам ReadProcessMemory()??
  • D7 (14.08.17 20:53) [5]
    > но в процессе возникает TMessageForm (стандартные диалоги), а с помощью WinApi считать с них текст нельзя

    Я не помню как именно реализовано TMessageForm (не факт что все через "стандартные"), но на WinApi считать можно что угодно и откуда угодно.
  • DayGaykin © (14.08.17 23:14) [6]

    > Я не помню как именно реализовано TMessageForm (не факт
    > что все через "стандартные"), но на WinApi считать можно
    > что угодно и откуда угодно

    Попробуйте.
  • Игорь Шевченко © (15.08.17 10:36) [7]

    > Попробуйте.


    Я пробовал, получилось.
  • Leonid Troyanovsky © (15.08.17 10:56) [8]

    > 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]
    Была бы более конкретная постановка задачи - попробовал бы. А так не особо понятно - что именно пробовать-то...
  • DayGaykin © (16.08.17 20:27) [10]

    > 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" этого сообщения.
    Горячей клавиши на вкладке нет.

    Подскажите, что можно сделать?
  • Игорь Шевченко © (16.08.17 20:48) [11]

    > Если можно в двух словах, напишите, как?


    Через Clipboard
  • D7 (16.08.17 22:55) [12]
    Ага. Имело место быть некоторое недопонимание. Из-за того что вопрос был:

    > Как определить является ли класс потомком TWinControl...

    А выяснилось что на самом деле нужно:

    > Так мне как раз текст этого TLabel и нужно читать

    А как раз TLabel же не является TWinControl, у него нет дескриптора. Оно является просто неким рисунком на клиентской области окна...
    Тогда да, уже сложнее, надо хорошенько подумать.

    > А пока она не показывается, у TabSheet нет окна, чтобы ему сделать Show.

    А вот TabSheet это уже TWinControl, у него есть дескриптор/окно. И всегда есть. Просто может оказаться что оно "NOT WS_VISIBLE", и всё что на нём лежит тоже всегда есть но может быть "NOT WS_VISIBLE".
    Попробуйте TCM_SETCURFOCUS или TCN_SELCHANGE.
  • D7 (16.08.17 23:14) [13]
    Потыкал палочкой в 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);

    Но если всё же требуется не "показывать/переключать", а просто доставать информацию - это и без листания вкладок можно, все контролы существуют и доступны, просто невидимые.
  • Leonid Troyanovsky © (17.08.17 12:51) [14]

    > 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.
  • DayGaykin © (17.08.17 14:24) [15]

    > Игорь Шевченко ©   (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 сработало. Спасибо.
  • DayGaykin © (17.08.17 14:26) [16]
    Настоящее такое, от-душевное спасибо. Помогли не смотря на сумбурность изложения и сомнительность идеи.
  • DayGaykin © (17.08.17 20:04) [17]
    Новая проблема: PopupMenu

    После нажатия на определенную кнопку возникает меню, где мне надо выбрать второй пункт меню. Изучив немного понял, что нужно отправить WM_COMMAND окну, которое передается в параметре hwnd функции TrackPopupMenu. Изучив генофонд, увидел, что там специально созданное для этого окно и найти его не понятно как.
    Как все-таки нажать на второй пункт меню?
  • Leonid Troyanovsky © (18.08.17 11:17) [18]

    > 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).
    - Считываю элементы меню по HMENU.
    - Нужному пункту отправляю MN_BUTTONDOWN и MN_BUTTONUP.

    И уже после всего нашел http://www.delphikingdom.com/asp/answer.asp?IDAnswer=75873
  • Leonid Troyanovsky © (18.08.17 20:57) [20]

    > DayGaykin ©   (18.08.17 19:37) [19]

    > - Нахожу окно #32768.
    > - Получаю по нему HMENU (MN_GETHMENU).

    Хорошая находка.
    Записал в мемориз, спасибо.

    --
    Regards, LVT,
 
Конференция "WinAPI" » Как определить является ли класс потомком TWinControl...
Есть новые Нет новых   [86760   +22][b:0.001][p:0.002]