· Выделить окно в списке и нажать ввод (enter)
· Выделить окно и выбрать пункт меню Spy->Open Detail
в результате которых появится окно с подробной информацией о выбранном окне, например, такое:
Для поиска окон предусмотрено два режима поиска: Find Window и Follow focus, которые запускаются из пункта меню Spy.
В режиме поиска окна (Find Window) нужно использовать кнопки вверх и вниз клавиатуры для выделения окна. Выберите окно в дереве окон, затем используйте кнопки со стрелочкам на клавиатуре для продвижения по дереву. Выделяемые окна в дереве окон будут помечены рамкой на экране для идентификации.
Для выхода из режима поиска окон нажмите любую кнопку мышки или клавишу Escape.
Режим следования за фокусом (Follow Focus) позволяет обнаруживать окна более простым способом – достаточно щелкнуть по интересующему окну и WinSight выделит это окно среди дерева окон.
Режим следования за фокусом остается включенным до тех пор, пока не будет явно выключен в меню Spy.
Для слежения за сообщениями необходимо указать, за какими окнами нужно следить в меню Messages – за всеми (All Windows) либо только за выделенными (Selected Windows). В этом меню также можно указать фильтр для сообщений (options) и процессов (process).
После нажатия на кнопку Start! (Пуск!) главного меню, программа начнет собирать сообщения, отображая их в соответствующем виде, включаемом в меню View. Сбор останавливается кнопкой Stop.
Сообщения, отправленные командой SendMessage, отображаются дважды: при отправке и при возвращении обратно.
Обработанные оконной функцией сообщения отображаются лишь один раз, так как их возвращаемые значения бессмысленны. Окно WinSight может быть разделено для того, чтобы отображать одновременно и дерево окон, и список сообщений.
Сообщения в окне Message View отображаются в следующем виде:
Дескриптор “Заголовок”-или-{Класс} Сообщение Статус
WinSight32 также отображает значки + и * перед сообщением для того, чтобы указать на возможность пропуска сообщений.
Близким аналогом рассмотренной программы является утилита Spy++, идущая в комплекте с Microsoft Visual Studio, в то время как WinSight идет в комплекте поставки сред программирования фирмы Borland.
Для управления сторонними приложениями (следующий раздел этой части) необходимо уметь определять заголовки и классы окон, контроль над которыми необходимо получить.
3.2. Приемы управления сторонними приложениями
Очень часто возникают ситуации, когда необходимо заставить некоторое стороннее приложение, исходный код которого недоступен и не экспортирует собственного API, выполнять некоторые действия. Одним из примеров является нажатие 500 раз на кнопку “Отмена” при установки ПО с некачественного диска. В другом примере, необходимо провести рутинную операцию по сглаживанию кривой, которая включает в себя команд – загрузить файл, построить график, выполнить сглаживание, экспортировать результат… Гораздо удобнее один раз нажать на кнопку и проделать эти операции автоматически. В этом разделе будет рассказано, каким образом можно подавать команды на различные управляющие элементы приложений.
В качестве примера, рассмотрим управление приложением, написанным на чистом API в предыдущей части “Создание …”.
Начать необходимо с того, чтобы найти дескриптор интересующего окна, что можно сделать с помощью функции FindWindow, которое находит первое попавшееся родительское окно верхнего уровня с совпадающими ClassName и WindowName. Не осуществляет поиск дочерних окон. Синтаксис этой функции в Делфи:
function FindWindow(ClassName, WindowName: PChar): HWnd;
где ClassName - имя класса окна (заканчивающееся пустым символом, nil - если все классы), WindowName - текстовый заголовок окна или nil, если все окна.
Функция возвращает 0, если окно не найдено, либо, в случае успеха, дескриптор окна.
Таким образом, для получения дескриптора необходимо указать имя класса окна и его заголовок. Если они неизвестны, то можно было бы запустить процедуру поиска с использованием функции EnumWindows, которая будет рассмотрена позже.
В нашем случае, зная заголовок и класс окна, можно обойтись следующим кодом:
program program_1;{$APPTYPE CONSOLE}uses Windows, SysUtils;const sClassName = 'myWindow';var handle: HWND;begin handle := FindWindow(sClassName, nil); if handle = 0 then Writeln('The window not found!') else Writeln('*> handle = ', handle); writeln('press enter'); readln;end.В данном случае нет необходимости указывать заголовок окна – оно однозначно определяется классом.
Приведем для справки функции, полезные при работе с окнами верхнего уровня:
Функция | Описание | Параметры | Возвращаемое значение |
GetClassName | Считывает имя класса окна. | Wnd: Идентификатор окна. ClassName: Буфеp для приема имени класса. MaxCount: Размер буфера | Фактическое число скопированных символов; 0 - если ошибка |
GetWindowText | Копиpует в Str заголовок окна или текст органа управления | Wnd: Идентификатор окна или органа управления. Str: Буфер, принимающий строку. MaxCount: Размер буфера Str. | Фактическое число скопированных байт или 0, если текст отсутствует |
GetWindowTextLength | Считывает длину заголовка окна или текста органа управления. | Wnd: Идентификатор окна | Длина заголовка окна в символах |
IsIconic | Определяет, является ли окно пиктограммой (минимизированным). | Wnd: Идентификатор окна | Не нуль, если минимизировано; 0 - если нет |
IsWindow | Определяет, является ли окно допустимым существующим окном. | Wnd: Идентификатор окна | Не нуль, если окно достоверно; 0 - если нет. |
IsWindowEnabled | Определяет, является ли окно разрешенным для ввода с мыши и с клавиатуры | Wnd: Идентификатор окна | Не нуль, если окно разрешено; 0 - если нет. |
IsWindowVisible | Определяет, сделано ли окно видимым функцией ShowWindow. | Wnd: Идентификатор окна | Не нуль, если окно существует на экране (даже если полностью закрыто); 0 - если нет |
IsZoomed | Определяет, является ли окно максимизированным. | Wnd: Идентификатор окна | Не нуль, если окно максимизировано; 0 - если нет |
GetWindowRect | Считывает в ARect размерности ограничивающего прямоугольника окна (в координатах экрана). | Wnd: Идентификатор окна. Rect: Пpинимающая стpуктуpа TRect. | Не используется |
GetClientRect | Функция возвращает размер рабочей области окна (уже без заголовка, меню и т.д.) в глобальных экранных координатах | Wnd: Идентификатор окна. Rect: Структура TRect для приема координат пользователя. | Не используется |
GetWindowLong | Считывает информацию об окне или о значениях дополнительного байта окна | Wnd: Идентификатоp окна. Index: Смещение в байтах или одна из следующих констант: GWL_EXSTYLE GWL_STYLE GWL_WNDPROC GWL_HINSTANCE GWL_HWNDPARENT GWL_ID GWL_USERDATA | Информация, характерная для окна |
GetClassLong | Считывает из структуры окна TWndClass со смещением Index длинное значение. Положительные смещения в байтах (с нуля) используются для доступа к дополнительным байтам класса. | Wnd: Идентификатор окна. Index: Смещение в байтах или константа … GCL_WNDPROC возвращает адрес стандартной процедуры окна | Считанное значение |
GetWindowThreadProcessId | Возвращает идентификатор процесса к которому принадлежит данное окно. Как правило, одинаков для всех окон приложения. | Wnd: Идентификатор окна lpdwProcessId : 32битное значение идентификатора процесса | Идентификатор потока |
Переберем теперь все окна системы и выведем их заголовки и классы в консольное окно. Для этого, заведем следующую вспомогательную функцию:
function EnumProc(h: HWND; param: longint): boolean; stdcall; var wnd_class, wnd_name: pchar; // буферы для класса и заголовка окна begin GetMem(wnd_name, 256); // резервируем память GetMem(wnd_class, 256); GetWindowText(h, wnd_name, 256); GetClassName(h, wnd_class, 256); writeln(k,') ',h,' "',wnd_name,'" {',wnd_class,'}'); inc(k); // k – глобальная переменная-счетчик result := true; FreeMem(wnd_name); // освобождаем память FreeMem(wnd_class); result := true; // продолжаем перебирать окна end;адрес которой будет передан API функции EnumWindows:
begin ... k := 1; EnumWindows( @EnumProc, 0 ); ...end.Результат работы консольного приложения (отрывок):
221) 66248 "SysFader" {SysFader}
222) 66106 "┬ёяы√тр■∙хх ьхэ■ ёхЄш" {ATL:701331A0}
223) 65860 "" {SynTrackCursorWindowClass}
224) 65812 "" {SynTrackCursorWindowClass}
225) 65788 "CL RC Engine3 Dummy Winidow" {CL RC Engine3 Dummy Winidow}
226) 65770 "" {tooltips_class32}
227) 131134 "CiceroUIWndFrame" {CiceroUIWndFrame}
228) 196642 "TF_FloatingLangBar_WndTitle" {CiceroUIWndFrame}
229) 66212 "" {tooltips_class32}
230) 131408 "╤яшёюъ с√ёЄЁющ тёЄртъш" {#32770}
231) 590728 "" {ComboLBox}
232) 393944 "" {WMDMNotificationWindowClass}
233) 131900 "DDE Server Window" {OleDdeWndClass}
234) 525194 "" {WMPMessenger}
Практически любые манипуляции с окнами и элементами управления выполняются путем отправки сообщений с помощью функции SendMessage и PostMessage. Отличие между этими функциями заключается в том, что функция SendMessage ожидает, пока адресат не обработает сообщение, в то время как PostMessage просто добавляет сообщение в очередь.
LRESULT SendMessage/PostMessage(
HWND hWnd, // дескриптор окна-адресата
UINT Msg, // посылаемое сообщение
WPARAM wParam, // первый параметр сообщения
LPARAM lParam // второй параметр сообщения
);
Например, рассмотренная выше функция GetWindowText эквивалентна посылке сообщения WM_GETTEXT.
Довольно часто используется сообщение WM_SYSCOMMAND, которое, позволяет выполнять различные операции в зависимости от значения параметра wParam:
· SC_CLOSE Закрывает окно.
· SC_CONTEXTHELP Изменяет курсор на вопросительный знак.
· SC_DEFAULT Выбирает элемент по умолчанию; эмулирует двойное нажатие на Системное меню.