Прогулянка по вікнах windows

Прогулянка по вікнах 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 такого змісту:

Тепер спробуємо в ній розібратися (дивись по тексту процедури). Status - клас TStatus Bar. Повідомимо в рядку статусу, що ми оновлюємо інформацію. Вимкнемо відображення поновлення дерева для того, щоб нічого в процесі оновлення у нас на дисплеї не смикалося. Очистимо дерево і створимо змінну Buffer, в яку будемо записувати рядкові результати виконання функцій WinAPI.

Наступний етап - дістатися до самого верхнього і самого першого вікна системи. Ми викликаємо функцію GetWindow з параметром GW_OWNER - отримання вікна-власника, починаючи з головного вікна програми. Отримавши вікно, яке не має власника, знаходимо для цього вікна вікно з нульовим Z-порядком, викликавши функцію GetWindow з параметром GW_HWNDFIRST - отримання першого вікна в даному рівні вкладеності. Тепер ми можемо почати обхід дерева вікон, починаючи з самого першого по порядку зростання вкладеності і Z-порядку, викликавши рекурсивную процедуру RegisterWindow.

Докладніші відомості про процес прості до неподобства (процедура RegisterWindow):

  • створити змінну опису вікна;
  • отримати по заданому ідентифікатором описаними раніше функціями WinAPI текст, ім'я класу і ідентифікатор меню, і записати ці значення в змінну вікна;
  • створити вузол дерева і привласнити йому перменная вікна, задавши власника вузла і присвоївши йому текстове значення;
  • отримати ідентифікатор дочірнього вікна, і якщо ідентифікатор статті не дорівнює 0, рекурсивно його обробити, передавши в RegisterWindow отримані параметри. При цьому, в процедуру передається ідентифікатор отриманого дочірнього вікна і щойно створений вузол дерева;
  • отримати ідентифікатор наступного за поточним вікна того ж рівня вкладеності. І якщо він не дорівнює 0, передати в RegisterWindow отриманий ідентифікатор і зовнішній параметр ParentTreeItem. При цьому вузли дерева будуть додаватися на тому ж рівні, що і поточний вузол.
Якщо ми викличемо процедуру FillTree в подію FormCreate, то дерево автоматично заповниться при запуску програми. Створимо дію acRefresh: TAction і зв'яжемо його з кнопкою оновлення, розташованої на формі. В обробці запуску дії напишемо такий код: Тепер ми можемо примусово оновлювати список. Перейдемо до відображення всієї зібраної інформації про вікно в компоненті List: TListView. Опрацюємо подія TTreeView.OnChange так: На виділення відповідного елемента дерева (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.

Схожі статті