Найти окно и нажать кнопку

Модераторы: Duncon, Naeel Maqsudov, Игорь Акопян, Хыиуду

Shouldercannon
Сообщения: 74
Зарегистрирован: 08 июн 2008, 15:49

Если кнопка находится на Panel1 или PageControl1 и возмжно где-то ещё, то она не нажимается. Из-за чего это происходит?

Код: Выделить всё

  if FindWindow(nil, 'Form1') <> 0 then
  begin
    Wind := FindWindow(nil, 'Form1');
    Btn := FindWindowEx(Wind, 0, nil, 'Кнопка');
    SendMessage(Btn, BM_CLICK, 0, 0);
  end;
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

А панели это тоже окна, и кнопка находится на них а не на главном окне. Получается древовидная иерархия...
Shouldercannon
Сообщения: 74
Зарегистрирован: 08 июн 2008, 15:49

Нашёл пример на C, не могу переписать на Delphi
Кратко о навигации по окнам:
У каждого окна (формы) как правило есть дочерние окна, т.е. объекты, находящиеся на этой форме. Каждого из этих объектов могут быть свои дочерние объекты, а у тех свои и т.д. Вот пример иерархии вложенности:

...................Главная______фор ма..........................
.................../........................... \........................
............Панель N1 ................. Панель N2...............
.........../........... \.................. /.............\..............
....нопка N1 .... Кнопка N2 ... Панель N3 ... Кнопка N3
................................... ......./................................
................................... Кнопка N4..........................

Таким образом навигацию по окнам можно сравнить с навигацией по файловой системе.
Раз у вас уже есть хэндл (назовём его hMain) "Главной формы", то остаётся "добраться" до нужной кнопки. Сделать это можно так:

Напишем рекурсивную функцию, которая будет обходить наше "оконное дерево", ища кнопку с именем "Кнопка N3". Вид функции:
HWND Walk(HWND Current,char * Name);

Где-нибудь в программе делаем вызов target=Walk(hMain,"Кнопка N3").
Код:

Код: Выделить всё

HWND Walk(HWND Current,char * name)
{
  HWND temp;
  temp=GetWindow(Current,GW_CHILD); // находим первое дочернее окно
  while (temp!=NULL) // проверяем, а есть ли оно вообще
  {
     char WindowText[1024];
     GetWindowText(temp,WindowText,1023); // получаем его название
     if (!strcmp(name,WindowText)) return temp; // проверяем, искомая ли это кнопка, если да, то возвращаем её хэндл

     HWND result=Walk(temp,name); // если это была не она, то обходим её дочерние объекты

     if (result!=NULL) return result; // если при этом искомый объект был найден, возвращаем хэндл на него

     temp=GetWindow(temp,GW_HWNDNEXT) // иначе - проверяем следующий объект, дочерний для Current
  }
  return NULL; // объект не найден
}
Если в окне на который указывет хэндл hMain есть объект с именем "Кнопка N3", эта функция вернёт его хэндл, иначе - NULL.

Если обект найден, то на него осталось только нажать:
SendMessage(target,WM_LBUUTONDOWN,0 ,0);
И "отжать" его:
SendMessage(target,WM_LBUUTONUP,0,0 );
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

А в чем собственно проблема переноса?
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Можно воспользоваться именно дельфийными полями объектов TWinControl для обхода по дереву и поиску нужного элемента

Примерный РЕКУРСИВНЫЙ алгоритм обхода:

Код: Выделить всё

procedure PassChildren (pControl: TControl);
var i : integer;
    ChildControl: TControl;
begin
  if (not (pControl is TWinControl)) then exit;

  for I:= 0 to (pControl as TWinControl).ControlCount -1 do
  begin
    ChildControl := (pControl as TWinControl).Controls[I];
    if (ChildControl is TWinControl) then
    begin
      // Проверяем у элемента (ChildControl as TWinControl) - это нужный нам или нет
      // Если нужный, завершаем обход, т.е. НЕ вызываем PassChildren (ChildControl)

    end;
    PassChildren (ChildControl);
  end;
end;

Вызов, соответственно:
PassChildren (Form)

Если нужно искать окно по тексту, то вышеописанную пр-ру можно оформить как функцию, передавать туда параметром нужный текст, а возвращать ф-ия будет найденный элемент или NIL, если требуемое окно не найдено.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Например, поиск по значению Caption:

Код: Выделить всё

function FindChildrenByCaption (pControl: TControl; stCaption : string) : TControl;
var i : integer;
    ChildControl: TControl;
    vReqResult: TControl;
begin
  if (not (pControl is TWinControl)) then begin Result := NIL; exit; end;

  for I:= 0 to (pControl as TWinControl).ControlCount -1 do
  begin
    ChildControl := (pControl as TWinControl).Controls[I];
    if (ChildControl is TWinControl) then
    begin
     // Проверяем у элемента (ChildControl as TWinControl) - это нужный нам или нет
     // Если нужный, завершаем обход, т.е. НЕ вызываем PassChildren (ChildControl)
      if (ChildControl as TWinControl).Caption = stCaption) then
        begin Result := ChildControl; exit; end;
    end;
    vReqResult := PassChildren (ChildControl);
    if (vReqResult <> NIL) then  // нужный элемент найден в поддереве, возвращаем результат
      begin Result := vReqResult; exit; end;
  end;
 // Здесь, если в поддереве нужное окно не найдено, возвращаем NIL
  Result :=  NIL;
end;

Вызов, соответственно:
Btn := PassChildren (Form, 'Кнопка')
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

Ну я не думаю что человек пытается нажать кнопку в своем приложении )
Shouldercannon
Сообщения: 74
Зарегистрирован: 08 июн 2008, 15:49

BulldozerBSG писал(а):Ну я не думаю что человек пытается нажать кнопку в своем приложении )
Совершенно верно
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Shouldercannon писал(а):
BulldozerBSG писал(а):Ну я не думаю что человек пытается нажать кнопку в своем приложении )
Совершенно верно
В таком случае (если получается, что пользоваться надо WinAPI-функциями и известен хэндл окна основной формы), возможно, может помочь EnumChildWindows
dr.Jekill
Сообщения: 526
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

Вопрос решен?
Если даже handle не известен, всё равно WinApi поможет :) .
Тогда нужен хотя бы заголовок окна (или в крайнем случае уникальный заголовок какого-нибудь оконного компонента нужного окна).
Перебираем EnumWindows все окна. Она принимает параметром указатель на процедуру, которая вызывается каждый раз при нахождении очередного окна.
В ней можно вызывать уже перебор дочерних окон как советовал BBB.

Нет религии выше истины
Ответить