Особливостi програмування для Windows

Програміст, вперше спробувавши створити Windows-програму, виявить, що цей процес суттєво відрізняється від процесу створення програм для середовища DOS.

Специфіка організації багатозадачності накладає суттєвий відбиток на технологію програмування задач, що повинні виконуватись у середовищі Windows.

Принципова відмінність створення програм для Windows полягає в тому, що кожна програма тепер повинна розроблятися на основі передачі та обробки повідомлень. Рисунок 2 - Схема обробки повідомлень

У зв’язку з керуванням та організацією зв’язку між програмами за допомогою повідомлень у кожній програмі (для кожного вікна) повинні бути явно виділені такі обов’язкові елементи, як цикл введення повідомлень та процедура їх обробки. Тобто кожна програма повинна в циклі аналізувати чергу повідомлень, обробити (процедурою вікна) повідомлення, що призначені саме їй, і, якщо програма не закінчила своєї роботи, повернутися до читання наступного повідомлення, в протилежному випадку - передати керування Windows. Процедура обробки повідомлень (або процедура вікна, WinProc) і є, власне, ядром кожної програми для Windows. Така організація роботи програми схематично проілюстрована на рис. 2.

 

Для полегшення складання і читання програм програмісти дотримуються такого принципу: імена змінних повинні містити префікс, який описує тип даних змінної. Наведемо декілька таких префіксів:

а: масив ( array );

сh: символ ( char );

by: байт ( byte );

i: ціле ( int );

fn: функція ( function );

w: слово ( word );

p: покажчик ( pointer );

l: довге ціле ( long );

dw: подвійне слово ( dword );

s: рядок ( string );

Константи:

wm_XXX: повідомлення(windows message);

cs_XXX: стиль класу ( class style );

id_XXX: ідентифікатор елемента.

 

Розглянемо дві прості програми: перша використовує функції Windows API, а друга - об'єктно - орієнтовану бібліотеку ObjectWindows.

 

program WinMin;

{ Наступні модулі містять опис основних типів та функцій, що використовуються в Windows’95 }

uses WinTypes, WinProcs;

 

const

AppName = 'WinMin';

 

{ Віконна функція. Розпізнає повідомлення і виконує відповідні дії. У нашому випадку функція опрацьовує тільки одне повідомлення wm_Destroy. У відповідь на це повідомлення викликом процедури PostQuitMessage наша програма поміщує в чергу повідомлення wm_Quit. Коли функція GetMessage отримує це повідомлення, цикл обробки повідомлень припиняє свою роботу і програма завершується }

 

function WindowProc(Window:HWnd; Message, WParam:word;

LParam:longint):longint;export;

begin

WindowProc:=0;

case Message of

{Обробка повідомлень}

wm_Destroy: {Розпізнали повідомлення}

begin { wm_Destroy }

PostQuitMessage(0); { Ставимо в чергу}

Exit {повідомлення про закриття}

end;

end;

{ Якщо наша віконна функція не опрацювала повідомлення, то викликаємо стандартну віконну функцію, яка обробляє всі повідомлення, що не оброблені заміщеною }

WindowProc:=DefWindowProc(Window, Message, WParam, LParam)

end;

 

{ Ініціюючі дії процедури WinMain полягають в створенні і регістрації класу вікна, створенні і відображенні вікна на екрані і активації циклу обробки повідомлень }

 

procedure WinMain;

var Window:HWnd; { Покажчик на вікно}

Message:TMsg; { Повідомлення}

WindowClass:TWndClass; { Клас вікна }

begin

if HPrevInst = 0 then {Тільки для першого екземпляра програми}

 

begin {Опис атрибутів WindowClass.Style := класу}

cs_HRedraw or cs_VRedraw; {Стиль вікна}

WindowClass.lpfnWndProc:= { Покажчик на }

@WindowProc; { віконну функцію }

WindowClass.cbClsExtra:=0; { Використовується

WindowClass.cbWndExtra:=0; для виділення

пам'яті }

WindowClass.HInstance:= { Ресурси }

HInstance;

{ Покажчик на іконку }

WindowClass.hIcon:=LoadIcon(0, idi_Application);

{ Покажчик на курсор } WindowClass.hCursor:=LoadCursor(0,idc_Arrow);

{ Заповнення фону вікна } WindowClass.hbrBackground:=GetStockObject(white_Brush);

WindowClass.lpszMenuName:=''; { Меню}

{ Ім'я програми}

WindowClass.lpszClassName:=AppName;

{Регістрація класу}

if not RegisterClass(WindowClass) then Halt(255)

end;

{Створення вікна}

Window:=CreateWindow(AppName,'Win_Min',ws_OverlappedWindow,

cw_UseDefault,cw_UseDefault,cw_UseDefault,

cw_UseDefault,0,0,hInstance,nil);

ShowWindow(Window,CmdShow);

UpdateWindow(Window);

{Цикл обробки повідомлень}

while GetMessage(Message,0,0,0) do begin

TranslateMessage(message);

DispatchMessage(Message)

end;

Halt(Message.WParam);

end;

 

begin

WinMain

end.

 

Під час виконання даної програми на екрані з'явиться вікно, яке можна переміщувати, змінювати його розмір і, нарешті, закрити.

Розглянемо дії, які виконують частини програми.

 

Вікно завжди створюється на основі класу. Спочатку відбувається заповнення властивостей класу, а потім - реєстрація. Клас вікна наведений в структурі TWndClass.

 

При створенні конкретного вікна, використовуючи функцію CreateWindow, можна зазначити більш детальні характеристики вікна.

 

Після відображення вікна керування передається циклу обробки повідомлень.

У цьому циклі повідомлення добуваються з черги. Для всіх повідомлень, крім wm_Quit ( закінчення роботи програми ), функція GetMessage повертає не нульове значення і цикл продовжується.

Процедура TranslateMessage передає повідомлення ядру Windows, яке викликає процедуру WinProc даного класу вікна.

 

Віконна функція розпізнає повідомлення і виконує відповідні дії. В нашому випадку функція опрацьовує тільки одне повідомлення wm_Destroy. У відповідь на це повідомлення, викликом процедури PostQuitMessage наша програма поміщує в чергу повідомлення wm_Quit. Коли функція GetMessage отримує це повідомлення, цикл обробки повідомлень припиняє свою роботу і програма завершується.

 

Як ви вже зрозуміли, процес створення навіть найпростішої програми для Windows є достатньо трудомісткий. Одним із засобів спростити програмування є використання об’єктно-орієнтованої бібліотеки Object Windows (OWL), яка бере на себе всю рутинну роботу по початковому створенню програми. Бібліотека повністю об’єктно-орієнтована.

Рисунок 3 - Вершина ієрархії бібліотеки OWL
Коротко нагадаємо основні принципи ООП:

Інкапсуляція - об’єднання даних та процедур, дає змогу створити об’єкти, що відповідають інтерфейсним елементам Windows. Набір методів такого об’єкта дозволяє повністю контролювати поведінку відповідного елемента інтерфейсу.

Механізм спадковості - дозволяє створювати об’єкти на основі вже існуючих та доповнювати їх новими даними та процедурами, що значно скорочує написання програм.

Поліморфізм забезпечує єдину лінію поведінки різних об’єктів під час виклику одного й того самого методу. Це особливо важливо в зв’язку з тим, що Windows-програма є, по суті, ієрархічна система вікон з різними властивостями, і велика кількість методів використовують однакові ітератори для виконання однотипних дій над різними дочірними об’єктами. Наприклад, при виконанні методу Create для всіх дочірних об’єктів здійснюється створення інтерфейсного елемента, якому відповідає об’єкт, незалежно від того, чи це вікно, чи елемент керування або панель діалогу.

Наявність віртуальних методів підсилює механізм спадковості, дозволяючи економити на кількості методів, що перепризначаються.

 

Обробка повідомлень в бібліотеці OWL реалізована з допомогою механізму динамічних віртуальних методів.

Відзначимо основні переваги цієї бібліотеки:

  • послідовний, спрощений інтерфейс з пристроями;
  • автоматична обробка повідомлень і керування вікнами;
  • можливість використання і розширення коду.
Ієрархія об’єктів ObjectWindows нагадує бібліотеку TurboVision і містить об’єкти, які реалізують загальні інтерфейсні елементи керування. Опис об’єктів OWL можна знайти в [3], а також в Help.

 

Кожна програма, створена за допомогою OWL, базується на нащадку об’єкта TApplication.

 

Uses OWindows, WinProcs;

{ Об'єкт, що реалізує програму}

var App:TApplication;

 

begin

{ Ініціюємо об'єкт}

App.Init('Sample Application');

App.Run; { Запуск обробки повідомлень}

App.Done { Деініціалізація і знищення

об'єкта} end.

 

Результат роботи цієї програми аналогічний розглянутій вище.

 

Конструктор Init виконує ініціалізацію об’єкта з віртуальними методами, створює і відображає головне вікно програми і підключає віконну функцію.

 

Метод Run містить цикл обробки повідомлень.

 

Деструктор Done виконує необхідні процедури для закінчення роботи програми і деініціалізації об’єкта.