Мечты вуайериста — чужие окна 2
{codecitation class=»brush: pascal; gutter: false;» width=»600px»}
Теперь, попробуем менять, что-то в чужих окнах. И вообще попробуем сделать с ними то же, что мы делаем со своими окнами. Сразу скажу, что тема эта неисчерпаема. Ей можно посвятить не одно скромное обозрение, а детальное многотомное издание. Я не ставлю св оей задачей.
Несколько предварительных сурьезных слов.
Вынужден сказать, что многое изложенное ниже может привести к неприятным последствиям. Например, к тому, что программа или система будет зависать. Поэтому будем считать, что читатель находиться в трезвом уме и здравой памяти и не будет совершать необдуман ные действия. Прежде чем убить или закрыть чужое окно, подумайте, а зачем это окно вообще висит. Помните, что если окна висят в системе значит это кому то нужно ?! (почти Маяковский). Ну а теперь немного попугав для проформы перейдем к делу.
Содержание:
Несколько предварительных сурьезных слов.
Режимы отображение окон верхнего уровня.
Системное меню и кнопки заголовка.
Некоторые дополнительные возможности.
Итоги
Итак…
Режимы отображение окон верхнего уровня.
Давайте попробуем для начала сделать чужое окно активным (мы уже это делали, когда спасали чужое окно в картинку). Для этого можно использовать одну из следующих функций:
Функция SetForegroundWindow
Синтаксис:
function SetForeGroundWindow(Wd: Hwnd):Boolean;
Описание: Показывает верхние окно системы.
Параметры:
Wnd: Идентификатор окна.
Возвращаемое значение: True- если функция отработала, False- при ошибке.
Процедура BringWindowToTop
Синтаксис:
procedure BringWindowToTop(Wnd: HWnd);
Описание: Активизирует и перемещает Wnd в вершину стека перекрывающихся окон.
параметры:
Wnd: Всплывающее или дочернее окно.
Возвращаемое значение: Нет
Теперь попробуем проделать с неким окном, имеющим идентификатор окна HD:HWnd некие стандартные действия:
1) Свернуть данное окно;
2) Развернуть данное окно;
3) Закрыть данное окно.
Все данные действия могут быть проделаны с окном при помощи стандартной функции SendMessage или PostMessage, с различными параметрами:
1) SendMessage(HD,WM_SYSCOMMAND,SC_MINIMIZE,0);
2) SendMessage(HD,WM_SYSCOMMAND,SC_MAXIMIZE,0);
3) SendMessage(HD,WM_SYSCOMMAND,SC_CLOSE,0);
Существуют и другие константы, для сообщений вида WM_SYSCOMMAND:
SC_CLOSE Закрывает окно.
SC_CONTEXTHELP Изменяет курсор на вопросительный знак.
SC_DEFAULT Выбирает элемент по умолчанию; эмулирует двойное нажатие на Системное меню.
SC_HOTKEY Инициирует окно, связанное с текущим — указанной комбинацией горячих клавиш.
SC_HSCROLL Прокручивается горизонтально окно.
SC_KEYMENU Открывает Системное меню как результат нажатия клавиши.
SC_MAXIMIZE (или SC_ZOOM) Разворачивает окно.
SC_MINIMIZE (или SC_ICON) Сворачивает окно.
SC_MONITORPOWER Устанавливает состояние дисплея.
SC_MOUSEMENU Открывает Системное меню как результат щелчка мыши.
SC_MOVE Перемещает окно.
SC_NEXTWINDOW Переходит к следующему окну.
SC_PREVWINDOW переходит к предыдущему окну.
SC_RESTORE Восстанавливает окно к его нормальной позиции и размеру.
SC_SCREENSAVE Запускает стандартный скринсейвер.
SC_SIZE Задает размеры окно.
SC_TASKLIST Выполняет или инициирует Windows Task Manager.
SC_VSCROLL Прокручивается окно вертикально.
Первый параметр — описатель искомого окна, второй сообщение (в нашем случае WM_SYSCOMMAND) третий одна из констант приведенных выше, четвертый параметр — координаты (x- младшее слово y — старшее).
Можно, так же, показать или скрыть окно, используя функцию API:
Процедура ShowWindow
Синтаксис:
function ShowWindow(Wnd: HWnd; CmdShow: Integer);
Описание: отображает или прячет окно образом, указанным параметром CmdShow.
параметры:
Wnd: Всплывающее или дочернее окно.
CmdShow — одна из констант:
SW_HIDE
SW_MAXIMIZE
SW_MINIMIZE
SW_RESTORE
SW_SHOW
SW_SHOWDEFAULT
SW_SHOWMAXIMIZED
SW_SHOWMINIMIZED
SW_SHOWMINNOACTIVE
SW_SHOWNA
SW_SHOWNOACTIVATE
SW_SHOWNORMAL
Возвращаемое значение: Не нуль, если окно было ранее видимым; нуль — если оно было ранее спрятанным.
Константы позволяют скрыть/показать окно с различными типами (распахнутым, свернутым, неактивным и пр.)
Давайте теперь попробуем решить ряд наиболее часто встречающихся проблем:
1) Как свернуть все окна системы ??? (как свернуть все окна системы кроме окна программы)
// Любимая наша процедура.
{Для того чтобы использовать данный пример необходимо наличие кнопки Button1.}
function EnumMiniProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Begin
If WdForm1.Handle then // если это не наша программа
If IsWindowVisible(WD) then // если окно видимо
If not IsIconic(WD) then // если окно не свернуто
If isWindow(WD) then // и вообще это — окно.
ShowWindow(WD, SW_MINIMIZE); // свернем его.
EnumProc := TRUE; // продолжаем перебирать все окна системы.
end;
procedure TForm1.Button1Click(Sender: : TObject); // допустим, закрываем по нажатию на клавишу
begin
EnumWindows (@EnumMiniProc, 0); // отрабатываем сворачивание окон.
end;
Для того чтобы окно программы тоже сворачивалось достаточно убрать строку If WdForm1.Handle then в EnumMiniProc
Конечно, можно поставить еще массу условий, по которым будут минимизироваться окна, но это уже дело конкретной задачи.
Еще один пример, который бывает зачастую нужен:
2) Как закрыть (или постоянно закрывать) окна, например содержащие в заголовке подстроку «Реклама»
Закрыть все окна, содержащие определенную подстроку в заголовке.
Const
ReclamaName : String = ‘Реклама’ ; // строка, по которой мы узнаем, что это — реклама.
TimeInterval : Integer = 500; // Интервал, с которым будем проверять наличие окон
{Для того чтобы использовать данный пример необходимо наличие таймера Timer1.}
// Любимая наша процедура
function EnumCloseProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!!
Var
Nm:Array[0..255] of Char; // буфер для имени
zName:String;
Begin
GetWindowText(Wd,Nm,255); // считываем текст заголовка окна
ZName:=AnsiUpperCase(String(Nm)); // преобразуем к верхнему регистру т.е РЕКЛАМА
If Pos(ReclamaName,zName)0 then SendMessage(WD,WM_SYSCOMMAND,SC_CLOSE,0);
EnumProc := TRUE; // продолжаем перебирать все окна системы.
end;
procedure Tform1.Timer1Timer(Sender: TObject); // будем проверять по таймеру…
begin
Timer1.Interval:= TimeInterval; // установим время до следующего вызова
EnumWindows (@EnumCloseProc, 0); // отрабатываем закрытие окон.
end;
Понятно, что настоящая реклама не дает себе таких заголовков, но общий принцип останется тем же, а так попробуйте поискать общее в заголовках окна, названии классов окна и т.п. Кроме того, использование таймера чревато тем, что окон в системе очень много и за установленный интервал времени все окна не будут отработаны, это приведет к замедлению работы системы. Но решение данной подзадачки автор оставляет за читателем, благо особых сложностей с этим нет (увеличения интервала времени, установка логического условия о том, что проверка уже идет, вставка оператора Application.ProcessMessages и проч.)
На этом все возможности этих функций API не исчерпываются, но общий принцип отображения чужих окон, закрытия, перемещения и прокрутки изложен, дальше нужно от конкретной задачи.
Системное меню и кнопки заголовка.
Системное меню, отображает обычно ряд доступных стандартных функций применимых к окнам.
Обычно к таким функциям относятся следующие команды (применительно к локализованным Windows, в англоязычных названия будут другие, есть подозрения, что английские J):
Восстановить — восстанавливает размер окна.
Переместить — перемещает окно.
Размер — позволяет изменить размер окна.
Свернуть — сворачивает окно до иконки (минимизирует).
Развернуть — разворачивает окна до максимально возможного размера
Закрыть — закрывает окно.
Все эти команды, а так же ряд других (например, добавленных пользователем) доступны при нажатии на иконку, расположенную в левой части заголовка окна.
Ряд команд имеет кнопку, расположенную в правой части заголовка. Обычно таких кнопок три: свернуть, восстановить, закрыть. Иногда добавляется кнопка помощь.
Зачем манипулировать доступными командами системного окна ??? Ну, например, есть окошко, у которого кнопка закрыть — недоступна, а в системном меню пункта закрыть нет, да и на Alt F4 она не откликается. А убрать программку ужас как хочется.
Процедура GetSystemMenu
Синтаксис:
function GetSystemMenu(Wnd: HWnd; Revert: Bool): HMenu;
Описание: Считывает системное меню окна для копирования и модификации.
параметры:
Wnd: Всплывающее или дочернее окно.
Revent: Нуль, чтобы возвращался описатель для копирования системного меню, и не нуль, чтобы возвращался описатель исходного системного меню.
Возвращаемое значение: идентификатор системного меню;
0 — если Revert отлична от нуля и системное меню не модифицировано.
Для начала надо получить идентификатор системного меню. При помощи приведенной выше функции.
Далее попробуем определить, что именно содержится в системном меню (надо сказать, что приведенные ниже функции API справедливы для любых меню, а не только системных, но об этом несколько позже):
Процедура GetMenuString
Синтаксис:
function GetMenuString(Menu: HMenu; IDItem: Word; Str: PChar;
MaxCount: Integer; Flag: Word): Integer;
Описание: копирует метку элемента меню в Str. параметры:
Menu: идентификатор меню.
IDItem: идентификатор элемента меню.
Str: принимающий буфер.
MaxCount: размер буфера.
Flag: Одна из констант меню
mf_ByPosition — определять пункт меню по порядковому номеру
mf_ByCommand — определять пункт меню по выполняемой команде.
Возвращаемое значение: Количество реально скопированных байт.
Как видно из описания функции возможно два варианта определения списка по номеру или по выполняемой команде.
Если Flag = mf_ByCommand тогда в качестве IDItem передаются стандартные команды (см. константы в WM_SYSCOMMAND. Предыдущий раздел).
Например
I:=GetMenuString (hMenu, SC_CLOSE, Mn,255,mfByCommand);
Возвращает название пункта системного меню, отвечающего за закрытие окна. I=0 указывает, что такого пункта в системном меню нет.
Если Flag = mf_ByPosition тогда в качестве IDItem передается порядковый номер искомого пункта меню, начиная с 0
Например
I:=GetMenuString (hMenu, 0, Mn,255,mfByPosition);
Возвращает название самого первого по порядку пункта системного меню (обычно это восстановить). I=0 указывает, что такого пункта в системном меню нет. ИМХО первый вариант более пригоден для получения списка строк системного меню, в то время как второй — д ля определения присутствует ли данная команда в системном меню.
Количество элементов меню можно получить при помощи функции
Процедура GetMenuItemCount
Синтаксис:
function GetMenuItemCount(Menu: HMenu): Word;
Описание: определяет число меню и элементов меню верхнего уровня в указанном меню.
параметры:
Menu: идентификатор меню.
Возвращаемое значение: В случае успешного завершения возвращается число элементов меню; 0 — в противном случае.
Вот как приблизительно может выглядеть функция, которая определяет системное меню окна:
Получение списка системного меню окна.
…
ListBox1 : TlistBox; // Полученный список запихиваем сюда
… …
Procedure GetSysMenuItem (Wd:HWND); // Передаем идентификатор окна.
Var
I,K,Q:Word;
hMenuHandle : HMENU;
Nm:Array[0..255] of Char;
Begin
ListBox1.Clear; // Очистим список перед использованием.
hMenuHandle:=GetSystemMenu(Wd, FALSE); // Получим идентификатор
if (hMenuHandle = 0) then Exit; // Если такого меню нет, то выходим
Q:=GetMenuItemCount(hMenuHandle); // Определяем количество пунктов меню.
For k:=0 to Q-1 do
Begin
i:=GetMenuString(hMenuHandle,k,Nm,255,MF_BYPOSITION); // Считываем название
ListBox1.Items.Add(String(Nm)); // Добавляем в список.
End;
End;
Итак, мы получили список пунктов системного меню. Пустые строки, скорее всего, означают разделители. Так же используются акселераторы (