Delphirus - прграммирование на delphi
   Все о delphi Delphirus - прграммирование на delphi
blocks.gif
Навигация
 

Главная
Статьи
Базы данных
Графика
Защита
Интернет
Система
Тексты
Мультимедиа
Файлы
Формы и окна
Другое
Советы
Базы данных
Графика
Интернет
Мультимедиа
Система
Тексты
Файлы
Файлы
Исходники
Компоненты
Инфо
Поиск по сайту
Обратная связь
Самое популярное
Аккаунт
Карта сайта

 
story.gif
Прогулка по окнам Windows
 
Формы и окна Всё или почти всё (хотя я не возьмусь сказать, что именно составляет исключение) в Windows имеет свой хэндл (Handle). Интересен перевод системой Сократ термина «Handle» — «Ручка», на нормальном техническом русском это будет дескриптор (определитель, идентификатор, описатель). Таким образом, handle — некий уникальный идентификатор любого ресурса Windows. Каждое окно имеет свой собственный дескриптор.

Иерархия окон в системе представлена таким образом:

  • каждое окно имеет список подчинённых окон. Список может быть пустым, в случае, если стиль окна не предусматривает хранения подчинённых элементов.
  • каждое окно имеет окно-владельца. Дескриптор окна-владельца будет нулевым (пустым), если окно имеет верхний уровень вложенности, например, главное окно программы.
  • для каждого окна можно получить слудующее и предыдущее, в его уровне вложенности, окно.

Мы имеем древовидную структуру с возможностью навигации по дереву, как вверх и вниз по уровню вложенности, так и горизонтально. Горизонтальная навигация возможна исключительно по окнам, имеющим то же окно-владелец. Рассмотрим простенькую схемку:

 

Для «Окна 3» — владелец «Окно 1», окна своего уровня — «Окно 3 …Окно N», и соответсвующие по рисунку, дочерние окна — «Окно N+1 … N+M». При этом, непосредственно получить идентификаторы других окон, используя идетификатор исходного окна невозможно.

Перейдём к иструментарию Windows API, позволяющему реализовать сказаное выше.

Оконные функции WinAPI, используемые в данном проекте.

function GetWindow(hWnd: HWND; uCmd: UINT): HWND;

Функция возвращает дескриптор окна, с заданным положением в иерархии окон относительно заданного окна.

  • hWnd — дескриптор исходного окна.
  • uCmd — направление связи, т.е. вверх, вниз или по горизонтали.

Значения переменной uCmd:

  • GW_CHILD — Возвращает дескриптор дочернего (подчинённого) окна, находящегося в верхней позиции Z-упорядочивания. В случае, если окно не имеет дочерних окон, возвращается 0.
  • GW_HWNDFIRST — Возвращает дескриптор окна, находящегося в верхней позиции Z-упорядочивания того же уровня, что и исходное окно.
  • GW_HWNDLAST — Возвращает дескриптор окна, находящегося в нижней позиции Z-упорядочивания того же уровня, что и исходное окно.
  • GW_HWNDNEXT — Возвращает дескриптор окна, находящегося в следующей позиции Z-упорядочивания того же уровня, что и исходное окно.
  • GW_HWNDPREV — Возвращает дескриптор окна, находящегося в предыдущей позиции Z-упорядочивания того же уровня, что и исходное окно.
  • GW_OWNER — Возвращает дескриптор окна владельца исходного окна. Если окно имеет нулевой уровень вложенности, возвращается 0.

О Z-упорядочивании: самое «верхнее» окно на экране имеет нулевую позицию, следующее, перекрываемое им окно - первую позицию, и так далее до самого «нижней» части экрана. Таким образом реализуется понятие трёхмерности, хотя в одной умной книжке я читал, что многооконная среда условно имеет 2.5-мерность (2.5D).

С помощью этой функции мы можем получить список всех окон системы несложной рекурсивной прогулкой по дереву (задача 1-го курса ВУЗа «Обход дерева»).

Нам понадобятся ещё несколько функций WinAPI:

function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer;

Функция получает текст окна по его идентификатору и возвращает считанную длину строки текста окна.

  • hWnd - идентификатор окна.
  • lpString - текст окна в переменной PChar. Переменную необходимо создать заранее.
  • nMaxCount - длина строки lpString.

function GetClassName(hWnd: HWND; lpClassName: PChar; nMaxCount: Integer): Integer;

  • hWnd - идентификатор окна.
  • lpClassName - имя класса окна в переменной PChar. Переменную необходимо создать заранее.
  • nMaxCount - длина строки lpClassName.

function GetMenu(hWnd: HWND): HMENU;
Функция возвращает идентификатор меню по переданному идентификатору окна.

Перейдём к непосредственному построению программы.

Построение программы.

Запустим Delphi и создадим новое приложение в меню File->New Application.

Не будем отвлекаться на навороченный интерфейс, не сомневаюсь, что большинство делает это легко, как «два байта переслать» ;). Построим что-нибудь а-ля "Проводник". Обзовём главную форму fmMain, а главный модуль - Main.pas. Разместим на форме компоненты как показано на рисунке:

 

Слева на форме находится компонент Tree:TTreeView, а клиентскую часть занимает компонент List:TListView.

Добавим в форму fmMain процедуру заполнения дерева FillTree следующего содержания:

// процедура заполнения дереваprocedure TfmMain.FillTree;var  h,h1:THandle;  Buffer:PChar;  // рекурсивная процедура заполнения дереваprocedure RegisterWindow(hWnd:THandle;ParentTreeItem:TTreeNode);var  Node:PNode; // данные окна  TreeNode:TTreeNode; // узел дерева  h:THandle; // хэндл окна, промежуточная переменнаяbegin  // получение и сохранение информации об окне  // создание переменной информации обокне  New(Node);  // присвоение дескриптора  Node^.Handle:=hWnd;  // получение текста окна  GetWindowText(hWnd,Buffer,256);  Node^.Text:=Trim(StrPas(Buffer));  // получение класса окна  GetClassName(hWnd,Buffer,255);  Node^.CName:=Trim(StrPas(Buffer));  // получение дескриптора меню окна  Node^.Menu:=GetMenu(hWnd);  // размещение в дереве  TreeNode:=Tree.Items.AddChild(ParentTreeItem,'');  // присвоение отображаемого текстового значения  if Node^.Text<>'' then TreeNode.Text:=Node^.Text    else if Node^.CName<>'' then TreeNode.Text:=Node^.CName      else TreeNode.Text:=IntToStr(Node^.Handle);  // сохранение ссылки на информацию об окне в соответствующем узле дерева  TreeNode.Data:=Node;  // Рекурсивные вызовы  // получение поддчинённых элементов  h:=GetWindow(hWnd,gw_Child);  if h<>0 then    RegisterWindow(h,TreeNode);  // получение элеметов того же уровня  h:=GetWindow(hWnd,gw_hWndNext);  if h<>0 then    RegisterWindow(h,ParentTreeItem);end;begin  // сообщение в строке статуса  Status.SimpleText:='Обновление информации об окнах ...';  Application.ProcessMessages;  // выключение отображения изменений дерева  Tree.Items.BeginUpdate;  // очистка дерева  Tree.Items.Clear;  // создание буфера для работы с PChar  Buffer:=StrAlloc(256);  // Получаем хэндл текущего окна  h1:=Handle;  repeat    // получаем хэндл окна-владельца    h:=GetWindow(h1,GW_OWNER);    // если не окно верхнего уровня, то ищем дальше    if h<>0 then begin      h1:=h;    end;  until h=0;  // находим первое верхнее окно  h:=GetWindow(h1,GW_HWNDFIRST);  // запуск рекурсии  RegisterWindow(h,nil);  // задание выделенного элемента дерева  Tree.Selected:=Tree.Items[0];  // включение отображения изменений дерева  Tree.Items.EndUpdate;  // удаление буфера для работы с PChar  StrDispose(Buffer);  // сообщение в строке статуса  Status.SimpleText:='Готово.';end;

Теперь попробуем в ней разобраться (смотри по тексту процедуры). Status — класс TStatus Bar. Сообщим в строке статуса, что мы обновляем информацию. Выключим отображение обновления дерева для того, чтобы ничего в процессе обновления у нас на экране не дёргалось. Очистим дерево и создадим переменную Buffer, в которую будем записывать строковые результаты выполнения функций WinAPI.

Следующий этап - добраться до самого верхнего и самого первого окна системы. Мы вызываем функцию GetWindow с параметром GW_OWNER — получение окна-владельца, начиная с главного окна программы. Получив окно, не имеющее владельца, находим для этого окна окно с нулевым Z-порядком, вызвав функцию GetWindow с параметром GW_HWNDFIRST — получение первого окна в данном уровне вложенности. Теперь мы можем начать обход дерева окон, начиная с самого первого по порядку возрастания вложенности и Z-порядка, вызвав рекурсивную процедуру RegisterWindow.

Как известно, у класса TTreeNode — узла дерева — есть поле Data:Pointer. В это поле можно записывать любые ссылки на данные, которые требуется ассоциировать с узлом дерева. Создадим структуру данных для хранения информации об окне.

// тип для хранения информации об окнеTNode=record  Handle:THandle; // хэндл окна  Text:string[255]; // текст окна  CName:string[255]; // имя класса  Menu:HMenu; // хендл менюend;PNode=^TNode; // указатель на структуру

При нахождении очередного окна будем создавать динамическую переменную типа PNode и её значение записывать в поле TTreeNode.Data. Итак, мы знаем «первое верхнее» окно. Передадим его в процедуру RegisterWindow. Также в эту процедуру передаётся ссылка на узел дерева, к которому добавляются узлы подчинёных окон. В первом случае вызова рекурсии, эта ссылка равна nil.

Следующие действия просты до безобразия (процедура RegisterWindow):

  • создать переменную описания окна;
  • получить по заданному идентификатору описанными ранее функциями WinAPI текст, имя класса и идентификатор меню, и записать эти значения в переменную окна;
  • создать узел дерева и присвоить ему перменную окна, задав владельца узла и присвоив ему текстовое значение;
  • получить идентификатор дочернего окна, и если идентификатор не равен 0, рекурсивно его обработать, передав в RegisterWindow полученные параметры. При этом, в процедуру передаётся идентификатор полученного дочернего окна и только что созданный узел дерева;
  • получить идентификатор следующего за текущим окна того же уровня вложенности. И если он не равен 0, передать в RegisterWindow полученный идентификатор и внешний параметр ParentTreeItem. При этом узлы дерева будут добавляться на том же уровне, что и текущий узел.

Если мы вызовем процедуру FillTree в событии FormCreate, то дерево автоматически заполнится при запуске программы. Создадим действие acRefresh:TAction и свяжем его с кнопкой обновления, расположенной на форме. В обработке запуска действия напишем такой код:

// действие "обновить"procedure TfmMain.acRefreshExecute(Sender: TObject);begin  // заполнение дерева  FillTree;end;

Теперь мы можем принудительно обновлять список. Перейдём к отображению всей собранной информации об окне в компоненте List:TListView. Обработаем событие TTreeView.OnChange так:

// событие изменения выделенного элемента дереваprocedure TfmMain.TreeChange(Sender: TObject; Node: TTreeNode);// добавление в List строкиprocedure AddItem(Name,Value:String);var  Item:TListItem;begin  Item:=List.Items.Add();  Item.Caption:=Name;  Item.SubItems.Add(Value);end;begin  // выключение обновления  List.Items.BeginUpdate;  // очистка  List.Items.Clear;  // добавление элементов  if (Node<>nil) and (Node.Data<>nil) then begin     AddItem('Текст',TNode(Node.Data^).Text);    AddItem('Класс',TNode(Node.Data^).CName);    AddItem('Дескриптор окна',IntToStr(TNode(Node.Data^).Handle));    AddItem('Дескриптор меню',IntToStr(TNode(Node.Data^).Menu));  end;  // включение обновления  List.Items.EndUpdate;end;

На выделение соответсвующего элемента дерева (Node:TTreeNode), добавляем в компонент List:TListView строки со значениями всех полей записи PNode^.

Всё!

Мы имеем информацию о всех окнах и используя идентификатор окна можем получить доступ к ОЧЕНЬ БОЛЬШОМУ КОЛИЧЕСТВУ параметров окна через WinAPI

И зачем всё это нужно?

Проиллюстрируем четырьмя примерами:

  • Спрятать ненужное (читаем, надоевшее) окно. Например, окно системы баннерных показов. Делается так:
    ShowWindow(TNode(Tree.Selected.Data^).Handle,sw_Hide).
    Правда эффективного скрытия этого окна нужно работать с собщениями cерии ABM_*, но это другая история.
  • Показать упрятанное окно. Обратное действие:
    ShowWindow(TNode(Tree.Selected.Data^).Handle,sw_Show).
    Например, слетела панель с кнопкой "Пуск". Все значки в системном трее пропали. Показать окно не представляет проблемы.
  • Разрешить запрещенный элемент управления:
    EnableWindow(TNode(Tree.Selected.Data^).Handle,True).
    Для чего это нужно, не будем даже и говорить.
  • Убить окно:
    PostMessage(TNode(Tree.Selected.Data^).Handle,wm_Close,0,0).
    Объяснение — см. пункт 3.

Источник www.delphi.aiq.ru

 
Разместил 10/02/2004 от rolcom ( Прочитано: )

  blocks.gif
Связанные ссылки
 

· Больше про Формы и окна
· Новость от rolcom


Самая читаемая статья: Формы и окна:
Окна - такие разные:круглые, треугольные, звездообразные...

 
blocks.gif
Рейтинг статьи
 

Средняя оценка: 4
Ответов: 4


Пожалуйста, проголосуйте за эту статью:

Отлично
Очень хорошо
Хорошо
Нормально
Плохо


 
blocks.gif
опции
 


 Напечатать текущую страницу  Напечатать текущую страницу

 Отправить статью другу  Отправить статью другу

 
 

Page generation 0.062 seconds