Прогулянка по вікнах 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 - напрямок зв'язку, тобто вгору, вниз або по горизонталі.
- 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.
- hWnd - ідентифікатор вікна.
- lpClassName - ім'я класу вікна в змінної PChar. Змінну необхідно створити заздалегідь.
- nMaxCount - довжина рядка lpClassName.
Функція повертає ідентифікатор меню по переданому ідентифікатором вікна.
Перейдемо до безпосереднього побудови програми.
Запустимо 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. При цьому вузли дерева будуть додаватися на тому ж рівні, що і поточний вузол.
Ми маємо інформацію про всіх вікнах і використовуючи ідентифікатор вікна можемо отримати доступ до ДУЖЕ ВЕЛИКИМ КІЛЬКІСТЮ параметрів вікна через 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.