Дмитрий Богданов дата публикации 14-11-2003 13:33 Мечты вуайериста III
Лирическое отступление или два года спустя (вполне можно пропустить) |
Не ожидал, что мои графоманские экзерсисы вызовут неподдельный интерес у читающих и пишущих. К вящему моему сожалению проект работы с чужими окнами был надежно заброшен мной уже давно. Но материал был рассчитан на четыре части, а описано было только две.
Сразу оговорюсь. Текст программы по здравому размышлению я не приводил и не привожу по целому ряду причин:
- Написано все было для Дельфи 3, но некоторые системные библиотеки не имели нужного описания (например, некоторые библиотеки тот же common controls interface unit пришлось брать из 5) и были чуть исправлены мной, вспомнить, что и где правилось, предусмотреть к чему приведет изменение этих модулей, я просто не могу. Хотя у меня вроде как работает.
- Программа перегружена огромным количеством интерфейсного материала (кнопочки и прочь и прочь) разобраться какие компоненты и чьи я использовал тоже занятие долгое и муторное, а как все это будет работать без интерфейсных компонент, не знаю.
- Программы структурирована a la Турбо Паскаль (ну пишу я так) комментарии редки и малоинформативны для всех кроме меня. Это при том, что общей объем кода составляет что-то около 4000 строк без вспомогательных юнитов. Поверьте мне на слово разбираться гораздо проще по доведенным до ума статьям, чем по таким программам.
- И наконец программа так и не была закончена и многие заготовки вообще неизвестно что делают и для чего задумывались. Да и передавать столь сырой текст, считаю просто не этичным.
Честное слово. Если бы выложив программу, я кому-то помог, а не погрузил бы в бездну дополнительных сложностей я бы так и сделал с самого начала. Более того, если программа будет доведена до какого-нибудь внятного финала я убедительно попрошу разместить ее исходники рядом со статьями.
Если мои объяснения вас не убедили, покорнейше прошу прощения.
А вот тут можно поставить слово "Начало". |
Итак, мы разобрались с тем, что такое чужие окна. Как с ними работать и так далее. Это конечно интересно, но лично меня (да я думаю и еще многих, включая истинных вуайеристов) интересует не чужие окна как таковые, а то, что в них. Этот раздел посвящен как раз получению оконных элементов, коими являются кнопки, мемо, списки и прочие излишества. Оконные элементы так же называются дочерними окнами.
Будем считать, что прежде чем добраться до третьей части вы хотя бы бегло просмотрели первые две и знакомы с теми понятиями и соглашениями, которые в них были. Для работы с оконными элементами нам потребуются знания работы окон верхнего уровня, и за подробным разбором данного действа сразу отсылаю вас в предыдущие серии.
Все статьи были написаны только для Королевства Дельфи и я думаю, что вы сможете всегда найти их на этом замечательном сайте.
Как, зная описатель окна верхнего уровня, получить все его оконные элементы ? |
Как всегда первым с чего мы начнем, будет процедура получения списка элементов.
Как раньше мы использовали функцию FindWindow, будем использовать следующую функцию:
Функция FindWindowEx
- Синтаксис:
-
function FindWindowEx(hndParent,hndChild:HWnd; ClassName,WindowName:LpctStr): HWnd;
- Описание:
-
Находит дочернее окно принадлежащие окну с описателем hndParent с совпадающими ClassName и WindowName.
- Параметры:
-
- HndParent: Описатель родительского окна верхнего уровня
- HndChild: Описатель дочернего окна с которого начинается поиск
- ClassName: Имя класса окна (заканчивающееся пустым символом, 0 - если все классы).
- WindowName: Текстовый заголовок окна или 0, если все окна.
- Возвращаемое значение:
-
Описатель окна; 0 - если такого окна нет.
Простейшее получение списка элементов окна:
Procedure GetSubChild(wd:HWnD);
Var Cw:HWnd;
Begin
List1.Items.Clear;
If WD=0 then Exit;
Cw := FindWindowEx(Wd, 0, nil, nil);
while (Cw <> 0) do
begin
ListBox1.Items.Add(IntToStr(Cw));
Application.ProcessMessages;
Cw := FindWindowEx(Wd, Cw, nil, nil);
End;
End;
| |
Итак, это сработало, но уж очень криво. В зависимости от того, с каким окном вы работали. Результатом может быть от одного найденного элемента до всех найденных элементов.
В этом есть маленькая хитрость в работе с дочерними окнами. Они сами могут стать родительскими для оконных элементов. Например, на вашей форме лежит панель (TPanel) на которой, в свою очередь располагаются кнопки. В таком случае дочерним окном для формы будет являться именно панель. А кнопок приведенная выше процедура просто не найдет. Вот как все оказывается просто.
Модифицируем нашу программу, учитывая данную специфику. Для этого сделаем вызов процедуры рекурсивным
Рекурсивное получение списка элементов окна:
Procedure GetSubChild(wd:HWnD);
Var Cw:HWnd;
Begin
If WD=0 then Exit;
Cw := FindWindowEx(Wd, 0, nil, nil);
while (Cw <> 0) do
begin
ListBox1.Items.Add(IntToStr(Cw));
Application.ProcessMessages;
GetSubChild(Cw);
Cw := FindWindowEx(Wd, Cw, nil, nil);
End;
End;
| |
Как видно из текста, разница минимальна. Отмечу, что очищать список в процедуре нельзя иначе мы получим список, состоящий только из элементов последнего дочернего окна. Очищать список, в таком случае, нужно перед вызовом процедуры поиска оконных элементов.
И как всегда существует более изящный способ получения списка оконных элементов
Функция EnumChildWindows
- Синтаксис:
-
function EnumChildWindows(hndParent: HWnd; EnumFunc: TFarProc, lParam: Longint): Bool;
- Описание:
-
Перечисляет все дочерние окна , передавая функции обратного вызова ( т.е объявленной как stdcall функция) описатель окна и lParam. Перечисление заканчивается, если функция обратного вызова возвращает нуль или если перечислены все окна.
- Параметры:
-
- HndParent: Описатель окна верхнего уровня для которого ищутся элементы
- EnumFunc: Адpес экземпляpа пpоцедуpы функции обpатного вызова.
- lParam: Значение, пеpеданное функции обpатного вызова.
- Возвращаемое значение:
-
Не нуль, если пеpечислены все окна; 0 - в пpотивном случае
И опять необходима дополнительная функция для получения списка.
Правильное получение списка элементов окна:
function EnumProc (WD: HWnd; Param: LongInt): Boolean; stdcall;
Begin
List1.Items.Add(IntToStr(WD));
EnumProc:=True;
End;
procedure GetSubChild;
Begin
List1.Items.Clear;
EnumChildWindows (Wd,@EnumProc, 0);
End;
| |
Все как обычно… классы, названия и т.п. |
Вот и получен список оконных элементов для любого окна. Вернее самого списка мы так и не видим. Видим просто список описателей. Но мы уже продвинуты. Мы знаем что описатель - это все для оконного элемента. И что делать с ним, мы тоже знаем. Можем получить класс (все та же старая добрая функция GetClassName), можем узнать текст (GetWindowText). Только текстом будут совершено разные вещи. Для кнопки - название, а для большинства элементов пустая строка. Определение этих параметров все так же советую помещать непосредственно в функцию определения окна:
Получение информации об элементах окна
function EnumProc (WD: HWnd; Param: LongInt): Boolean; stdcall;
Var Nm:Array[0..255] of Char;
Cs: Array[0..255] of Char;
Begin
GetWindowText(Wd,Nm,255);
GetClassName(Wd,Cs,255);
List1.Items.Add(String(Nm)+'/'+String(Cs));
EnumProc:=True;
End;
procedure GetSubChild;
Begin
List1.Items.Clear;
EnumChildWindows (Wd,@EnumProc, 0);
End;
| |
Можно поэкспериментировать с определением стилей для оконных элементов, используя те же функции, которые мы использовали для определения стилей у окон верхнего уровня.
Очень полезными могут оказаться функции определяющие видимость/доступность оконного элемента (IsWindowVisible / IsWindowEnabled)
А вот то, чего не было у окон верхнего уровня, так это получения описателя родительского окна по описанию дочернего.
Функция GetParent
- Синтаксис:
-
function IsChild(WndParent,WndChild: HWnd): Bool;
- Описание:
-
Опpеделяет, является ли окно дочерним.
- Параметры:
-
- WndParent: Идентификатор окна претендующего на "отцовство".
- WndChild: Идентификатор дочернего окна.
- Возвращаемое значение:
-
Не нуль, если окно максимизировано; 0 - если нет.
И, наконец, несколько слов о классах. |
Как и с окнами верхнего уровня, классы оконных элементов могут быть совершено произвольными. Но стандартные типы остаются стандартными типами, их названия зачастую сохраняются.
Вот примерный список некоторых стандартных классов
'LISTBOX', 'COMBOBOX', 'MEMO', 'MAINMENU',
'EDIT', 'SCROLLBAR', 'BUTTON', 'LISTVIEW',
'STATIC', 'TREEVIEW', 'HEADER', 'TOOLBAR',
'STATUSBAR', 'TRACKBAR', 'UPDOWN', 'PROGRESS',
'TABCONTROL' 'RICHEDIT', 'POPUPMENU', 'CHECKBOX',
'LABEL', 'GAUGE'
| |
Кроме того, зачастую классы указывают на то, что имеют отношение к тому или иному стандартному классу, например класс ComboboxEx32 скорее всего имеет некоторое отношение к Combobox.
Все это достаточно важно, потому что методы работы и свойства оконных элементов зависят от типа классов.
В дополнение к списку окон мы научились получать список оконных элементов. Увидели что во многом информация, получаемая для окон верхнего уровня, может быть получена и для оконных элементов. Методы работы и функции, описанные в первой части, в большинстве случаев применимы и для оконных элементов. Можете попробовать скопировать оконный элемент в Bmp, используя нашу старую функцию,… это будет работать.
Список функций
- FindWindowEx
- EnumChildWindows
- GetParent
- IsChild
Список текстов
- Простейшее получение списка элементов окна
- Рекурсивное получение списка элементов окна
- Правильное получение списка элементов окна
- Получение информации об элементах окна
Дмитрий Богданов,
Специально для Королевства Delphi
[Окна, оконные сообщения] [Работа с контролами чужого приложения]
Обсуждение материала [ 12-01-2004 23:23 ] 4 сообщения |