Вікна додатків в середовищі Windows

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Основи організації додатка в середовищі Windows

Отже, ми розглянемо основи оранізація додатка в середовищі Windows і відзначимо кілька нюансів:
Додаток в середовищі Windows, як і в середовищі DOS, містить так звану "головну функцію" (WinMain), що викликається при запуску програми. Додаток завершується практично при закінченні роботи функції WinMain.
Звичайно, хоча це і не обязятельно, функція WinMain реалізує наступну схему:
1) виконуються необхідні ініціалізаційний дії
2) створюється головне вікно програми, для чого часто реєструється новий клас вікон (віконна функція);
3) організовується цикл обробки повідомлень додатку. Зазвичай цикл завершується при закритті головного вікна додатка (не завжди)
4) після завершення циклу обробки повідомлень виконується "деініціалізацію" даних і звільнення зайнятих ресурсів, після чого функція WinMain () закнчівается.
Кілька зауважень:
Зауваження 1. Якщо додаток містить нетривалі (близько 1 сек.) Операції, які потребують взаємодії з користувачем (наприклад, тільки файл-орієнтований введення-виведення або настроювання іншої програми), то ці дії можуть бути виконані безпосередньо функцією WinMain () без створення вікон і без організації циклу обробки повідомлень.
Зауваження 2. У деяких випадках додаток може обійтися без реєстрації класу вікон та організації циклу обробки повідомлень, застосовуючи як головного вікна модальний діалог.
Зауваження 3. У момент виклику функції WinMain () їй, через аргументи, передається кілька параметрів, наприклад хендл копії додатку (hInstance). До виклику WinMain () додаток "не знає" цих даних. Тому можуть виникати складнощі з використанням статичних конструкторів об'єктно-орієнтровані мов (C + +).
Ця особливість, взагалі кажучи абсолютно неприродна. Справа в тому, що функція WinMain () викликається не безпосередньо середовищем Windows, а проміжним startup-кодом, що є частиною run-time бібліотеки (як і в DOS-додатках). Цей код ініціалізує стандартні змінні, купу, стек, обнуляє неініціаізірованние статичні дані та викликає конструктори статичних об'єктів до виклику функції WinMain ().
Windows викликає безпосередньо цей startup-код, передаючи йому потрібні дані через регістри. Тобто, в той момент, коли викликаються конструктори статичних об'єктів, параметри функції WinMain () вже відомі, і, більше того, вони навіть збережені в статичних змінних. Однак з незрозумілих міркувань ці змінні не декларовані як публічні і є локальними для startup-коду.
Зауваження 4. Цикл обробки повідомлень, у тому вигляді, який рекомендований проводами, не перевіряє наявність вікон у додатку. Для його завершення використовується повідомлення WM_QUIT, видобування якої з черги приводить до завершення циклу.
При цьому потрібно, що б повідомлення WM_QUIT надсилалося за допомогою функцій PostMessage (), PostAppMessage () або PostQuitMessage () (тільки тоді воно потрапляє в чергу додатка). Зазвичай це повідомлення надсилається при знищенні головного вікна програми (при обробці повідомлення WM_DESTROY спрямованого цьому вікну). У більш загальному випадку мається на увазі останнє вікно програми.
Ви зобов'язані самі передбачити кошти для посилки повідомлення WM_QUIT, так як не один стандартний обробник НЕ посилет його. Звичайно, Ви можете передбачити власні, альтернативні методи для переривання циклу обробки повідомлень. Так, наприклад, Ви можете в циклі обробки повідомлень перевіряти коректність хендла головного вікна:
while (IsWindow (hMainWnd)) {
if (! GetMessage (& msg, NULL, NULL, NULL)) break;
TranslateMessage (& msg);
DispatchMessage (& msg);
}
Якщо цикл обробки повідомлень не буде перерваний за знищення останнього вікна програми, то додаток залишиться активним, а у Вас вже не буде коштів для його завершення, крім виходу з Windows. При цьому Ваше додаток зникне із списку додатків Task Manager (цей список, взагалі кажучи, містить не завдання, а головні вікна додатків).
Зауваження 5. Windows не є об'єктно-орієнтованої середовищем. Хоча вікно і може бути названо об'єктом ООП, але лише з достатньою натяжкою. Найсуттєвіша відмінність вікна в Windows від об'єкта ООП полягає в тому, що повідомлення, яке обробляється віконної функцією, в багатьох випадках не виконує дій, а є "інформативним", вказуючи на те, що над вікном виконується та чи інша операція какіой-якої зовнішньої функцією .
Пояснимо це на прикладі створення вікна. У випадку "чистого" ООП для створення об'єкта він повинен отримати повідомлення "create", обробка якого призведе до його ініціалізації. У Windows повідомлення WM_CREATE не виконує ніяких функцій щодо створення вікна. Воно тільки інформує вікно про те, що в цей час вікно створюється засобами звичайної функціональної бібліотеки, наприклад за допомогою функції CreateWindowEx (). Ви можете взагалі ігнорувати це повідомлення, повертати будь-який результат, викликати або не викликати функцію обробки за замовчуванням - вікно все одно буде створено.
Якщо б всі повідомлення, одержувані вікном були тільки інформаційними, то до цього легко можна було б пристосуватися. Однак для деяких повідомлень повинна виконуватися обробка за умовчанням, якщо Ви її не виконали самі, а для інших така обробка повинна виконуватися обов'язково, навіть якщо Ви вже обробили це повідомлення. Все це помітно ускладнює написання додатків в середовищі Windows.
Ситуація додатково ускладнюється тим, що в документації як правило нічого не повідомляється про те, яка обробка повідомлення виконується за замовчуванням і, крім того, за деякими повідомленнями наводяться некоректні (або неповні) відомості про їх параметрах, виконуваних функцій, умовам виникнення і повернутому результаті. Help, супроводжуючий компілятори Borland найповніший із всіх (але не вичерпний).
Для вікон, що використовують як процедури обробки повідомлень за замовчуванням не DefWindowProc (), а іншу функцію (наприклад, DefMDIChildProc ()), можна уточнити список повідомлень обов'язково підлягають обробці за замовчуванням. Однак це уточнення стосується тільки тих повідомлень, обробку яких для DefWindowProc () можна ігнорувати, а для інших функцій не можна, список ж того, що можна ігнорувати для DefWindowProc (), а що не можна залишається невідомим.
Зауваження 6. У Windows існує певна плутанина термінів. Спробуємо розібратися з деякими з них. Як відомо, вікно може перебувати в декількох станах:
· Максимізації, тобто бути "розкритим" на весь екран - при цьому внутрішня область вікна займає весь екран, крім невеликих смуг зверху - де розміщується заголовок і меню, знизу - горизонтальна смуга прокрутки і справа - вертикальна смуга прокрутки; рамка вікна знаходиться за межами екрану, ми її не бачимо, переміщення вікна неможливо.
Для максимізації вікна ми можемо скористатися функцією ShowWindow з наступними можливими параметрами:
ShowWindow (hWnd, SHOW_FULLSCREEN);
ShowWindow (hWnd, SW_SHOWMAXIMIZED);
ShowWindow (hWnd, SW_MAXIMIZE);
максимізації вікно завжди активно і має фокус вводу. Коли будь-яке вікно максимізує, всі інші верхні вікна отримують повідомлення WM_SIZE, що інформує про те, що вони "закриті" максимізувати вікном.
Ми можемо дізнатися, чи є наше вікно максимізований за допомогою функції
BOOL IsZoomed (hWnd);
При використанні системного меню операції максимізації вікна відповідає пункт Maximize, вибір якого породжує системну команду SC_MAXIMIZE (або синонім SC_ZOOM). (Див. повідомлення WM_SYSCOMMAND)
Тут замість терміна maximize може використовуватися zoom.
· Мінімізованим, тобто представленим у вигляді ікони. Для того, що перетворити вікно в іконку ми повинні скористатися одним із способів:
ShowWindow (hWnd, SHOW_ICONWINDOW);
ShowWindow (hWnd, SW_SHOWMINIMIZED);
ShowWindow (hWnd, SW_SHOWMINNOACTIVE);
ShowWindow (hWnd, SW_MINIMIZE);
CloseWindow (hWnd);
Різні способи, що використовують ShowWindow, відрізняються лише правилами активації вікна. SW_SHOWMINIMIZED і SHOW_ICONWINDOW відображає вікно у вигляді іконки, роблячи його активним; SW_SHOWMINNOACTIVE не змінює поточного активного вікна; SW_MINIMIZE (як і функція CloseWindow ()) робить активним наступне вікно у списку Windows. Останній спосіб ефективний при мінімізації головного вікна програми - так як минимизированное головне вікно зазвичай позначає передачу активності іншому додатку.
Перевірити стан вікна можна за допомогою функції
BOOL IsIconic (hWnd);
При використанні системного меню перетворенню вікна в іконку відповідає пункт 'Minimiz e', породжує системну команду SC_MINIMIZE (або синонім SC_ICON). (Див. повідомлення WM_SYSCOMMAND)
У цьому випадку використовується відразу три різні терміна для позначення одного і того-ж: minimize, close і iconic. При цьому функція CloseWindow () є єдиною, інтерпретує термін close таким способом, у решті випадках close означає дійсно закриття (іноді знищення) вікна. Тут же треба, чтто термін open, застосовуваний до мінімізованому вікна позначає його максимізацію або відновлення нормальних розмірів.
· Нормальним, тобто ми бачимо (або можемо побачити) його рамку, ми можемо переміщати вікно по екрану. Коли вікно знаходиться в нормальному стані, то для нього визначені максимально і мінімально допустимий розміри. Ці розміри не можна плутати з максимізувати і мінімізованим станами. Максимальний розмір нормального вікна може навіть перевищувати розмір вікна в максимізований стані, мінімальний розмір це зазвичай такий розмір, при якому вікно ще може бути коректно представлено у вигляді вікна.
Для переходу з мінімізації стану до нормального можна скористатися функцією OpenIcon (hWnd), або, як з мінімізірованія, так і з максимізації стану можна користуватися функцією ShowWindow () з параметрами:
ShowWindow (hWnd, SHOW_OPENWINDOW);
ShowWindow (hWnd, SW_SHOWNORMAL);
ShowWindow (hWnd, SW_RESTORE);
ShowWindow (hWnd, SW_SHOWNOACTIVATE);
У документації (SDK Help) зазначено, що SW_RESTORE і SW_SHOWNORMAL еквівалентні, але це далеко не так - SW_RESTORE відновлює попередній стан, а не нормальний. Тобто, якщо Ви мінімізували вікно з максимізувати, то SW_RESTORE поверне Вас до максимізований вікна, а SW_SHOWNORMAL - до нормального. SW_SHOWNORMAL має синонім SHOW_OPENWINDOW.
Якщо вікно відновлюється або максимізується з мінімізованого стану, то Ваше вікно отримає повідомлення WM_QUERYOPEN - обробляючи яке Ви можете дозволити або заборонити подальші дії. Якщо Ви повертаєте TRUE, то вікно буде розкрито, а якщо Ви повернете FALSE, то вікно залишиться мінімізованим.
Зауваження 7. Додатково треба розібратися з кількома термінами Windows, які постійно застосовуються, але ніяк в документації не описані. Мова йде про хендла копії додатку (HINSTANCE), модуля (HMODULE) та завдання (HTASK). Всі ці хендла використовуються різними функціями, причому різниця між ними ніяк не пояснюється. Тому нам треба розглянути ці хендла більш докладно:
· HTASK описує задачу.
У Windows 3.x під завданням мається на увазі конкретний запущений процес, для якого визначені командний рядок, поточна виконувана інструкція, покажчик на стек, змінні оточення, PDB (еквівалент префікса завдання (PSP) в середовищі DOS) та ін хендл завдання можна отримати за допомогою функції
HTASK GetCurrentTask (void);
У Win32 хендл завдання не застосовується, а замість нього треба користуватися хендла і ідентифікаторами процесу і потоку. Їх можна отримати за допомогою функцій:
HANDLE GetCurrentProcess (void);
HANDLE OpenProcess (fdwAccess, fInherit, dwIDProccess);
DWORD GetCurrentProcessId (void);
HANDLE GetCurrentThread (void);
DWORD GetCurrentThreadId (void);
Функції GetCurrentProcess і GetCurrentThread повертають так званий псевдодескріптор процесу (потоку). Псевдодескріптор - це деяка величина, що розглядається у якості дескрітора поточного процесу (потоку). Тобто ця величина, що застосовується в контексті іншого процесу (потоку), буде описувати його, а не даний потік. Для отримання "справжнього" хендла треба скористатися функцією:
BOOL DuplicateHandle (
hSourceProcess, hSourceHandle, hTargetProcess, lphTargetHandle,
fdwAccess, fInherit, fdwOptions
);
· HINSTANCE описує копію додатку.
в Windows 3.x цей хендл вказує на сегмент даних програми, який містить стек і локальну купу. Для кожного запущеного додатка створюється свій власний сегмент даних, що дозволяє однозначно визначити конкретну копію програми за його сегменту даних або організувати обмін даними між двома копіями одного додатку. Так функція GetInstanceData дозволяє скопіювати дані, що належать сегменту даних іншої копії, в то-ж саме місце поточної інсталяції.
int GetInstanceData (hInstance, pByte, cbData);
У функцію WinMain передається як хендл поточної копії додатку, так і хендл попередньої копії, що дозволяє організувати взаємодію двох копій між собою, запобігти запуск інших копій, якщо це може призвести до помилки, або для виконання дій, необхідних тільки в першій копії додатку.
У Win32 для кожного запущеного додатку (тобто процесу) виділяється віртуальний адресний простір в 4G в єдиному сегменті. Тому даний хендл описує не сегмент даних (який описує весь 4G сегмент), а адреса у віртуальному просторі, з якого був завантажений даний модуль. У адресному просторі одного процесу ніяких інших додатків не існує, тому цей хендл не може застосовуватися для виявлення інших копій програми і тим більше для обміну даними між різними копіями додатків. У додатках Win32 hPrevInstance завжди дорівнює NULL, а хендл поточної інсталяції програми в більшості випадків збігається. При необхідності виявлення інших копій програми треба використовувати будь-які інші методи, наприклад функцію:
HWND FindWindow (lpszClassName, lpszWindowTitle);
Хендл вікна в Win32 є унікальним і може ідентифікувати конкретну вікно в будь-якому додатку.
Для обміну даними між додатками (процесами) доводиться передавати дані з адресного простору одного процесу в адресний простір іншого. Для виконання цих операцій передбачено повідомлення WM_COPYDATA. Коли Ви надсилаєте це повідомлення вікна, створеному іншим процесом, вказані Вами дані копіюються в адресний простір іншого процесу і можуть бути прочитані віконної процедурою вікна-одержувача. Цей механізм може застосовуватися і для обміну даними між 16-ти і 32-х бітовими додатками, однак для цього необхідно визначити номер повідомлення WM_COPYDATA і спеціальну структуру COPYDATASTRUCT для 16-ти бітової платформи - так як файл windows.h не містить цих визначень:
# Define WM_COPYDATA 0x004A
typedef struct tagCOPYDATASTRUCT {
DWORD dwData;
DWORD cbData;
LPVOID lpData;
} COPYDATASTRUCT, FAR * PCOPYDATASTRUCT;
· HMODULE описує окремий модуль.
У Windows 3.x під модулем розуміється окремий виконуваний файл або біблотека динамічного компонування. Для опису модуля створюється спеціальний сегмент опису модуля, що містить інформацію про всіх сегментах даного модуля і їх атрибути. Хендл модуля ідентифікує цей сегмент. Для будь-якої програми створюється тільки один описує сегмент, який поділяється між усіма копіями цього додатка. Для отримання хендла модуля Ви можете скористатися функціями:
HMODULE GetModuleHandle (lpszFileName);
int GetModuleFileName (hInstance, lpsBuffer, cbMaxSize);
У більшості випадків, функції Windows API, що працюють з хендлов модуля, коректно виконуються при передачі їм хендла копії додатку, так що в документації можливий деякий різнобій у використовуваних термінах.
У Win32 хендл модуля є синонімом хендла копії додатку. У документації зустрічаються обидва терміни, як вони перекочували з 16-ти бітових Windows, хоча вони тотожні.

Повідомлення. Здійснення та передача повідомлень

Раніше, на перших лекціях, ми розглядали метод передачі повідомлень, званий "посилкою" повідомлень (post). При використанні цього методу повідомлення ставиться в чергу програми і пізніше витягується з неї. Однак цей механізм не завжди зручний, тому що не дозволяє отримати результату обробки повідомлення, або дочекатися його завершення. Точніше, дозволяє, але дуже громіздким способом.
Для вирішення цих завдань вводиться альтернативний механізм, званий передачею повідомлень. При цьому повідомлення в чергу не потрапляє, і направляється безпосередньо віконної функції. По суті його можна розглядати як безпосередній виклик віконної функції з передачею їй зазначених параметрів. Це накладає деякі обмеження; так, наприклад, не можна передавати повідомлення WM_QUIT - воно обов'язково має пройти через чергу повідомлень, інші повідомлення (скажімо, клавіатури) додатково обробляються в циклі обробки повідомлень і їх теж треба посилати а не передавати і т.д.
Крім того складності виникають при використанні багатопоточних додатків Win32 API - там прийнято, що повідомлення, спрямовані вікна, обов'язково обробляються тим потоком, який це вікно створив. Це істотно ускладнює процес передачі (не посилки) повідомлень вікна, створеному іншим потоком - передавальний повідомлення потік поміщає це повідомлення в чергу приймає потоку з прапором 'передане повідомлення' і припиняється до одержання відповіді. Приймаючий потік, закінчивши обробку поточного повідомлення, витягує з черги першими повідомлення, помічені як передані, обрабативет їх і після цього відновлює роботу послав потоку. Однак при цьому можливе зависання обох потоків, якщо потік, що приймає повідомлення, намагається передати зухвалому потоку підтвердження - той перебуває в зупиненому стані і не може обробити підтвердження, а приймає в свою чергу зупиняється, поки подвержденіе не буде опрацьовано.
Для здійснення та передачі повідомлень можуть застосовуватися наступні функції:
BOOL PostMessage (hWnd, wMsg, wPar, lPar);
Посилає зазначене повідомлення вікна (через чергу повідомлень).
LONG SendMessage (hWnd, wMsg, wPar, lPar);
Передає повідомлення вікна (прямий виклик обробника повідомлень) і звращает управління в викликала процедуру після обробки зазначеного повідомлення. При цьому вона повертає значення, повернуте віконної функцією. При використанні цієї функції повідомлення взагалі не надходить в чергу додатки (крім додатків Win32 API). Тобто Ви можете скористатися цією функцією для обробки тих чи інших повідомлень до організації циклу обробки повідомлень.
BOOL PostAppMessage (hTask, wMsg, wPar, lPar); / / Windows 3.x
BOOL PostAppMessage (dwProccessId, wMsg, wPar, lPar); / / Win32 API
BOOL PostThreadMessage (dwThreadId, wMsg, wPar, lPar); / / Win32 API
Посилає повідомлення конкретного завдання. При цьому в чергу відповідного додатку міститься повідомлення, що має нульовий хендл вікна - одержувача. Відповідно таке повідомлення не діспетчерізуется ніякому вікна.
Хендл завдання hTask не є хендлов копії додатку. Ви можете використовувати функцію GetCurrentTask для отримання хендла завдання в середовищі Windows 3.x, а також функції GetCurrentThreadId і GetCurrentProcessId в Win32 API.
для отримання цього хендла.
BOOL PostQuitMessage (wPar);
Посилає повідомлення WM_QUIT із заданим параметром wPar вашому додатку. Повідомлення WM_QUIT використовується для завершення головного циклу обробки повідомлень.

Типові послідовності повідомлень, одержуваних вікном.

Зараз ми займемося вивченням основних повідомлень, використовуваних вікном. При цьому ми розглянемо кілька типових послідовностей повідомлень, одержуваних вікном. Так, наприклад, ми розглянемо послідовність повідомлень, одержуваних вікном при його створенні. При цьому треба вважати, що наведені повідомлення в послідовності є як би "скелетом", а реальна послідовність може мати більше повідомлень, або деякі можуть бути відсутні. Це визначається характеристиками вікна, його властивостями і виконуваної додатковою обробкою повідомлень. (Так, наприклад, послідовності повідомлень, одержуваних "прихованими" вікнами або вікнами в мінімізованому стані можуть істотно відрізнятися).
У розглянутих нами послідовностях повідомлень більшість повідомлень не проходять через чергу повідомлень, а передаються безпосередньо віконної процедури будь-якої функцією. Саме логіка виконання цієї функції і визначає послідовність отримання повідомлень. Крім того, деякі повідомлення в цих ланцюжках є як би "вкладеними" - тобто вони породжуються при обробці будь-якого іншого повідомлення.

Ініціалізація вікна

Для створення вікна використовується функція CreateWindow (). Під час її виконання вікно отримує кілька повідомлень:
WM_GETMINMAXINFO 0 & MINMAXINFO
Інформація про допустимі розміри вікна; дані в структурі MINMAXINFO задаються ДО передачі повідомлення, так що ви можете їх не змінювати, а тільки прочитати при необхідності. (Для отримання даних Ви можете замість обробки цього повідомлення скористатися функцією GetWindowPlacement ()).
WM_NCCREATE 0 & CREATESTRUCT
Створення зовнішньої (non-client) області вікна. Обробка, передбачена процедурою DefWindowProc () ініціалізує необхідні структури і, зокрема, виділяє простір для зберігання заголовка вікна. Ніякого малювання не виконується. Повертається процедурою 0 вказує на виниклу помилку і вікно не створюється; не 0 вказує на успішне створення зовнішньої області.
WM_NCCALCSIZE flag & NCCALCSIZE_PARAMS
Визначення розміру внутрішньої (client) частини вікна, окрім цього визначається частина вікна, яка може бути скопійована без змін при переміщенні вікна або зміні його розмірів.
WM_CREATE 0 & CREATESTRUCT
Передбачено для виконання Вами необхідних дій для створення внутрішньої (client) області вікна. При цьому Ви проводите ініціалізацію пов'язаних об'єктів, створення дочірніх вікон і пр. Як і WM_NCCREATE функція повертає подтвержение про успішне виконання потрібних дій. Проте "успіх" позначається 0, а помилка: -1 (!).

Активація програми

Зараз ми розглянемо ще декілька послідовностей повідомлень, одержуваних головним вікном додатка. Розглядати ми їх будемо на прикладі створення і відображення головного вікна програми. Для початку ми виділимо кілька повідомлень у дві звичайних послідовності, які можна назвати "активацією" і "деактивацією" додатка. Ці ланцюжки повідомлень виникають при передачі активності (фокуса введення) від однієї програми іншому. За час роботи однієї програми ці ланцюжки можуть виникати багаторазово. Зазвичай відразу за створенням головного вікна додатка треба активація цього додатка.
Активація програми:
WM_ACTIVATEAPP TRUE hTask
повідомлення, що інформує про активацію програми. Якщо програма має кілька вікон "верхнього рівня" (тобто вікон стилю WS_OVERLAPPED або WS_POPUP), то всі вони отримують ці повідомлення. Молодше слово lParam містить хендл того завдання, яка була активна до цього моменту.
WM_NCACTIVATE TRUE minimized & hWnd
активація (або деактивація, дивлячись по параметру wParam) зовнішній області вікна. Обробка цього повідомлення за замовчуванням перемальовує зовнішню область вікна для виділення кольором активного / неактивного станів. При цьому активність або неактивність вікна запам'ятовується в структурі опису вікна. Ви можете самі посилати WM_NCACTIVATE для зміни стану вікна. У документації параметр 'lParam' не описується, однак він може бути не 0, і містити такі ж дані, як і в повідомленні WM_ACTIVATE (див. нижче). Це повідомлення може бути отримано не тільки головним вікном додатка, так як інформує про активацію вікна, а не програми.
Якщо вікно активується, то повертається значення може бути будь-яким, а якщо деактивується то значення 0 забороняє його подальшу деактивацію. Дочірні НЕ MDI вікна (стиль WS_CHILD) часто цього повідомлення не отримують.
WM_GETTEXT bufsize & buffer
при обробці цього повідомлення зазначений буфер заповнюється назвою активованого вікна. У даному випадку це повідомлення породжується при перемальовуванні зовнішньої області вікна - WM_GETTEXT "вкладено" в обробку WM_NCACTIVATE. Якщо вікно не має заголовка (caption bar), то це повідомлення не надсилається.
WM_ACTIVATE 1 або 2 minimized & hWnd
активація внутрішньої області вікна. Це повідомлення може бути отримано не тільки головним вікном додатка, так як інформує про активацію вікна, а не програми. Однак дочірні вікна (наприклад, керуючі елементи діалогів) можуть цього повідомлення не отримувати. Параметр wParam інформує про те, що вікно активується за допомогою натиснення кнопки миші (2) або іншим шляхом (0). Параметр lParam містить у старшому слові не 0, якщо вікно мінімізовано, а в молодшому слові - хендл вікна був до цього активним.
<Отримання фокусу вводу>
повідомлення отримання / втрати фокусу введення вікном див. нижче.
У цьому ланцюжку можна виділити дві фази:
1) активація додатки (якщо потрібно)
2) активація вікна, яка теж виконується в декілька фаз:
· Активація зовнішньої області
· Активація внутрішньої області
Весь ланцюжок часто породжується функцією SetWindowPos (...); при цьому такий ланцюжок входить у більш складну групу, що починається з WM_WINDOWPOSCHAINGING і що закінчується WM_WINDOWPOSCHANGED. При активації вікна "клацанням мишкою" перед цим ланцюжком проходить WM_MOUSEACTIVATE і пара повідомлень WM_WINDOWPOSCHAINGING, WM_WINDOWPOSCHANGED. (Див. нижче)

Деактивація програми

При деактивації програми також можна виділити дві фази - деактивацію вікна і деактивацію додатки. Однак всупереч звичайній практиці виконання зворотних дій у зворотному порядку Windows виконує деактивацію вікна в тому ж порядку, як і активацію - спочатку зовнішню, а потім внутрішню області.
WM_NCACTIVATE FALSE minimized & hWnd
деактивація починається з повідомлення про деактивації зовнішньої області вікна, про що говорить параметр wParam == FALSE. Параметр lParam містить в молодшому слові хендл вікна, стає активним, старше вказує на стан Вашого вікна. (Більш детально - див. вище "активація програми")
WM_GETTEXT bufsize & buffer
Це повідомлення породжується при перемальовуванні зовнішньої області вікна.
WM_ACTIVATE FALSE minimized & hWnd
потім деактивується внутрішня область вікна (wParam == FALSE). Параметр lParam містить в молодшому слові хендл вікна, стає активним, старше вказує на стан Вашого вікна.
WM_ACTIVATEAPP FALSE hTask
і в кінці деактивується все застосування. Параметр hTask вказує на додаток, що стає активним.
<Втрата фокусу введення вікном>
повідомлення отримання / втрати фокусу введення вікном див. нижче.

Отримання і втрата фокусу введення вікном

Ланцюжки повідомлень активації та деактивації програми супроводжуються повідомленнями, передають фокус введення потрібного вікна активного застосування. Поняття фокус введення вимагає деякого пояснення. Раніше (на першій лекції) ми говорили про те, що вікно, що взаємодіє з клавіатурою, є активним. Зараз ми введемо додаткове поняття вікна, має фокус вводу - тобто отримує дані від клавіатури. Різниця двох понять - активного вікна і вікна, що має фокус вводу ми розглянемо пізніше. Поки що ми можемо відзначити, що вікно має фокус вводу завжди активно (але не навпаки).
Як правило послідовність собщеній активації вікна закінчується повідомленням про отримання фокусу введення, і повідомлення про деактивації закінчуються повідомленням про втрату фокусу вводу (знову-таки не в зворотному порядку).
отримання фокусу введення вікном:
WM_SETFOCUS hWnd losing focus 0
отримуємо фокус введення, причому параметр wParam вказує хендл вікна, втрачає фокус вводу (або NULL).
втрата фокусу введення вікном:
WM_KILLFOCUS hWnd received focus 0
втрачаємо фокус введення, причому параметр wParam вказує хендл вікна, що здобуває фокус введення (або NULL).

Відображення вікна

Зазвичай, після створення головного вікна програми, ми викликаємо функцію ShowWindow () для відображення головного вікна в потрібному нам стані.
Обробка ShowWindow ():
WM_SHOWWINDOW TRUE / FALSE 0
Параметр wParam вказує на необхідну дію - показати (TRUE) або "заховати" (FALSE) вікно. Молодше слово lParam містить 0, якщо повідомлення надіслано функцією ShowWindow ().
<Повідомлення активації програми>
Якщо ми викликаємо ShowWindow () для активації програми, то зараз проходить бесіда про активацію програми та передачі фокусу вводу.
WM_NCPAINT 0 0
Це повідомлення, як і WM_NCACTIVATE виконує малювання зовнішньої області вікна (крім них цим займаються ще й інші повідомлення, наприклад WM_NCLBUTTONDOWN і WM_SYSCOMMAND). У документації зазначено, що параметри wParam і lParam не використовуються. У Borland Help зазначено, що wParam є хендлов регіону, що визначає область де малювання потрібно, а lParam не використовується. Насправді wParam може бути 0 (при цьому зовнішня область не перемальовувався), може бути хендлов регіону, може бути 1 (малювання зовнішньої області потрібно). Параметр lParam може бути 0, або може містити в молодшому слові хендл вікна а в старшому ще якісь дані.
WM_GETTEXT bufsize & buffer
Як і у випадку WM_NCACTIVATE це повідомлення "вкладено" в обробку WM_NCPAINT.
WM_ERASEBKGND hDC 0
Очищається фон внутрішній області вікна. Для цього звичайно використовується кисть, визначена у структурі опису вікна. Параметр wParam задає хендл контексту пристрою, відповідного внутрішній області вікна. Якщо Ви плануєте самі зафарбовувати фон вікна, то для визначення розмірів внутрішньої області вікна треба використовувати функцію GetClientRect (). Це пов'язано з тим, що при створенні видимого вікна (що має стиль WS_VISIBLE) повідомлення WM_ERASEBKGND зустрічається до першого повідомлення WM_SIZE, а повідомлення WM_WINDOWPOSCHANGING в цьому випадку хоч і обробляється до WM_ERASEBKGND, але містить нульові розміри внутрішньої області вікна.
WM_SIZE type height & width
Задається розмір вікна, причому параметр wParam інформує про "типі" розміру - вікно може бути максимізувати, може бути мінімізовано або відображено в нормальному стані. Це повідомлення, крім того, надсилається вікна якщо воно закривається яких-небудь максимізований вікном, або стає видимим, коли закривав його максимізований вікно стало нормальним або перетворилося на іконку.
WM_MOVE 0 y & x
Задається положення вікна на екрані (або у внутрішній області вікна-батька).

Зміна розмірів, положення або стану вікна

Зазвичай при переміщенні (в X, Y або Z напрямку), зміни розмірів вікна або при зміні його стану, крім WM_SIZE і WM_MOVE Ви будете отримувати повідомлення WM_WINDOWPOSCHANGING і WM_WINDOWPOSCHANGED. При цьому всі повідомлення посилются функцією SetWindowPos (...) (або еквівалентної) у наступному порядку:
WM_WINDOWPOSCHANGING 0 & WINDOWPOS
Повідомлення вказує на те, що положення або стан вікна змінюється. Параметр lParam містить покажчик на структуру WINDOWPOS, що описує положення вікна і виконувану операцію. Змінивши відповідні поля структури можна змінити положення і розміри вікна або вплинути на їх дії.
<Повідомлення активації вікна (або всього програми)>
якщо вікно просто активується, то зазвичай таке повідомлення WM_WINDOWPOSCHANGED, завершальне весь ланцюжок, без всіх розглянутих нами проміжних повідомлень, що застосовуються при зміні розмірів або положення.
WM_GETMINMAXINFO 0 & MINMAXINFO
перевірка нових розмірів вікна
WM_NCCALCSIZE flag & NCCALCSIZE_PARAMS
визначення розміру внутрішній області
WM_NCPAINT 0 0
відображення зовнішньої області вікна
WM_ERASEBKGND hDC 0
очищення фону вікна
WM_WINDOWPOSCHANGED 0 & WINDOWPOS
стан вікна змінено. Параметр lParam є покажчиком на структуру WINDOWPOS, що містить дані про положення і розмірах вікна. Повідомлення WM_WINDOWPOSCHANGING ... WM_WINDOWPOSCHANGED часто не містять між собою жодних інших повідомлень, якщо стан, розмір і положення не змінилися. (Часто це буває, якщо вікно активується у відповідь на "клацання мишкою").
WM_MOVE 0 y & x
WM_SIZE type height & width
Ці повідомлення інформують про новому положенні і розмірах вікна. Вони надсилаються при відповідних змінах процедурою DefWindowProc () при обробці повідомлення WM_WINDOWPOSCHANGED. Хоча в документації зазначено, що можна дещо прискорити роботу, перехоплюючи замість WM_MOVE і WM_SIZE повідомлення WM_WINDOWPOSCHANGED і не викликаючи при цьому DefWindowProc (), але використовувати цей прийом потрібно дуже обережно - оскільки повідомлення WM_SIZE і WM_MOVE використовуються MDI вікнами.
Розглянута нами ланцюжок повідомлень породжується функцією SetWindowPos (або еквівалентної) і ці повідомлення безпосередньо передаються вікна, а не посилаються.
WM_PAINT 0 0
малювання внутрішній області вікна. У результаті виконаної операції вікно (або його частина), як правило, маркується як невірне, що призводить до перемальовуванні вікна. Повідомлення WM_PAINT витягується з черги повідомлень і може оброблятися з деякою затримкою після зміни розмірів, положення або стану вікна.

Оновлення вікна

Після виклику ShowWindow () в WinMain () зазвичай відбувається процедура UpdateWindow (). Ця процедура перевіряє наявність невірних прямокутників і, якщо вони є, передає повідомлення WM_PAINT (не ставить в чергу, а викликає обробку цього повідомлення).
Обробка UpdateWindow ():
WM_PAINT 0 0
Намалювати внутрішню область вікна. Якщо для отримання хендла контексту пристрою використовується функція BeginPaint (), то вона може передавати повідомлення WM_ERASEBKGND для очищення фону в невірному прямокутнику (якщо при його створенні було зазначено, що фон має бути відновлений).
WM_ERASEBKGND hDC 0
Обробляючи це повідомлення, ми повинні зафарбувати фон вікна, використовуючи переданий нам хендл контексту пристрою, або викликати обробку за замовчуванням.

Знищення вікна
Для знищення вікна треба викликати функцію DestroyWindow (), яка виконає необхідні дії щодо закриття вікна. Причому при цьому надсилаються:
<Повідомлення деактивації (якщо треба)>
Іноді буває так, що повідомлення деактивації вікна не надходять, відбувається деактивація програми та знищення вікна;
WM_DESTROY 0 0
Знищення внутрішній області вікна.
WM_NCDESTROY 0 0
Знищення зовнішньої області вікна.
Повідомлення WM_DESTROY і WM_NCDESTROY є останніми повідомленнями, одержуваними вікном. Після WM_NCDESTROY вікно не отримає жодного повідомлення, тому Ви можете сміливо руйнувати всі створені для вікна структури даних.
Повідомлення WM_DESTROY зручно застосовувати для знищення всіх об'єктів, пов'язується з даними вікном, в тому числі і дочірніх вікон.
Увага: під час обробки повідомлень WM_DESTROY і WM_NCDESTROY не можна активувати будь-яких дочірніх вікон. (В тому числі не можна застосовувати функцію MessageBox, передаючи їй хендл знищуваного вікна як батьківського) - це призведе до появи додаткових повідомлень, спрямованих знищуваному вікна, і, в кінцевому результаті, до помилки "General protection fault ..."
Якщо Ви хочете отримати підтвердження перед закриттям вікна, то Ви повинні використовувати повідомлення WM_CLOSE. Функція DefWindowProc, обробляючи це повідомлення, викликає функцію DestroyWindow. Ви можете легко вставити власні кошти для обробки повідомлення WM_CLOSE, і викликати DestroyWindow або процедуру за замовчуванням тільки при позитивній відповіді на запит. Повідомлення WM_CLOSE краще посилати, а не передавати.
Подія WM_CLOSE використовується самим середовищем, причому зазвичай передує послідовністю повідомлень, що призводить до закриття вікна. Наприклад, такий: активація системного меню - вибір пункту 'Close' - завершення меню - посилка WM_SYSCOMMAND з параметром SC_CLOSE - посилка WM_CLOSE - знищення вікна.

Завершення роботи Windows

Існує ще два повідомлення, яке можуть призвести до закриття вікна:
WM_QUERYENDSESSION 0 0
Це повідомлення інформує про те, що Windows закінчує роботу коли додаток активно. Воно посилається до усіх запущених застосунків. Якщо все повертають TRUE, то Windows завершує роботу.
Обробляючи це повідомлення, Ви повинні повернути TRUE, якщо ваша заявка може бути завершено, або FALSE в протилежному випадку. При цьому Windows продовжить нормальну роботу.
WM_ENDSESSION TRUE / FALSE 0
Це повідомлення надсилається активному додатку, якщо воно відповіло TRUE на повідомлення WM_QUERYENDSESSION. Прапор в параметрі 'wPar' рівний TRUE указує, що робота Windows може завершитися відразу після обробки цього повідомлення. Ви не зобов'язані знищувати вікно і посилати собі WM_QUIT для завершення роботи, якщо завершується робота всього Windows.

Клавіатура. Повідомлення клавіатури
Зараз ми розглянемо основні повідомлення клавіатури і кілька функцій, призначених для роботи з нею. Коли ми натискаємо на будь-яку кнопку, наше (точніше - активне) додаток отримує повідомлення
WM_KEYDOWN VkCode dwKeyData
Параметр wPar містить віртуальний код клавіші, клавіші. У Windows підтримується спеціальна система нумерації клавіш, яка представлена ​​їх віртуальними номерами.
Параметр dwKeyData присутній для всіх повідомлень клавіатури і містить інформацію про поточний подію.
Для більшості клавіш їх віртуальний код відповідає тій великої букви, яка на них нанесена (для англійської клавіатури). Наприклад, віртуальний код клавіші A дорівнює ASCII кодом символу 'A'. Для тих клавіш, які не відповідають буквам і символів передбачені спеціальні номери, наприклад VK_F6 для клавіші F6, VK_MENU для клавіші Alt, VK_RETURN для клавіші Enter.
Якщо подивитися на список віртуальних кодів (це можна зробити в WINDOWS.H), то ми побачимо кілька дивних клавіш:
VK_LBUTTON, VK_RBUTTON, VK_MBUTTON
відповідних кнопок миші. Однак ці віртуальні коди використовуються дещо інакше, і програма не отримує повідомлень WM_KEYDOWN при натисканні кнопок миші.
Параметр dwKeyData присутній для всіх повідомлень клавіатури і містить наступну інформацію:

WM_KEYUP VkCode dwKeyData
Крім повідомлення WM_KEYDOWN Windows посилає повідомлення WM_KEYUP при відпусканні клавіші. Параметри цього повідомлення такі ж, як і у повідомлення WM_KEYDOWN.
Коли Ваша програма витягує з черги повідомлення WM_KEYDOWN, то зазвичай воно транслюється за допомогою функції TranslateMessage (це відбувається у головному циклі обробки повідомлень). Ця функція здійснює трансляцію повідомлень клавіатури і в результаті посилає повідомлення WM_CHAR, відповідний натиснутій символу або WM_DEADCHAR.
WM_CHAR nCharacter dwKeyData
Повідомляє додатком про те, що натиснута клавіша відповідає даному (nCharacter) символу. Якщо Ви розробляєте, наприклад, власний редактор, то Ви повинні обробляти повідомлення WM_CHAR для формування тексту, а не повідомлення WM_KEYDOWN.
WM_DEADCHAR nDeadChar dwKeyData
Це повідомлення інформує додаток про натискання так званої Dead-Key. Dead-Key клавіші зазвичай відповідають акцентним символів. Так, наприклад, німецька клавіатура має кілька Dead-Key, скажімо '. Якщо натиснути цю клавішу, а, потім, букву A, то результат повинен відповідати A з наголосом. При цьому натиснутий 'сам по собі не відповідає ніякому символу (це Dead-Key), а служить модифікатором для наступної клавіші. Таким чином на послідовність повідомлень WM_KEYDOWN для клавіш 'і A будуть генеруватися повідомлення WM_DEADCHAR для' і WM_CHAR для A з наголосом.

Вікно, що має фокус вводу, і активне вікно

Крім розглянутих чотирьох повідомлень Windows використовує ще 4 повідомлення, що містять таку ж інформацію, як і вже розглянуті повідомлення клавіатури:
WM_SYSKEYDOWN VkCode dwKeyData
WM_SYSKEYUP VkCode dwKeyData
WM_SYSCHAR nCharacter dwKeyData
WM_SYSDEADCHAR nDeadChar dwKeyData
Зазвичай ці повідомлення посилаються при натисканні клавіш спільно з клавішею Alt. Ви обов'язково повинні передбачити обробку цих повідомлень за замовчуванням, так як, якщо цього не робити, то буде порушений стандартний інтерфейс Windows - перестануть працювати такі клавіші як Alt-Tab, Ctrl-Esc і пр.
Зараз ми повинні будемо розібратися у відмінностях понять "активне вікно" і "вікно, що має фокус вводу". Сформулюємо кілька правил, які дозволяють розрізняти ці поняття.
· Вікно, що має фокус вводу, завжди активно.
· У будь-який момент часу існує активне вікно.
· Мінімізоване вікно не може мати фокуса введення, якщо активне вікно мінімізовано, то фокус введення не належить ніякому вікна.
· Якщо активне вікно не мінімізовано, то воно має фокус вводу.
Все, що ми зараз з'ясували про повідомлення клавіатури стосується вікна, що має фокус вводу. Якщо наше вікно активно і не має фокусу вводу, то замість повідомлень WM_KEYDOWN, WM_KEYUP, WM_CHAR і WM_DEADCHAR воно буде отримувати повідомлення WM_SYSKEYDOWN, WM_SYSKEYUP, WM_SYSCHAR і WM_SYSDEADCHAR.
Для чого в Windows використовуються ці два поняття? Уявімо собі, що ми розробляємо будь-якої редактор. Для управління ним ми використовуємо звичайні (не системні) повідомлення клавіатури. Тепер ми мінімізували наш редактор і зробили його активним. Якби при цьому він як і раніше отримував звичайні повідомлення клавіатури, то довелося б спеціально передбачати блокування введення даних у мінімізованому стані - так як робота "в сліпу" навряд-чи буде зручна. А, так як в мінімізованому стані він втрачає фокус введення, то замість звичайних повідомлень він буде отримувати системні, що виключить небажаний ефект.
Проте, обробляючи системні повідомлення, не можна припускати, що клавіша Alt була натиснута заздалегідь - минимизированное активне вікно буде отримувати ці повідомлення навіть для звичайних клавіш. Треба обов'язково перевіряти біт з номером 1D (Context code) у dwKeyData, що б переконатися в активності Alt-статусу.
Тут же треба звернути увагу на те, що в документації про Windows зустрічається два трактування поняття "активне вікно"
· З одного боку це може бути вікно, одержує повідомлення від клавіатури; Таке вікно може бути тільки одне.
· З іншого боку активному вважається всяке вікно, чиє дочірнє має фокус вводу. Активне вікно виділяється кольором рамки і заголовка. У цьому випадку активних вікон може бути багато.

Визначення стану окремих клавіш

Працюючи з клавіатурою іноді доводиться перевіряти стан тієї чи іншої конкретної клавіші. Для цього Windows містить дві функції
int GetKeyState (nVkCode);
void GetKeyboardState (lpbKeyStates);
Функція GetKeyState () повертає слово, характризует стан заданої клавіші. Якщо старший біт (маска 0x8000) дорівнює 1, клавіша натиснута, якщо 0, то відпущена. Молодший біт (маска 1) вказує стан "перемикається" клавіші (наприклад, CapsLock).
Функція GetKeyboardState () заповнює масив з 256 байт станами всіх клавіш. В якості індексу масиву використовується віртуальний код клавіш. Кожен байт масиву описує одну клавішу, його найстаршого біта (маска 0x80) описує стан клавіші, молодший (маска 1) - стан перемикається клавіші.
На додаток до цих функцій Windows містить ще одну функцію
void SetKeyboardState (lpKeyStates);
яка дозволяє задати стан конкретних клавіш. Ця функція тільки коригує відповідний масив, з яким працює сама система, але не посилає жодних повідомлень, що симулюють роботу з клавіатурою. Застосовувати цю функцію треба дуже обережно, тому що клавіатура є ресурсом, що розділяється.

Каретка

Говорячи про клавіатуру ми повинні ввести ще одне поняття - каретка (caret). Справа в тому, що Windows використовує два укази елемента - курсор (cursor), який показує положення миші, і каретка, яка показує місце, в яке буде уводитися текст. Каретка управляється клавіатурою.
При роботі з кареткою треба враховувати деякі особливості. Практично всі вони пов'язані з тим, що каретка є ресурсом, що розділяється, тому Ви повинні уважно стежити за коректним використанням каретки. Так, наприклад, Ви можете створювати власну каретку; однак Ви повинні передбачити кошти для її "приховування" і знищення в той момент, коли ваша заявка втрачає фокус введення, і створювати або показувати її, тільки якщо Ваше вікно має фокус вводу.
Для роботи з кареткою призначені наступні функції:
void ShowCaret (hWnd);
void HideCaret (hWnd);
З їх допомогою Ви можете показати каретку або зробити її невидимою. Параметр hWnd може бути NULL, якщо використовується поточне вікно. Видима каретка автоматично починає блимати.
void SetCaretPos (nX, nY);
void GetCaretPos (lpPoint);
void SetCaretBlinkTime (mMSec);
UINT GetCaretBlinkTime (void);
Ці функції дозволяють задати положення каретки в координатах вікна і період мигання каретки, в мілісекундах. Крім того Ви можете створювати власну каретку за допомогою функції
void CreateCaret (hWnd, hBmp, nWidth, nHeight);
Параметр 'hBmp' може бути хендлов бітмапами, при цьому параметри 'nWidth' і 'nHeight' ігноруються, або однією з двох значень: 0 - суцільна каретка, або 1 - "сіра" каретка; в цьому випадку параметри 'nWidth' і 'nHeight 'задають розміри каретки.
void DestroyCaret (void);
Ця функція знищує поточну каретку (якщо каретка використовує бітмапами, то бітмапами зберігається).
Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Лекція
85.7кб. | скачати


Схожі роботи:
Структура типового робочого вікна ос Windows Опис структури вікна основних елементів вікна в О
Вікна в Windows
Структура вікна Windows 98
Створення Web-додатків в середовищі Delphi
Створення розрахункових додатків і програми пошуку в базі даних у середовищі Delphi 7 0
Панель керування в ОС Windows Панель управління пристороями Пк у середовищі Windows
Створення розрахункових додатків і програми пошуку в базі даних у середовищі Delphi 1970
Особливості багатозадачності в середовищі Windows
Операції над папками текстовими документами та ярликами в середовищі Windows Основні прийоми р
© Усі права захищені
написати до нас