Excel. Трюки и эффекты
Шрифт:
Листинг 10.2.
Добавление в дерево информации об окне и поиск дочерних окон
function NewWindow(wnd: HWND; param: LPARAM):BOOL; stdcall;
var
wndNode, parentNode: TTreeNode;
begin
wndNode := AddWindowToTree(wnd); //Добавление информации об
окне в дерево
//Перечисление дочерних окон
parentNode := enInfo.parent;
enInfo.parent := wndNode;
EnumChildWindows(wnd, Addr(NewWindow), param);
enInfo.parent := parentNode;
//Продолжать перечисление (после перечисления
//всех дочерних окон)
NewWindow := True;
end;
Используемая в листинге 10.3 функция AddWindowToTree добавляет элемент, соответствующий найденному окну, в дерево (определяет текст заголовка окна и имя оконного класса):
Листинг 10.3.
Добавление
function AddWindowToTree(wnd: HWND): TTreeNode;
var
caption, classname: String;
text: String;
node: TTreeNode;
begin
//Получение текста окна
SetLength(caption, SendMessage(wnd, WM_GETTEXTLENGTH, 0, 0) + 1);
SetLength(caption, SendMessage(wnd, WM_GETTEXT, Length(caption),
Integer(PAnsiChar(caption))));
//Имя класса окна
SetLength(classname, 1024);
SetLength(classname, GetClassName(wnd, PAnsiChar(classname),
100));
//Формирование текста для элемента и добавление его в дерево
text := \'"\' + caption + \'" \' + classname;
node := enInfo.tree.Items.AddChild( enInfo.parent, text );
node.Data := Pointer(wnd); //Не забываем запомнить
//дексриптор окна
AddWindowToTree := node;
end;
Вот, собственно, и все, что требуется для построения полного дерева окон, показанного на рис. 10.3.
Получение информации об окне
Следующей функцией «оконного шпиона» является определение более-менее полной информации об окне, выбранном в дереве. Форма с информацией о выделенном в дереве окне (в данном случае это пресловутая кнопка Пуск) показана на рис. 10.4.
Рис. 10.4. Форма свойств окна
Начинается все с того, что по команде меню Правка → Свойства вызывается метод ShowWindowProp созданного при запуске программы объекта f rmWindowProp. Этот метод принимает в качестве параметра дескриптор окна, информацию о котором нужно отобразить (дескриптор сохраняется в поле Data каждого элемента при построении дерева) (листинг 10.4).
Листинг 10.4.
Подготовка формы свойств выбранного окна
procedure TfrmWindowProp.ShowWindowProp(window: HWND);
begin
wnd := window;
LoadWindowInfo;
ShowModal; //Не забываем показать сами себя
end;
Переменная wnd, в которой сохраняется переданный BShowWindowProp дескриптор окна, является членом класса Tf rmWindowProp. Она нужна для того, чтобы другие методы формы Tf rmWindowProp могли получать доступ к дескриптору окна.
Определение заголовка, имени класса, идентификатора окна, а также области экрана, занимаемой окном, осуществляется в процедуре LoadWindowInf о (листинг 10.5).
Листинг 10.5.
Определение общей информации об окне
procedure TfrmWindowProp.LoadWindowInfo;
var
rect: TRect;
buffer: String;
begin
//Сбор сведений об окне
//..имя класса
SetLength(buffer, 1024);
SetLength(buffer, GetClassName(wnd, PAnsiChar(buffer), 1024));
txtClassName.Text := buffer;
//..имя (заголовок) окна
SetLength(buffer, SendMessage(wnd, WM_GETTEXTLENGTH, 0, 0) + 1);
SendMessage(wnd, WM_GETTEXT, Length(buffer),
Integer(PAnsiChar(buffer)));
txtWindowName.Text := buffer;
//..идентификатор (или дескриптор меню) окна
txtId.Text := IntToStr(GetWindowLong(wnd, GWL_ID));
//..оконный прямоугольник
GetWindowRect(wnd, rect);
txtWindowRect.Text :=
\'(\' + IntToStr(rect.Left) + \',\' + IntToStr(rect.Top) + \')\' +
\' – \' +
\'(\' + IntToStr(rect.Right) + \',\' + IntToStr(rect.Bottom) + \') \' +
IntToStr(rect.Right–rect.Left) + \'x\' + IntToStr(rect.Bottom –
rect.Top);
//Определение стиля окна
LoadWindowStyle;
LoadWindowExStyle;
end;
Если вы внимательно просмотрели листинг 10.5, то могли заметить вызовы двух процедур в двух последних строках кода. Процедура LoadWindowStyle заполняет списки используемых и доступных оконных стилей (см. рис. 10.4), а процедура LoadWindowExStyle соответственно заполняет списки используемых и доступных дополнительных (или расширенных) стилей окна.
Реализация процедуры LoadWindowStyle приводится в листинге 10.6
Листинг 10.6.
Заполнение списков оконных стилей
procedure TfrmWindowProp.LoadWindowStyle;
var
i: Integer;
style: DWORD;
begin
style := GetWindowLong(wnd, GWL_STYLE);
lstStyle.Clear;
lstAvailStyle.Clear;
//Выделение из 32-битного значения составляющих стиля окна
for i := 0 to 17 do
if styles[i].value and style <> 0 then
begin
//Стиль
lstStyle.Items.Add(styles[i].name);
styles[i].used := True;
end
else
begin
//Стиль не используется
lstAvailStyle.Items.Add(styles[i].name);
styles[i].used := False;
end;
end;
Вместо громоздкой проверки наличия в значении, возвращенном API-функцией GetWindowLong, битов каждого возможного стиля при помощи, например, case здесь используется глобальный массив styles структур Styleinf о. Объявление типа структуры (записи) Styleinf о выглядит следующим образом:
type
StyleInfo = record
value: DWORD; //Код стиля
name: String; //Текстовое обозначение стиля
used: Boolean; //Служебное поле
end;
Каждый элемент массива styles хранит информацию об определенном оконном стиле. Объявление этого массива, так же, как структуры Stylelnfo и прочих рассмотренных в этом разделе типов данных, находится в модуле WindowData, расположенном на диске в папке с номером главы.
Ниже приведено объявление массива styles (флаги стиля, являющиеся комбинацией других флагов, в массив не попали) (листинг 10.7).
Листинг 10.7.
Массив с информацией об оконных стилях
styles: array [0..17] of StyleInfo =
(
(value: WS_BORDER; name: \'WS_BORDER\'),
(value: WS_CAPTION; name: \'WS_CAPTION\'),
(value: WS_CHILD; name: \'WS_CHILD\'),
(value: WS_CLIPCHILDREN; name: \'WS_CLIPCHILDREN\'),
(value: WS_DISABLED; name: \'WS_DISABLED\'),
(value: WS_DLGFRAME; name: \'WS_DLGFRAME\'),
(value: WS_HSCROLL; name: \'WS_HSCROLL\'),
(value: WS_MAXIMIZE; name: \'WS_MAXIMIZE\'),
(value: WS_MAXIMIZEBOX; name: \'WS_MAXIMIZEBOX\'),
(value: WS_MINIMIZE; name: \'WS_MINIMIZE\'),
(value: WS_MINIMIZEBOX; name: \'WS_MINIMIZEBOX\'),
(value: WS_OVERLAPPED; name: \'WS_OVERLAPPED\'),
(value: WS_POPUP; name: \'WS_POPUP\'),
(value: WS_SYSMENU; name: \'WS_SYSMENU\'),
(value: WS_TABSTOP; name: \'WS_TABSTOP\'),
(value: WS_THICKFRAME; name: \'WS_THICKFRAME\'),
(value: WS_VISIBLE; name: \'WS_VISIBLE\'),
(value: WS_VSCROLL; name: \'WS_VSCROLL\')
);
Процедура LoadWindowExStyle реализована практически так же, как и процедура LoadWindowStyle. Только она заполняет cnncKHlstExStyle HlstAvailExStyle и обращается к массиву exstyles, а не styles. Поэтому приведем объявление только массива exstyles (листинг 10.8).
Листинг 10.8.
Массив с информацией о дополнительных оконных стилях
exstyles: array [0..18] of StyleInfo =
(
(value: WS_EX_ACCEPTFILES; name: \'WS_EX_ACCEPTFILES\'),
(value: WS_EX_APPWINDOW; name: \'WS_EX_APPWINDOW\'),
(value: WS_EX_CLIENTEDGE; name: \'WS_EX_CLIENTEDGE\'),
(value: WS_EX_CONTEXTHELP; name: \'WS_EX_CONTEXTHELP\'),
(value: WS_EX_CONTROLPARENT; name: \'WS_EX_CONTROLPARENT\'),
(value: WS_EX_DLGMODALFRAME; name: \'WS_EX_DLGMODALFRAME\'),
(value: WS_EX_LAYERED; name: \'WS_EX_LAYERED\'),
(value: WS_EX_LEFT; name: \'WS_EX_LEFT\'),
(value: WS_EX_LEFTSCROLLBAR; name: \'WS_EX_LEFTSCROLLBAR\'),
(value: WS_EX_MDICHILD; name: \'WS_EX_MDICHILD\'),
(value: WS_EX_NOACTIVATE; name: \'WS_EX_NOACTIVATE\'),
(value: WS_EX_NOINHERITLAYOUT; name: \'WS_EX_NOINHERITLAYOUT\'),
(value: WS_EX_NOPARENTNOTIFY; name: \'WS_EX_NOPARENTNOTIFY\'),
(value: WS_EX_RIGHTSCROLLBAR; name: \'WS_EX_RIGHTSCROLLBAR\'),
(value: WS_EX_STATICEDGE; name: \'WS_EX_STATICEDGE\'),
(value: WS_EX_TOOLWINDOW; name: \'WS_EX_TOOLWINDOW\'),
(value: WS_EX_TOPMOST; name: \'WS_EX_TOPMOST\'),
(value: WS_EX_TRANSPARENT; name: \'WS_EX_TRANSPARENT\'),
(value: WS_EX_WINDOWEDGE; name: \'WS_EX_WINDOWEDGE\')
);
Изменение оконных стилей
Изменение стилей окна «на лету» производится не сложнее, чем их определение: с помощью API-функций GetWindowLong и SetWindowsLong. Пример добавления флага, обозначение которого выбрано в списке доступных стилей, приводится в листинге 10.9.
Листинг 10.9.
Добавление оконного стиля
procedure TfrmWindowProp.cmbAddStyleClick(Sender: TObject);
var
style: DWORD;
addstyle: DWORD;
begin
if lstAvailStyle.ItemIndex = –1 then Exit;
//Удаление выбранного стиля окна
//..определяем, какой стиль удалить
addstyle := styles[GetStyleIndex(lstAvailStyle.ItemIndex,
False)].value;
//..вычисляем и устанавливаем новое значение стиля окна
style := GetWindowLong(wnd, GWL_STYLE);
style := style or addstyle;
SetWindowLong(wnd, GWL_STYLE, style);
//..перерисуем все окна
InvalidateRect(0, nil, True);
//Обновим список стилей окна
LoadWindowStyle;
end;