Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsiyi.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
188.29 Кб
Скачать

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

Основна відмінність системного програмування від прикладного – воно машинозалежне, оскільки тісно пов’язане з архітектурою і структурою комп’ютера для якого створені.

До задач СП відносять:

  • системне ансамблювання – переводить початковий файл з команди в завантажувальний;

  • завантажувачі – абсолютний – записує об’єктну програму в ОП і передає управління на адресу початку її виконання; зв’язний – забезпечує переміщення програми в будь-яку область пам’яті;

  • макропроцесори – дозволяють групи команд що часто повторюються замінювати однією макроконструкцією;

  • ОС – інтерфейс;

  • драйвер – робота периферійних пристроїв.

Програмування на Асемблері у Windows є частково спрощеним в порівнянні з звичайним Асемблером, оскільки переважно робота зводиться до використання АРІ-функцій. При цьому можна виділити 3 типи структури програм:

  • класична

  • діалогова

  • консольна

Основні положення:

  1. АРІ-функції (більше 2000);

  2. головним елементом програми в середовищі Windows є вікно. Для кожного вікна треба визначати свою процедуру обробки повідомлень. Вікно може містити елементи керування (меню, списки, колонки), але подію, які відбув. з цими елементами будуть приходити в процедуру вікна, на якому вони розміщені;

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

  4. ОС Windows підтримує багатозадачність.

Всі параметри для виклику АРІ-функції заносяться в стек за золотим правилом: якщо в довідці записані в рядочок – то справа наліво, якщо в стовпчик – то знизу вверх.

Класична структура програми має таку структуру:

  1. реєстрація класу вікна чи вікон. Здійснюється функцією RegisterClassA(). Дана функція має єдиний параметр, який є вказівником на структуру WNDCLASS. В цій структурі міститься вся інформація про те, яке саме вікно буде створюватись;

  2. створення головного вікна. Здійснюється функцією CreateWindowA() або CreateWindowExA();

  3. створення циклу обробки черги повідомлень. Вилучення повідомлень з черги і спрямування їх відповідним вікнам здійснює функція WinMain. Цей процес відбувається так:

  • повідомлення вибирається з черги з допомогою функції GetMessage чи PeekMessage;

  • потім повідомлення транслюється з допомогою функції TranslateMessage;

  • потім його надсилають вікну з допомогою функції DispatchMessage (диспетчеризація).

На виконання операцій існують спеціальні функції. Ці функції утворюють цикл обробки повідомлень, оскільки після завершення обробки одного повідомлення додаток має приготуватися до опрацювання наступного. Цикл закінч. лише при завершенні роботи – GetMessage повертає 0.

>MSGmsg;

>while (>GetMessage(&msg,NULL,NULL,NULL)) {

>TranslateMessage(&msg);

>DispatchMessage(&msg); }

Це найбільш простий вид циклу обробки повідомлень. У реальних додатках він більше складний.

  1. процедури головного вікна. Віконна процедура головного вікна клієнта має вигляд:

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

HWND hwnd – дескриптор вікна;

UINT uMsg – повідомлення;

WPARAM wParam, LPARAM lParam – параметри повідомлення.

Основна процедура вікна:

WNDPROC PROC

PUSH EBP //регістр базового вказівника

MOV EBP, ESP //регістр стеку

PUSH EBX

PUSH ESI //індексний регістр джерела

PUSH EDI //індексний регістр джерела

push DWORD PTR [ebp+014h]

push DWORD PTR [ebp+010h]

push DWORD PTR [ebp+0Ch]

push DWORD PTR [ebp+08h]

call DefWindowProcA@16

POP EDI

POP ESI

POP EBX

POP EBP

RET 16 //звільняємо стек від 4-ох параметрів

WNDPROC ENDP

DefWindowProcA@16 викликається для тих повідомлень, які не обробляються у функції вікна.

Як правило, всі сучасні пакети підтримують спрощену сегментацію, яка значно спрощує письмовий об’єм програми. Цими сегментами є:

.DATA – містить ініціалізовані дані програми;

.DATA? – містить неініціалізовані дані програми;

.CONST – містить константи програми;

.COD – містить код програми;

.386

.model flat,stdcall

.data

sum dword Ø

.code

Start:

.data

A dword 3

.code

mov EAX,A

.data

B dword 3

.code

add EAX,B

mov sum,EAX

ret

end start

.386 – вказує яку систему команд треба використ.; літера р – використ. привілейовані команди.

.model flat,stdcall – використ. плоску модель пам’яті flat і передбачено віддалений виклик процедур. Є такі моделі пам’яті:

  1. Tiny – дуже маленька – дані і код після компіляції знаходяться тільки в 1 сегменті.

  2. Small – маленька – дані і код після компіляції знаходяться в 1 сегменті, але в програмному коді можуть бути розділені

  3. Compact – код знаходиться в 1 сегменті, а кожна змінна фактично в своєму власному сегменті. Застосовують переважно при роботі з відео.

  4. Medium – протилежна до Compact. Для хорошу оптимізацію при великих обсягах прогр. коду;

  5. Large – розподіл коду по сегментах відбув. за вибором програміста;

  6. Flat – програма взагалі не ділиться на сегменти, але може містити великі обсяги даних.

Схема передачі параметрів через стек:

Вказівники можуть мати конкретний тип незалежно від вибраної моделі пам’яті. Найчастіше:

  • near (16 біт) – ближчі. При їх використанні для обчислення адреси використовують 1 сегментний регістр. Легко з ним працювати, але придатний для локал. програм. Вони визначають адреси об’єктів у конкретному сегменті, оскільки near-вказівник зберігає лише зміщення повної адреси. Near-вказівники можна використовувати тоді, коли початкову адресу сегмента непотрібно вказувати. За допомогою near-вказівників організують доступ до функцій чи даних у одному сегменті.

  • far (32 біти) – дальні. Містять і адреси самого сегменту (на відміну від near) та зміщення, дозволяють мати декілька сегментів коду і можуть використ. в більших програмах.

  • huge (32 біти) – нормалізовані. На відміну від far-вказівників, huge-вказівники містять лише нормалізовані адреси, тобто адреса довільного об’єкта в пам’яті відтворена єдиним сполученням значень його сегментованої частини та зміщення. Тому їх використовують при порівняннях. Але їм треба більше затрат часу і працюють повільніше в решті арифм. операцій.

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

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

При завершенні роботи програми всі консолі автоматично звільняються. Проте це можна робити примусово – FreeConsole(). Рекомендовано використ. на початку кожної консольної програми.

Щоб отримати ідентифікатор консольного вікна, використ. функцію GetStdHandle(). Аргумент:

  • STD_INPUT_HANDLE (equ -10)

  • STD_OUTPUT_HANDLE (equ -11)

  • STD_ERROR_HANDLE (equ -12)

Для читання з буфера консолі використ. ReadConsole(). Має 5 параметрів: 1-й – дескриптор, 2-й – адреса буфера куди буде поміщатись інфа, 3-й – довжина буфера, 4-й – кількість зчитаних символів, 5-й – зарезервований.

Заголовок вікна консолі – SetConsoleTitle(). Єдиний параметр – рядок, який буде відобр. в імені вікна. Має біти меншим за 64К. Для виводу у вікно консоль використ. DOS-кодування, а назви вікна – Windows-кодування. Щоб змінити – CharToOem(), де 2 параметри – рядок що перекодовується і куди помістити результат.

Встановлення позиції курсору в консолі – SetConsoleCursorPosition(). Має 2 параметри: 1-й – дескриптор вхідного буфера консолі, 2-й – вказівник на структуру COORD STRUC:

COORD STRUC X WORD ?

Y WORD ?

COORD ENDS

Встановлення кольору тексту – ConsoleTextAttribute(). Має 2 параметри: 1-й – дескриптор вхідного буфера консолі, 2-й – колір букв і фону. Константи кольору. Колір може формуватись за допомогою комбінації з цих кольорів.

Встановлення розмірів консолі – SetConsoleScreenBufferSize(). Має 2 параметри: по х та у.

Практично в більшості консольних функцій у випадку успішного завершення в EAX заноситься ненульове значення, у випадку помилки – NULL.

Для отримання інформації про подію від клавіатури чи мишки є функція ReadConsoleInput(). Має 4 параметри: 1-й – дескриптор вхідного буфера консолі, 2-й – вказівник на структуру чи їх масив, 3-й – кількість структур, 4-й – вказівник на подвійне слово, що містить к-сть реально отриманих структур.

Саме 4-ий параметр (його молодше слово) визначає тип події. Зарезервовано 5 типів подій:

KEY_EVENT (equ 1h)

MOUSE_EVENT (equ 2h)

WINDOW_BUFFER_SIZE_EVENT (equ 4h)

MENU_EVENT (equ 8h)

FOCUS_EVENT (equ 10h)

KEY_EVENT:

+4 – натиснення клавіші

+8 – кількість повторів при натисненні клавіші

+10 – віртуальний код клавіші

+12 – скан-код клавіші

+14 – молодший байт = ASCII-коду клавіші

+16 – міститься стан керуючих клавіш:

  • RIGHT_ALT_PRESSED equ 1h;

  • LEFT_ALT_PRESSED equ 2h;

  • RIGHT_CTRL_PRESSED equ 4h;

  • LEFT_CTRL_PRESSED equ 8h;

  • SHIFT_PRESSED equ 10h;

  • NUMLOCK_ON equ 20h;

  • SCROLLLOCK_ON equ 40h;

  • CAPSLOCK_ON equ 80h;

  • ENHANCED_KEY equ 100h.

MOUSE _EVENT:

+4 – молодше слово – Х-координата мишки, старше - У-координата

+8 – описує стан кнопок мишки: 1-й – ліва, 2-й – права, 3-й – середня (біти нумеруються 0-2). Якщо біт = 1, кнопка нажата

+12 – стан керуючих клавіш (аналогічно +16 в KEY_EVENT)

+16 – містить 2 значення:

  • MOUSE_MOV equ 1h; //був рух мишки

  • DOUBLE_CL equ 2h; //був подвійний клік

В структурі події WINDOW_BUFFER_SIZE_EVENT по зміщенню +4 знаходиться подвійне слово, яке містить новий розмір консольного вікна. Молодше слово - розмір по X, старше слово - розмір по Y. Коли йде мова про консольне вікно, всі розміри і координати даються в "символьних" одиницях.

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

  • завантажується в пам’ять лише при зверненні до нього;

  • підтримуються системою автоматично;

Опис ресурсів зберігається в текстовому файлі *.rc і компілюється транслятором ресурсів rc.exe в файл з розширенням *.res. У виконавчий файл ресурс підключається компоновщиком LINK/subsytem:windows *.obj *.res

Піктограми. Можуть розміщатись як в файлі ресурсів, так і в окремому *.ico-файлі. Додавання піктограми у файлі ресурсів:

#define HM_ICON1 1 //ідентифікатор піктограми

HM_ICON1 ICON "rl.ico" //ідентифікатор файлу

Завантаження піктограми в програмі:

PUSH HM_APPLICATION

PUSH 1

CALL LoadIcon@8

MOV [WC.CLSHICON], EAX

Курсори. Аналогічно:

#define IDI_CUR1 2 //ідентифікатор курсору

IDI_CUR1 CURSOR "rl.cur" //ідентифікатор файлу

Завантаження курсору в програмі:

PUSH IDC_CROSS

PUSH 0

CALL LoadCursorA@8

MOV [WC.CLSHCURSOR], EAX

Бітові малюнки. Аналогічно:

#define IDI_ BIT1 3

IDI_BIT1 BITMAP "picture1.bmp"

В якості ресурсів можуть виступати і рядки. Для їх задання використ. STRINGTABLE:

#define STR1 11

#define STR2 12

STRINGTABLE

{

STR1, "Повідомлення"

STR2, "Версія програмами 1.00"

}

Завантажуються рядки з файлу функцією LoadString().

Діалогове вікно не містить ідентифікатора. Звертання до діалогу відбув. за його іменем.

#define WS_SYSMENU 0x00080000L

#define WS_MINIMIZEBOX 0x00020000L

#define WS_MAXIMIZEBOX 0x00010000L

DIAL1 DIALOG 0, 0, 240, 120

STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX

CAPTION "Приклад діалогового вікна"

FONT 8, "Arial"

{

}

Діалогове вікно дуже похоже на звичайне вікно. Воно має свою процедуру, яка має ті самі параметри, що і процедура звичайного вікна. Повідомлення, які приходять на процедуру діалогового вікна, значно менші. Але ті, які є в діалогового вікна, в основному співпадають з аналогічними повідомленнями для звичайного вікна. Тільки замість повідомлення WM_CREATE приходить повідомлення WM_INITDIALOG. Процедура може повертати нульове чи ненульове значення.

Різниця. Коли створюємо звичайне вікно, то його властивості обумовлюються 3 факторами:

  1. властивостями класу;

  2. властивостями, що обумовлюються при створенні вікна;

  3. реакцією процедури вікна при визначенні повідомлення.

При створенні діалогового вікна всі властивості задані в ресурсах. Частина їх задається, коли при виклику функції створення діалогового вікна (DialogBox, DialogBoxParam і т.д.) неявно викликається функція CreateWindow. Інша частина властивостей буде визначатись поведінкою внутр. функції, яку буде створювати сама система при створенні діалогового вікна.

Якщо в діалоговому вікні щось відбувається, то повідомлення спочатку приходить на внутрішню процедуру, а потім викликається процедура діалогового вікна, яку ми створюємо в програмі. Якщо процедура повертає 0, то внутр. процедура продовжує обробку даного повідомлення, інакше повідомлення не обробляється.

Меню. Опис в файлі ресурсів:

MENUP MENU

{

POPUP "&Первый пункт"

{

MENUITEM "&Первый", 1

MENUITEM "В&торой", 2

}

POPUP "&Второй пункт"

{

MENUITEM "Трети&й", 3

MENUITEM "Четверт&ый", 4

POPUP "Еще подмен&ю"

{

MENUITEM "Десятый пунк&т", 6

}

}

MENUITEM "Вы&ход", 5

}

& - виклик клавішами.

Пункти меню можуть містити додаткові параметри, які визначають додаткові властивості цих пунктів:

  • CHECKED – пункт відмічений галочкою;

  • GRAYED – елемент недоступний (має сірий колір);

  • HELP – пункт може бути зв’язаний з допомогою;

  • MENUBARBREAK – для горизонтального пункту це означає, що поначинаючи з нього горизонтальні пункти розміщуються в новій стрічці. Для вертикального пункту – в новому стовпці. При цьому може бути розділова лінія;

  • MENUBREAK - аналогічно попередньому, але розділової лінії немає;

  • INACTIVE - неактивний;

На початку роботи програми MENU треба завантажити LoadMenu() і встановити SetMenu().

Акселератори. Дозволяють вибрати меню просто комбінацією клавіш. Таблиця акселераторів є ресурсом, ім’я якого має збігатись з ім’ям меню, для якого вона призначена.

Ім’я ACCELERATORS

{

Клавіша 1, ідентифікатор пункту меню (1) [, тип] [, параметр]

Клавіша N, ідентифікатор пункту меню (N) [, тип] [, параметр]

}

Клавіша – це або символ в «», або ASCII-код, або віртуальна клавіша. Якщо спочатку стоїть код символу, то тип задається як ASCII. Якщо використ. віртуальна клавіша, то тип є VIRTUAL.

Параметр може приймати значення:

  • NOINVERT – вибраний пункт меню не буде підсвічуватись

  • ALT

  • CONTROL - крім заданих клавіш, треба щоб була нажата ще одна клавіша

  • SHIFT

При роботі з акселераторами треба:

  1. завантажити таблицю акселераторів LoadAccelerators();

  2. повідомлення, яке прийшло від акселератора, треба перетворити в повідомлення WM_COMMAND. Для цього використ. функцію TranslateAccelerator().

Дана функція перетворює повідомлення WM_KEYDOWN і WM_SYSKEYDOWN в повідомлення WM_COMMAND і WM_SYSCOMMAND відповідно. При цьому в старшому слові параметра WPARAM міститься 1, що є ознакою акселератора. В молодшому слові міститься ідентифікатор пункту меню. WM_COMMAND і WM_SYSCOMMAND потрібні для виведення повідомлення.

Функція TranslateAccelerator() повертає ненульове значення, якщо перетворення повідомлення було здійснено вдало, інакше NULL. Виклик функції включають в цикл обробки повідомлень:

MSG_LOOP:

PUSH 0

PUSH 0

PUSH 0

PUSH OFFSET MSG

CALL GetMessageA@16

CMP EAX, 0

JE END_LOOP

;транслювати повідомлення акселератора

PUSH OFFSET MSG

PUSH [ACC]

PUSH [NEWHWND]

CALL TranslateAcceleratorA@12

CMP EAX, 0

JNE MSG_LOOP

PUSH OFFSET MSG

CALL TranslateMessage@4

PUSH OFFSET MSG

CALL DispatchMessageA@4

JMP MSG_LOOP

END_LOOP

Функція TranslateAccelerator() має 3 параметри: 1-й – дескриптор додатку чи вікна де вона використовується, 2-й – ідентифікатор таблиці акселераторів, який отримуємо ф-єю LoadAccelerators(), 3-й – адрес, де міститься повідомлення, яке отримуємо ф-єю GetMessage().

Даний цикл обробки можна використ. і в діалогових вікнах. Якщо діалогове породжується звичайним вікном, то цикл буде працювати. У випадку, коли основне вікно є діалоговим, а породжуване модальним діалоговим, то працювати не буде, немодальне – буде.

Створення немодального - CreateDialog. Знищення немодального - DestroyWindow.

Відображення на екрані немодального вікна – треба вказати йому властивість WS_VISIBLE або після створення діалогу вказати ф-ю ShowWindow.

З моменту створення ОС Windows основною ідеєю було створення цієї ОС незалежною від апаратної частини і від периферійних пристроїв. Щоб підтримати цю незалежність з боку ОС використ. бібліотека GDI32.dll, а з сторони периферійних пристроїв – драйвери. Сполучною ланкою між ними виступає контекст пристрою DeviceContext. Якщо програмі треба виконати обмін даним з зовн. Пристроєм, то вона має повідомити бібліотеку про необхідність підготувати пристрій до операцій вводу/виводу. Формується структура з рядком характеристик даного пристрою, яка записується в контекст пристрою і програма отримує ідентифікатор цього контексту. Напряму програма не може звертатись до процедури, тільки через функцію. Коли програма завершує роботу з контекстом, його треба явно видаляти, оскільки система сама не видаляє.

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

Контексти пристроїв зберігаються в кеші. Дескриптор загального контексту программа отримує за допомогою функцій GetDC(), GetDCEx() або BeginPaint(). Після того, як програма попрацювала з дисплеєм, вона повиина звільнити контекст функцією ReleaseDC() або EndPaint(). Після того, як контекст дисплея звільнений, всі зміни, які були внесені в нього програмою, пропадають і при повторному отриманні контексту всі дії по зміні контексту необхідно виконати знову.

Приватний контекст відрізняється від загального тим, що зберігає зміни навіть після того, як прикладна програма звільнила його. Приватний контекст не зберігається в кеші, тому прикладна програма може не звільняти його. Звичайно, що у цьому випадку за рахунок використ. великого об’єму пам’яті отримується більш висока швидкість роботи з дисплеєм.

Для роботи з приватний контекстом потрібно при реєстрації класу вказати стиль CS_OWNDC. Після цього програма може отримувати дескриптор контексту пристрою точно так само, як і в випадку загального контексту. Система видаляє загальний контекст в тому випадку, коли видається вікно.

При роботі з контекстами необхідно запам’ятати, що дескриптори контексту за допомогою функції BeginPaint() необхідно отримувати тільки в випадку обробки повідомлення WM_PAINT. У всіх випадках треба використ. функції GetDC() чи GetDCEx().

Інформ. контекст – ще одна проміжна ланка. Не є фактично контекстом певного пристрою і служить тільки для отримання інформації про реальний контекст пристрою. Створюється функцією CreateDC. Працює набагато швидше за реальні контексти. Видаляється функцією DeleteDC.

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

  1. отримуємо ідентифікатор того контексту пристрою, куди буде виводитись зображення – hDC;

  2. отримуємо ідентифікатор зображення, яке буде виводитись – hBitmap;

  3. створюємо сумісний з даний пристроєм контекст пам’яті – CreateCompatibleDC;

  4. вибираємо зображення з п.2. як поточне для даного контексту пам’яті – hCompatibleDC;

  5. копіюємо контекст з пам’яті в контекст пристрою – hCompatibleDС -> hDC;

  6. видаляємо сумісний контекст – hCompatibleDC;

  7. видаляємо карту зображень – hBitmap;

  8. видаляємо контекст пристрою – hDC;

Переважна більшість функцій, які працюють з віконними додатками, визначають координати відносно початку робочої віконної області. При цьому логічні одиниці, в яких вимірюються координати залежать від режиму відображень, що встановлені для даного конкретного вікна. При виводі інформації на екран чи пристрій одиниці догічних координат перетворюються в фізичні (пікселі). Встановлюється режим відображення GetMappingMode, де 1-й – ідентифікатор контексту пристроою, 2-й – визначає режим відображення. Режими:

MM_TEXT логічна 1 = 1 піксель, початок координат – верхній лівий кут вікна(х вправо, у вниз)

MM_LOMETRIC логічна 1 = 0.1 мм, напрямок осей звичайний

MM_HIMETRIC логічна 1 = 0.01 мм, напрямок осей звичайний

MM_LOENGLISH логічна 1 = 0.01 ", напрямок осей звичайний

MM_HIENGLISH логічна 1 = 0.001 ", напрямок осей звичайний

MM_TWIPS логічна 1 = 1/20 поліграфічної точки = 1/1440 ", напрямок осей звичайний

MM_ISOTROPIC x = y, логічна 1 і напрямок осей визначається користувачем

MM_ANISOTROPIC x! = y, логічна 1 і напрямок осей визначається користувачем

За замовчуванням йде режим MM_TEXT.

В Windows при виведенні зображення на екран створюється віртуальне вікно, на яке записується зображення і коли система отримує WM_PAINT, то вміст вікна буде копіюватись на реальне вікно. При цьому можна використ. 1 з 3 методів:

  1. робоча область вікна може бути відновлена, якщо вміст формується за допомогою певних обчислень (графіки, фракталів);

  2. послідовність подій, що формують робочу область зображення можна зберегти, а потім програти потрібну кількість разів;

  3. в цих 2 методах можна обійтись без віртуального. Тут створюється віртуальне вікно і вивід відбув. за такою схемою:

  • створ. вікна (створ. сумісний контекст пристрою – CreateCompatibleDC, створ. сумісну карту зображень – CreateCompatibleBitmap, вибрати перо з кольором, створ. бітовий шаблон – PatBlt;

  • вся інформація виводиться на віртуальне вікно – InvalidateRect;

  • при отрим. Повідомл. WM_PAINT ми переписуємо вміст віртуал. вікна в реальне вікно – BitBlt.

Функції для малювання:

  1. встановлення поточної позиції – MoveToEx. Має 4 параметри: 1-й – контекст пристрою, 2-й, 3-й – координати точки (Х,У), 4-й – вказівник на структуру Point, де будуть зберігатись координати старої поточної позиції.

  2. малювання 1 пікселя (встановлення кольору) – SetPixel. 4 параметри: 1-й – контекст пристрою, 2-й, 3-й – координати точки (Х,У), 4-й – колір в RGB-форматі – подвійне слово 0х00bbggrr, де молодший байт – інтенсивність. При нормальному завершенні повертає попередній колір. Якщо ж -1, то або невдалось промалювати, або піксель вийшов за межі робочої області.

  3. LineTo – провести лінію від поточної точки до точки з заданими координатами, яка тепер стає поточною.

  4. Arc – малювання дуги. 7) Rectangle – малювання прямокутника.

  5. RoundRect – малювання прямокутника із заокругленими кутами.

  6. Ellipse, Pie – малювання еліпсів і секторів еліпсів.

Малювання графічних примітивів відбув. за допомогою пер. Є 3 пера: BLACK_PEN, WHITE_PEN, NULL_PEN (прозоре). По замовчуванню - BLACK_PEN. Ідентифікатор кожного з пер можна отримати – GetStackObject. За допомогою CreatePen() можна створ. додатк. пера, де 1-й – стиль пера, 2-й – колір:

PS_SOLID 0 суцільна лінія PS_DASH 1 пунктирна лінія

PS_DOT 2 лінія з крапок PS_DASHDOT 3 штрих-пунктир

PS_DASHDOTDOT 4 дві крапки-тире PS_MULL 5 прозоре перо

PS_INSADEFAME 6 при малюванні замкнутої фігури границя йде не по зовн. краю, а по середній лінії.

Кисть створюється функцією CreateSolidBrush().

Робота з файлами – основна характеристика ОС. Windows зараз використ. декілька файлових систем. Впринциці передбачено 2 рівня звертання до магнітних дисків – верхній і нижній. При роботі на нижньому рівні програміст звертається до програм управління диском. Типовими операціями для нього є запис і читання секторів чи форматування доріжок. При цьому файлова система не використ, а потрібна інфа знаход. за номером поверхні циліндра і сектора. Дані операції викон. за допомогою переривання BIOS, але при цьому програма має працювати в захищеному режимі. Верхній рівень реалізується за допомогою переривання DOS. Дане переривання вже підтримує функції обслуговування файлової системи і дає можливість оперувати поняттями, як логічний диск, папка, файл.

Для пошуку файлів використ. 2 функції: FindFirstFile() і FindNextFile(). Ще деякі функції:

  1. GetFullPathName() – шукає в поточному диску, папці. Функція повертає к-сть байт, що записані в буфер. Якщо значення більше за розмір буфера, то виконання операції може бути некоректним. Якщо повертає 0 – виникла помилка.

  2. SearchPath() – має вказівник на рядок, що визначатиме шляхи до тих каталогів, в яких буде здійснено пошук файлу. Якщо він не заданий, то пошук буде такий: - шукатиме в поточній папці, - поступово у вкладених, - в системних папках, - в основних папках Windows, - в тих папках, які були вказані в змінних оточення.

  3. FindFirstFile() і FindNextFile() працюють в парі. FindFirstFile() при успішному пошуку знаходить файл і повертає його ідентифікатор, а FindNextFile() знаходить файл, отримує ідентифікатор і продовжує пошук. FindFirstFile() має 1 параметр – вказівник на структуру, а FindNextFile() - ідентифікатор файлу і теж вказівник на структуру:

FIND STRUCT

;атрибут файлу

ATR Dword ?

;час створення

CRTIME Dword ?

Dword ?

;час модифікації

WRTIME Dword ?

Dword ?

;розмір

SIZEM Dword ?

SIZEL Dword ?

;довге ім’я

NAM DB260 DUP(0)

;коротке ім’я

ANAM DB14 DUP(0)

FIND ENDS

Ця пара дозволяє пошук на всьому диску і дозволяє при пошуку використ. *,?. Але повільніші.

Атрибути файлів:

  • FILE_ATTRIBUTE_READONLY equ 1h

  • FILE_ATTRIBUTE_HIDDEN equ 2h – схований файл, який недоступний при звич. перегляді

  • FILE_ATTRIBUTE_SYSTEM equ 4h

  • FILE_ATTRIBUTE_DIRECTORY equ 10h

  • FILE_ATTRIBUTE_ARCHIVE equ 20h

  • FILE_ATTRIBUTE_NORMAL equ 80h – звичайний файл, над яким можна викон. операції

  • FILE_ATTRIBUTE_TEMPORARY equ 100h – тимчасовий файл, який система може видалити

  • FILE_ATTRIBUTE_COMPRESSED equ 800h – стиснутий самою системою файл (якщо каталог, то всі всередині файли теж стиснуті)

  • FILE_ATTRIBUTE_OFFLINE equ 1000h – дані з файлу зараз є недоступними

Отримати значення атрибута можна функцією GetFileAttribute(), а встановити - SetFileAttribute().

Характеристики файлів:

  • числові. FAT-32 для файлу має 3 числові характеристики - час створення, час останньої модифікації і час останнього доступу. Час відраховується в наносекундах і починається з 12:00 1 січня 1600р. При цьому час зберігається в 2 32-бітних величинах – «універсальних координатах» і для його використ. треба перетвор. в локальний час - FileTimeToLocalFileTime(). Отримати ці числові дані про файл – GetFileTime().

  • довжина файлу або в 2-х 32-бітних величинах, або в 1-й 64-бітній. Розмір файлу можна отримати функцією GetFileSize().

  • ім'я файлу. Розрізняється довге і коротке ім’я, так само як довгий і вкорочений шлях. Перетворення одне в друге – GetShortPathName(),GetFullPathName(). Загальна структура запису:

+0 – ім'я файлу чи каталогу, яке вирівняне по лівому краю і доповнене пробілами

+8 – розширення імені файлу, яке вирівняне по лівому краю і доповнене пробілами

+11 – атрибут файлу +12 – час доступу +14 – час створення +16 – дата створення

+18 – дата доступу +22 – час модифікації +24 – дата модифікації +28 – розмір файлу в байтах

ОС Windows надає ряд функцій АРІ, які полегшують роботу над групами файлів чи каталогів. Такі функції починаються з SH. SHFileOperation() – копіює, переносить, перейменовує, видаляє файли. Має 1 параметр – вказівник на структуру, яка визначає яку саме операцію і як саме здійснити.

SHStruct

Hwnd Dword ? //дукстриптор вікна, де буде результат

wFunc Dword ? //код операції – FO_COPS, FO_DELETE, FO_MOVE, FO_RENAME

pFrom Dword ? //назва файлу, групи чи каталогу, над яким викон. операція

pTo Dword ? //ім’я файлу чи групи імен куди буде запис. Код операції

fFlags Dword ? //прапорець, що визначає характер операції

fanyOperationsAborted Dword ? //визначає чи переривалась операція (якщо 0 – то так)

hNameWapping Dword ? //дескриптор відображуваного в пам’яті файлу

pSizeProgressTitle Dword ? //вказівник на рядок заголовок для діалогового статусу

SHENDS Параметри прапорців:

  • FOF_ALLOWUNDO – треба зберегти інфу для повернення у вихідний стан якщо можливо

  • FOF_CONFIRMMOUSE – не реалізоване

  • FOF_FILESONLY – виконувати тільки над тими файлами, для яких встановлений шаблон

  • FOF_MULTIDESTFILES – параметр функції pTo матиме декілька результуючих значень

  • FOF_NOCONFIRMATION – відповідати ствердно на всі запити

  • FOF_NOCONFIRMMKDIR – створювати каталог без запиту

  • FOF_RENAMEONCOLLISION – додавати файлам нові імена якщо файл з даним ім’ям існує

  • FOF_SILENT – не показувати вікно статусу

  • FOF_SIMPLEPROGRESS – показувати вікно статусу, але не показувати імена файлів

  • FOF_WANTMAPPINGHANDLE – файл, що відображається в пам’яті, має бути заповненим.

Головна ознака текстового файлу – містить рядки різної довжини, що відрізняються розділювачами. Найчастіше це послідовність кодів 13 та 10. В Асемблері використ. переважно такі способи роботи з текст. файлами:

  1. побайтове читання з файлу

  2. якщо знаємо довжини рядків, то можна зчитати в буфер кусок файлу, знаходити символ кінця рядка, робити зміни, поправку на довжину рядка і знову зчитуємо той самий розмір в буфер

  3. зчитати в буфер весь файл (або велику частину)

  4. використ. файли, які відображаються в пам’ять. Для цього:

  • створюємо новий файл – CreateFile().

  • відображаємо даний файл в пам’ять – CreateFileMapping().

  • копіюємо вміст файлу в відображуваний – MapViewOfFile().

  • виконуємо всі дії над файлом.

  • якщо потрібно, зберігаємо файл на фізичний носій – FlushViewOfFile().

  • закриваємо відображуваний файл - UnMapViewOfFile().

  • закриваємо відображуваний і створений файли.

Стратегія обробки файлів.

Переваги бібліотеки С:

  1. код переноситься легко на різні системи

  2. функції С набагато простіші при використанні за АРІ

  3. має велику кількість функцій для роботи з символами та рядками

  4. не має прямих аналогів серед функцій АРІ

  5. функції С здатні працювати в середовищах з багато потоковою підтримкою

Недоліки бібліотеки С:

  1. засоби С не забезпечують управління каталогами в обхід дерева каталогів і в більшості випадків не дозволяють встановлювати атрибути файлів

  2. не надає можливості відображення файлів, асинхронний ввід/вивід, взаємодію між процесами в файлах

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

Для блокування є функції LickFile() i LockFileEx().

LockFileEx() відноситься до функцій розширеного вводу/виводу. Вона блокує ділянку відкритого файлу для роздільного доступу.

BOOL LockFileEx()

HANDLE hFile //дескриптор відкритого файлу

DWORD dwFlags //прапорець, що визначає вид блокування файлу

DWORD dwReserved // = 0

DWORD nNumberOfBytesToLockLow // визначають ділянку блокування,

DWORD nNumberOfBytesToLockHigh // якщо 0, то весь файл LPOVERLAPPED lpOverLapped //вказівник на структуру OverLapped

Для розблокування використ:

BOOL UnLockFileEx()

HANDLE hFile //дескриптор відкритого файлу

DWORD dwReserved // = 0

DWORD nNumberOfBytesToLockLow // визначають ділянку блокування,

DWORD nNumberOfBytesToLockHigh // якщо 0, то весь файл LPOVERLAPPED lpOverLapped //вказівник на структуру OverLapped

Коли відбув. блокування, треба звертати на увагу на:

  1. межі області розблокування повинні повністю співпадати з межами області, що булла заблокована, тобто не можна розблокувати частину області (інакше видасть, що області немає)

  2. не дозволяється об’єднання двох чи більше раніше заблокованих областей

  3. щойно створена область блокування і існуюча не повинні перекриватись. Проте можливо, щоб область блокування виходила за межі фалу

  4. блокування не успадковується щойно створеними процесами

Реєстр – централізована ієрархічна БД, що зберігає інформацію про параметри конфігурації системи і встановлених додатків. Доступ до реєстру здійснюється через розділи чи ключі реєстру (registery keys), що відіграють роль папок, каталогів у файловій системі.

Розділ може містити або інші розділи, або пари ім’я-значення, в яких між іменем і значенням існує зв’язок, як між іменем і вмістом файлу. В цих парах міститься інформація:

  1. номер версії ОС, номер сборки, інформація про зареєстр. Користувачів

  2. номер версії програми (додатку), номер сборки, інформація про роботу

  3. техн. Інформація: к-сть і тип процесорів, ОП

  4. налаштування кожного з користувачів

  5. інформація про встановлені служби

  6. відображення мережевих адрес на імена, що використ. локал. комп’ютерами

Доступ до реєстру йде через розділ. Є деяка к-сть розділів, що відіграє роль точок входу в розділ:

  1. HKEY_LOCAL_MACHINE – зберіг. інфа про обладнання локал. комп’ютера і його ПЗ

  2. HKEY_USERS – зберіг. інфа про налаштування користувача

  3. HKEY_CURRENT_CONFIG – зберіг. інфа про налашт. техн. параметрів

  4. HKEY_ CURRENT_USER – містить інфу, що визначається користувачем

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

Для вказівки існуючих розділів і створення нових використ. дескриптори типу HKEY.

  1. LONG RegOpenKeyEx() – функція відкриття розділу

HKEY hKey //вказівник на поточний відкритий реєстр

LPCTSTR lpSubKey //вказівник на рядок імені розділу (найчастіше шлях)

DWORD dwOptions // = 0

REGSAM samDesired //маска доступу (all_access, read, write, set_value, query_value)

PHKEY phkResult //вказівник на дескриптор розділу, що відкривається

Функція повертає результат ERROR_SUCCESS, інакше помилка.

  1. RegCloseKeyEx() – функція закриття. Єдиний параметр – дескриптор відкритого розділу.

  2. RegEnumValue(), RegQueryValue() – отримання пар ім'я-значення. Взаємодоповнюючі.

  3. RegEnumKeyEx() – отримання імен розділів заданого розділу.

  4. LONG RegCreateKeyEx() – функція створення розділу

HKEY hKey //вказівник на поточний відкритий реєстр

LPCTSTR lpSubKey //вказівник на рядок імені нового розділу

DWORD Reserved

LPTSTR lpClass

DWORD dwOptions // = 0

REGSAM samDesired //маска доступу (all_access, read, write, set_value, query_value)

LPSECURITY_ATTRIBUTES lpSecurity_Attributes//атрибути доступу (read,write, readwrite)

PHKEY phkResult

LPDWORD lpdwDisposition

  1. RegDeleteKeyEx() – функція видалення розділу. Має 2 параметри – дескриптор відкритого розділу і ім’я підрозділу.

Управління значеннями реєстру відбув. за допомог. RegEnumValue(). Вона повертає рядок, що містить ім’я вказаного параметра і розмір даних. Також отрим. значення вказаного параметра і його тип.

RegQueryValue() працює аналогічно, тільки замість вказівника індексу до даної функції звертаються через параметр за іменем.

Для встановлення значення параметра відкритого розділу використ. функція RegSetValueEx(). Тут задається ім’я параметра, його тип і значення.

Знищити значення – RegDeleteValue().

Під процесом в СП розуміється об'єкт, що створюється ОС при завантаженні виконавчого модуля і який отримує в одноосібне використання:

  1. віртуальну пам'ять, що ОС для нього виділяє

  2. дескриптори відкритого ним файлів

  3. список завантажених ним в його пам’ять динам. модулів

  4. створені ним підпроцеси чи потоки, що виконуються незалежно один від одного в пам’яті головн. процесу

Створюється функцією CreateProcess(). Її 6 параметр – прапорець, який відповідає за властивості процесу, що породжується. Його можливі значення:

  • CREATE_DEFAULT_ERROR_MODE – новий процес не успадковує режим помилок

  • CREATE_NEW_CONSOLE – дочірній процес успадковує консоль батьківського

  • CREATE_NO_WINDOW – використ. у випадку консолей і коли дана програма запуск. без консольного вікна

  • DEBUG_PROCESS – батьківський процес буде розглядатись як відлагоджувач, а дочірній – процес, що відлагоджується батьківським

  • DEBUG_ONLY_THIS_PROCESS – якщо викликаючий прапорець відлагоджується ззовні, то дочірній теж відлагоджується

  • DETACHED_PROCESS – новий процес не успадковує консоль батьківського

9 параметр – вказівник на структуру, що містить інфу про вікно дочірнього процесу. Одним з параметрів структури є прапорець, який набуває таких значень:

START_USESHOWWINDOW equ1h – додати поле ShowWindow

START_USESIZE equ2h – дозволити змінювати розміри

START_USEPOSITION equ4h – дозволити змінювати позицію курсору

START_USECOUNTCHARS equ8h

START_USEFILLATTRIBUTE equ10h

Часові характеристики процесу – BOOL GetProcessTimes()

HANDLE hProcess //ідентифікатор процесу

LPFILETIME lpCreationTime //час створення

LPFILETIME lpExitTime //час завершення

LPFILETIME lpKernelTime //час, який витрачається ядром процесора

LPFILETIME lpUserTime //час, який використ. користувачем

Знищити процес можна 2 функціями: ExitProcess() і TerminateProcess(). У звичайних умовах процес завершується, коли він сам чи один з належних йому потоків викликає ExitProcess() – має 1 параметр (ідентифікатор). При цьому відбув. Такі дії:

  1. викликаються функції деініалізації всіх підключених dll-бібліотек

  2. закриваються чи знищуються всі об'єкти, відкриті чи створені процесом

  3. стан процесу змінюється на звільнений – signaled, що є сигналом для всіх інших потоків чи процесів, які очікують завершення роботи даного процесу

  4. лічильник числа користувачів процесу зменш. на 1

Взнати чи є процес остаточно завершеним - GetExitProcessCode(), де якщо не є завершеним, то повертає – STILL_ACTIVE.

Завершення роботи процесу не приводить до автоматичного завершення роботи всіх породжених ним процесів.

Аварійний спосіб завершення роботи процесу викликається ззовні. Недоліки: можуть бути незавершені дії, не вилучає бібліотеки dll з пам'яті.

Потік (Thread) - незалежна одиниця виконання в контексті процесу. Потоки, що належать 1 процесу, розділяють загальні дані і код. Тому:

  1. у кожного потоку є власний стек, який він використ. при виклику функцій і обробці даних

  2. кожен потік може розприділяти індекси власних локальних областей зберігання даних TLS – Thread Local Storage, які надають у розпорядження потокам невеликі масиви даних і забезп. захист даних 1 потоку від даних іншого.

TLS вигляд. Індекси спочатку є нерозподілені і потік в любий момент може їх розподіляти. Мінімальна рядків любого потоку визнач. значенням TLS_MINIMUM_AVIALABLE (по замовч – 64). Функція TLSAlloc() повертає розподілений індекс 0,1,…(якшо -1, то немає розподілених).

Отримати значення можна функцією TLSGetValue за вказівкою індексів таблиці TLS.

Встановити значення BOOL TLSSetValue()

DWORD dwTlsIndex

LPVOID lpValue

Звільнити – TLSFree().

Створення потоку – CreateThread(). При створенні потоку треба вказувати розмір стеку, що буде відводитись під потік (інакше виділить тільки 1 МБ), вказівник на функцію, що буде передавати аргумент потоку (функція буде повертати код завершення роботи потоку), вказівник на структуру атрибутів захисту.

GetCurrentThread() – повертає псевдо дескриптор потоку

GetCurrentThreadId() – повертає ідентифікатор потоку

GetThreadId() – дозволяє отримати дескриптор потоку, якщо відомий його ідентифікатор

OpenThread() – створює дескриптор потоку, якщо відомий його ідентифікатор

SuspendThread (HANDLE hThread) – призупинення потоку

ResumeThread (HANDLE hThread) – відновлення потоку

В кожного потоку є лічильник і він відновлюється, коли count = 0.

Завершення роботи з потоком – ExitThread(), TerminateThrеad().

Чи потік повністю завершив свою роботу – GetExitCodeThread().

Недоліки однопотокових програм:

  1. перемикання між процесами, що виконуються, споживає значну частину часових ресурсів і пам’яті, що понижує ефективність програм;

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

  3. тяжко організовувати просте і ефективне управління декількома паралельно виконуючими задачами, що взаємодіють між собою;

  4. тісно пов’язані з виконанням операції вводу/виводу програми вимушені обмежуватись простою ідеєю читання/зміна/запис, особливо коли йде робота з файлами.

Недоліки багатопотокових програм:

  1. потоки розділяють загальну пам’ять і інші ресурси і при недостатньому плануванні можуть виникати помилки, коли 1 потік втручається в дані іншого потоку і крім виявлення помилок, немає синхронізації;

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

Для синхронізації роботи потоків Windows надає такі можливості:

  • використ. критичних ділянок коду;

  • використ. мютексів;

  • використ. семафорів і події.

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

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

InterlockedIncrement(lplong lpaddend) – збільшує значення параметра на 1.

InterlockedDecrement(lplong lpaddend) – зменшує значення параметра на 1.

InterlockedExchangeAdd(lplong lpaddend, long increment) – збільшує значення змінної на increment.

InterlockedExchange(lplong lpaddend, long increment) – заміна значення змінної на increment.

Функції є атомарними і їх не можна викликати 2 рази підряд.

5 правил безпечного багатопотокового коду:

  1. змінні, що є локальними стосовно потоку, не повинні бути статистичними і їх варто розміщати в стеці потоку або в TLS;

  2. коли функцію можуть викликати декілька потоків і існує специфічний параметр, що характеризує стан потоку (лічильник), то його значення теж має зберігатись в TLS чи в тій структурі даних, що є визнач. для даного потоку. Зберігати цю змінну в стек не рекомендується;

  3. не створювати умов змагання між потоками;

  4. потоки не мають змінювати оточення процесу, бо це автоматично вплине на всі інші потоки. Тобто потік не має визначати дескриптори стандартного вводу/виводу і змінні оточення. Це не стосується тільки головного потоку.

  5. змінні, що поділяються всіма потоками, мають бути статистичними чи зберігатись в глобальній пам’яті з використанням volatile (специфікатор пам’яті, який гарантує, що після кожної зміни змінної воно обов’язково буде повертатись в пам’ять, регістр). Вони додатково мають бути захищені з використ. одного з способів синхронізації.

Об’єкт критичної ділянки коду – ділянка програми, що має виконуватись 1 потоком. Найпростіший механізм – використ. критичних секцій CRITICAL_SECTION. Її об’єкти можна ініціалізувати і видаляти. Вони не мають дескрипторів і не можуть спільно викор. різними процесами. Якщо використ. змінні, то вони мають оголошуватись як змінні CS і коли говорять про CS, то кажуть, що потоки можуть входити і виходити з CS і при цьому виконання коду дозволяється лише 1 потоку. Один і той самий потік може знаход. в різних ділянках CS, якщо вони розташ. в різних ділянках коду.

VOID InitializeCriticalSection (LPCRITICALSECTION lpCriticalSection) - ініціалізація

VOID DeleteCriticalSection (LPCRITICALSECTION lpCriticalSection) – знищення

VOID EnterCriticalSection (LPCRITICALSECTION lpCriticalSection) – блокує потік, якщо на даній CS присутній інший потік. Потік, що очікує, розблокується тоді, коли інший потік покине СS: VOID LeaveCriticalSection (LPCRITICALSECTION lpCriticalSection)

Потік, який володіє об’єктом CS, може повторно заходити в цей самий потік без блокування. Тобто об’єкти CS є рекурсивними. При цьому підтримується лічильник входжень потоку в даний об’єкт і щоб потік поступився правами на даний об’єкт іншому потоку, він повинен залишити об’єкт стільки разів, скільки зайшов.

VOID TryEnterCriticalSection (LPCRITICALSECTION lpCriticalSection) – перевіряє чи не належить об’єкт CS на даний момент іншому потоку. Якщо повертає істину, то потік може зайти в дану CS.

Оскільки CS не є об’єктом ядра, вони є більш продуктивними і швидкодіючими. Якщо треба синхронізувати роботу потоків в межах 1 процесу, то треба використ. цей механізм синхронізації.

Об’єкти взаємного виключення (mutual exception – mutex) є об’єктами синхронізації ядра і забезп. більш універсальну функціональність в порівнянні з об’єктом CS. На відміну CS, мютекси мають імена і дескриптори. Тому їх можуть використ. потоки, що належать різним процесам.

Вцілому мютекси є аналогічними CS, але мають інтервал кінцевого очікування. Тобто потік набуває права володіння мютексом чи блокує його шляхом виклику функції WaitForSingleObject(), WaitForMultiObject() з використ. дескриптора мютекса. Потік поступається правами шляхом виклику ReleaseMutex(). Як і у випадку CS, потоки можуть набувати правами володіти мютексом декілька разів і стільки ж разів його звільняти.

HANDLE CreateMutex() - створення

LPSECURITY_ATTRIBUTES lpsa //вказівник на структуру

BOOL binitialOwner //істина – потік, що створює мютекс, зразу набуває права володіння

LPCTSTR lpMutexName //вказівник на рядок, що містить ім’я мютекса (менше 260 симв)

Якщо не задати мютексу ім’я, то можна до нього звертатись за дескриптором.

Відкриття – OpenMutex()

Звільнення мютекса, яким володіє потік, що викликав функцію – ReleaseMutex(). Якщо мютекс не належить потоку, виникає помилка. Якщо потік завершив роботу і не звільнив мютекс, то мютекс «покинутий» (abondened), дескриптор його перейде в сигнальний стан. Перевірити мютекс на те, чи є він покинутим – WaitForSingleObject().

При використ. мютексів і об'єктів CS найбільш небезпечна ситуація – взаємоблокування роботи потоків. Щоб уникнути, треба:

  • краще планувати;

  • використ. WaitForSingleObject() з чіткою вказівкою кінцевого інтервалу. Якщо по закінченню виявляється, що мютекс належить іншому потоку, потік ніби «засинає» і через деякий час знову пробує отримати мютекс. І так поки не отримає.

  • використ. WaitForMultiObject(). Тут прапорець fWaitAll треба встановити в істину і потік після 1 атомарної операції буде захоплювати або всі мютекси, або жодного.

Порівняння мютексів і CS (є подібними, відрізн. наявністю дескриптора):

  1. мютекси, що покинуті, переходять в сигнальний стан і інші потоки не блокуються. Якщо CS, то блокується;

  2. мютексам можна присвоювати іменам і тому їх можуть використ. потоки. В CS не можна.

  3. в мютексів є можливість організовувати очікування потоків для отримання потрібного мютекса; в об’єкта CS можна організовувати опитування їх стану;

  4. при створенні мютекса потік може зразу отримати право володіти ним, що знизує змагальність між потоками. При створенні об’єкта CS такого немає і потоки змагаються на право володіння;

Якщо переваги є несуттєвими, то рекомендується використ. CS, оскільки вони продуктивніші.

Семафори є також об’єктом синхронізації, який може бути в сигнальному і несигнальному стані і цей стан залежить від лічильника семафору (якщо >0, то в сигнальному, якщо =0, то в несигнальному). Для роботи використ. 3 функції.

  1. CreateSemaphore() – створює семафор

LPSECURITY_ATTRIBUTES lpsa //вказівник на структуру атрибутів захисту

LONG lpSemInitual //значення (від 0 до мах, на початку 1)

LONG lpSemMax //мах значення

LPCTSTR lpSemname //ім’я

Функція повертає дескриптор семафора

  1. OpenSemaphore() – відкриває існуючий іменований семафор

  2. ReleaseSemaphore() – змінює значення лічильника семафора

HANDLE hSemaphor //дескриптор семафора

LONG cReleaseCount //змінна, яка додається до лічильника (>0)

LPLONG lpPreviousCount //попереднє значення

Функція очікування зменш. значення лічильника семафора на 1.

Коли мах=1, семафори подібні до мютексів. Але немає прав на володіння семафором, тобто семафор може бути звільнений любим потоком, а не тільки тим, що очікує його звільнення. Також в них немає поняття покинутого семафора.

Застосування семафорів:

  1. управління розподілом кінцевих ресурсів. Значення лічильника семафора асоціюється з к-стю доступних ресурсів і мах значення лічильника = мах розміру черги і потік, який є виробником повідомлення, поміщає його в чергу, одночасно викликаючи ReleaseSemaphore() зі значенням 1, семафор йде в сигнальний стан і потоки, які очікують, забирають повідомлення з черги, зменш. значення лічильника на 1.

  2. дросель (throttle) семафора – використ. коли треба обмежити к-сть робочих потоків для зменш. змагальності між ними. Для цього:

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

  • кожен робочий потік повинен очікувати сигнальний стан семафора перш ніж увійти в CS. При вході він зменш. значення лічильна на 1, при виході – збільш.

Таким чином семафори підтримують в робочому стані потрібну к-сть потоків і планувати роботу програми легше. Недолік – мах значення потім змінити не можна.

Події – об’єкти синхронізації ядра. Використовуються для сигналізування іншим потокам, що наступила певна подія. Подія – будь-що (повідомлення і т.п.). Особливість – перехід в сигнальний стан єдиного об’єкта події здатний вивести з стану очікування одночасно декілька потоків. Є 2 події:

  1. manual-reset – скидається вручну – можуть сигналізувати одночасно всім потокам, що чекають на настання цієї події і переводяться в несигнальний стан програми;

  2. auto-reset – скидається автоматично – один потік знаход. в несигнальному стані, решта чекають.

Використовується 5 функцій:

  1. CreateEvenr()

  2. OpenEvent()

  3. SetEvent() – встановлення в сигнальний стан. Якщо подія автоматично скидається, то вона автоматично повернеться в несигнальний стан. При відсутності потоків подія буде в сигнальному стані до появи 1 (тоді веде себе як семафор з лічильником 1).

  4. ResetEvent() – звільнення події

  5. PulseEvent() – звільнення потоків, що очікують настання звільнення події

Подія без імені не використ. потоками інших процесів.

4 моделі використ. подій

Auto

Manual

SetEvent

1

2

PulseEvent

3

4

1 – звільняється 1 потік. Якщо жожен з потоків не чекає настання поідї, то потік, що 1-им перейде в стан очікування з наступної події одразу буде звільнений. Після цього подія автоматично скидається.

2 – звільняються всі потоки, які в даний момент чекають настання події. Подія буде в сигнал. стані поки не буде кинута іншим потоком.

3 – звільняється 1 потік коли він очікує настання цієї події. Подія одразу скидається.

4 – звільняються всі потоки, що в цей час чекали на настання події.

Некоректне використ. подій може привести до помилок взаємоблокування і ці помилки знаходяться найважче.

Синхронізація взаємодії процесів забезпечується за допомогою:

  1. файлів і файлів, що відобр. в пам’ять

  2. сокетів

  3. віддаленого виклику процедур

  4. використання каналів

Основний принцип каналу – на виході одного процесу відбув. буферизація даних і забезп. можливість читання вмісту даного буфера іншим процесом. Процес, що створив канал – сервер, інші – клієнти. При роботі використ. функції ReadFile i WriteFile.

Анонімні канали (Anonimous channels) – забезп. однонаправлену (напівдуплексну) по символьну взаємодію між процесами. Кожен з каналів має 2 дескриптори – дескриптор читання (read handle) і запису (write handle).

BOOL CreatePipe() – створення каналу

HANDLE CreateNamedPipe() – створення іменованого каналу. Повертається його дескриптор.

Процесу потрібно передавати дескриптори для того, щоб він міг зчитувати дані з каналу. Сервер записує, клієнт зчитує (навпаки – ні).

Дескриптор назив. успадкованим, якщо він передається.

Іменовані канали забезп. двонаправлену взаємодію. Можливості ширші:

  1. оскільки двонаправлені, то за допомогою 1 каналу можна здійснити обмін повідомленнями між 2 процесами

  2. орієнтовані на обмін повідомленнями

  3. кожна з систем, що підключена до мережі, може звернутись до каналу за іменем і працювати абсолютно однаково з каналом незалежно де знаходяться процеси - на 1 чи різних машинах

  4. допускається існування декількох каналів, що мають однакові імена

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

У випадку між платформної взаємодії TCP/IP і використ. мережевих додатків, механізм іменованих каналів краще не використ, а використ. WindowsSockets для взаємодії між процесами.

Для створення 1-го екземпляру іменованого каналу використ. функція CreateNamedPipe, де 1-ий – вказівник на ім’я каналу ((\\.\pipe[path]\ім’я каналу). Символ «.» - канал на локальному комп’ютері (прискорює роботу). На віддаленому – без «.».

2-й – задає режим, в якому відкривається канал. Можливі варіанти:

- PIPE_ACCESS_DUPLEX – для читання і запису;

- PIPE_ACCESS_INBOUND – для читання;

- PIPE_ACCESS_OUTBOUND – для запису;

- PIPE_READMODE_BYTE – побайтове читання;

- PIPE_READMODE_MESSAGE

- PIPE_WAIT – працює в блокую чому режимі, поки процес переводиться в стан очікування до завершення операцій в каналі

- PIPE_NOWAIT

- FILE_FLAG_OVERLAPPED – читання/запис з перекриттям

- FILE_FLAG_WRITE_THROUGH – заборона буферизації передачі даних по мережі

3-ий – прапорець. Значення - PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, PIPE_READMODE_ BYTE, PIPE_READMODE_MESSAGE, PIPE_WAIT і PIPE_NOWAIT.

При 1-ому виклику функції CreateNamedPipe відбувається створення самого каналу, при повторних – екземпляри. Закриття останнього відкритого екземпляра іменованого каналу приводить до знищення самого каналу, але після цього дане ім’я каналу можна буде використовувати повторно.

Підключення клієнтів і стан іменованих каналів. Підключення відбув. з 2-х сторін – клієнта і сервера. Вони відрізняються один від одного. Після створення іменованого каналу сервер може очікувати підключення клієнта, використ. функцію

BOOL ConnectNamedPipe()

HANDLE lpNamedPipe //дескриптор каналу

LPOVERLAPPED lpOverLapped //прапорець перекриття

З сторони клієнта відбув. підключення функцією CreateFile, де замість імені – шлях на ім’я каналу.

Сервер створює іменований канал CreateNamedPipe() і встановлює зв’язок з клієнтом функцією ConnectNamedPipe(). Після роботи закриваємо дескриптор.

Клієнт може використ. функцію WaitNamedPipe(), яка використ. клієнтом для синхронізації з’єднань з сервером, тобто функціє перевіряє чи є на сервері незнаверш. виклик даної функції. У випадку наявності, клієнт може створити з’єднання функцією CreateFile() і робити різні зміні з файлом (читання/запис). Після роботи закриваємо дескриптор.

Функції стану іменованих каналів:

  1. GetNamedPipeHandleState() – повертає для заданого відкритого дескриптора каналу інформацію: в якому режимі працює – блокувальному чи ні, працює з повідомленнями чи масивами байт, яка в каналу кількість екземплярів.

  2. SetNamedPipeHandleState() – дозволяє встановити атрибути стану. Параметри задаються не по значенню, а по адресі.

  3. GetNamedPipeInfo() – дозволяє встановити кому належить канал – серверу чи клієнту.

Функції транзакції іменованих каналів. При типовій конфігурації іменованого каналу клієнт виконує операції:

  1. відкриває екземпляр каналу, при цьому створюючи довготривале з’єднання, яке повністю займає канал;

  2. періодично посилає запити про те, чи є щось для нього в каналі і чекає відповіді;

  3. виконує читання/запис;

  4. закриває.

Послідовність викликів читання/запису можна розглядати як 1 транзакцію і викон. за допом. ф-ї:

  • BOOL TransactNamedPipe() – об’єднує ReadFile, WriteFile. Якщо канал малого розміру, то використ. функції приведе до збільш. ефективності – 60%, малого – 20%. Створює постійне з’єднання з каналом.

  • BOOL CallNamedPipe() – об’єднує CreateFile, ReadFile, WriteFile, CloseHandle. Створює тимчасове з’єднання з каналом.

  • BOOL PeekNamedPipe() – перевіряє чи є повідомлення в іменованому каналі. Використ. для опитування стану іменованого каналу. Якщо дані є, то 1-й параметр функції >0. Функція зчитує повідомлення з іменованого каналу, але не видаляє його звідти. Її неможливо блокувати.

Канали Fifo – канали, які передає ОС Uniх. Вони є аналогом іменованих каналів і теж забезп. взаємодію незв’язних між собою процесів. Але, в порівнянні з іменованими каналами, вони:

  1. напівдуплексні;

  2. діють в межах 1 комп’ютера;

  3. орієнтовані на роботу з байтами.

Якщо на комп’ютері використ. канали Fifo, то для кожної відповіді клієнта сервер повинен створити окремий канал, а клієнти можуть посилати запити по одному каналу.

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

  1. посилання повідомлення;

  2. зчитування повідомлення.

Реалізація такого зв’язку може здійснюватись на фізичному рівні через загал. пам’ять чи апаратну шину. Таку організацію реалізувати не складно.

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

  1. створення поштової скриньки;

  2. відправлення повідомлень через поштову скриньку;

  3. зчитування повідомлення;

  4. видалення поштової скриньки.

При НКП є певні особливості:

  • 1 процес може встановити зв’язок з багатьма процесами, що мають доступ до даної скриньки;

  • кожна пара процесів може мати кілька ліній зв’язку – повідомлення посилаються через різні поштові скриньки;

  • зв’язок може бути двонаправленим;

  • зв’язок може бути ненаправленим. Може виникнути проблема синхронізації, яку виріш. так:

  1. обмежують зв’язок між 2 процесами;

  2. дозволити на кожен момент часу отримати повідомлення тільки 1 процесу;

  3. зробити способи нотифікації отримувача.

Комунікацію між процесами можна реалізувати через чергу повідомлень 3 способами:

  1. нульова ємність черги повідомлень – повідомлення не можуть зберігатись в черзі і процес-відправник чекає процес-отримувач;

  2. обмежена ємність черги повідомлень(найбільш вживано) – при реалізації треба кожен раз перевіряти чи в буфері для черги є місце і коли його немає треба організувати очікування процеса-відправника;

  3. необмежена черга повідомлень – використовується коли не треба, щоб процес очікував.

Операціям вводу/виводу властива повільна швидкість виконання в порівнянні з іншими видами обробки, тому що існують затримки:

  • обумовлені витратою часу на пошуки потрібних доріжок і секторів;

  • пов’язані з низькою швидкістю обміну даних між фізичн. пристроями і системною пам’яттю;

  • при передачі даних по мережі з використ. файлових серверів і сховищ даних.

В зв’язку з цими недоліки способом прискорення вводу/виводу є асинхронний ввід/вивід. Методи:

  1. багато потоковий ввід/вивід – кожен з потоків в процесі чи в їх наборі виконує звичайний синхронний ввід/вивід, але при цьому не блокується робота інших потоків і вони працюють в звичайному штатному режимі;

  2. ввід/вивід з перекриттям – потік допускає операцію читання чи запису чи іншу якусь операцію (передачу даних) і продовжує виконання. Якщо йому потрібні дані, то він призупиняється і чекає поки не стане доступний відповідний дескриптор і не наступить задана подія.

  3. Розширений ввід/вивід – асинхронний ввід/вивід з використ. процедур завершення – подібний до попереднього, але під час завершення операцій вводу/виводу система викликає спеціальну процедуру завершення, яка виконується всередині потоку.

Для організації вводу/виводу що перекривається треба використ. структури OverLapped. Вона використ. в функціях LookFileEx() – блокування файлів, ReadFile(), WriteFile(), TransactNamedPipe(), ConnectNamedPipe(). Для організації такого вводу/виводу треба встановити атрибут перекриття (overlapped attribute), який встановлюється при створенні файлу – CreateFile() і задати прапорець File_Flag_Overlapped.

До використ. структури OverLapped є застереження:

  1. не можна повторно використовувати, поки ввід/вивід не завершиться;

  2. не можна повторно використав. подію, яка вказана в структурі до завершення вводу/виводу;

  3. якщо існує декілька незакритих запитів, що відносяться до 1 дескриптора, то для синхронізації треба використ. не дескриптори, а події

Наслідки вводу/виводу, що перекривається:

  1. операції не блокуються, тобто функції виконуються повернення не чекаючи операції завершення вводу/виводу;

  2. значення, що повертаються цими функціями не можуть бути використані як критерій успішності чи неуспішності виконання функції;

  3. повернення значення кількості переданих байт теж не є інформативним;

  4. оскільки операції вводу/виводу завершуються не в тому порядку, в якому починались, то організовувати синхронізацію роботи є обов’язковим моментом, оскільки програма має визначати яка з операцій вводу/виводу вже завершилась.

BOOL GetOverLappedResult() – визначає стан вводу/виводу. Дана функція повертає істину, якщо операція вводу/виводу була успішно завершена. Якщо повертає помилку, а функція GetLastError() повертає ERROR_IO_INCOMPLETE, то операція вводу/виводу булла завершена з помилкою.

За допомогою CanselIO() можна відмінити виконання всіх операцій вводу/виводу що перекривається і пов'язані з дескриптором в якості параметра.

Розширений ввід/вивід з використанням процедур завершення. Тут потік не чекає надходження сигналу завершення, а система ініціює визначену користувачем процедуру завершення. Щоб використати процедуру завершення, треба якось передавати їй адресу. Для цього використ. ReadFileEx(), WriteFileEx(). Перш, ніж процедуру завершення буде виконана, треба щоб виконувалось:

  1. повинна бути завершена операція вводу/виводу;

  2. викликаючий потік повинен знаходитись в стані чергового очікування, сповіщаючи систему що треба виконати процедуру завершення. Цим він робить неможливим передчасне завершення.

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

Функції чергового очікування:

  1. WaitForSingleObjectEx()

  2. WaitForMultiObjectEx()

  3. SleepEx()

Всюди останнім параметром є прапорець, який у випадку асинхронного вводу/виводу має бути істиною. Тривалість інтервалів очікування задається в мс. Кожна з функцій робить повернення, коли відбувається 1 з 3 подій:

  1. дескриптор переходить в сигнальний стан;

  2. закінчується час очікування;

  3. процедури очікування всі що є в черзі потоку, припиняють своє виконання, а стан прапорця є істина.

Оскільки операції вводу/виводу завершуються не в тому порядку, в якому починались, то організовувати синхронізацію роботи є обов’язковим моментом, оскільки програма має визначати яка з операцій вводу/виводу вже завершилась.

Якщо треба передавати певні дані процедурі завершення, то використ. поле події hEvent в структурі OverLapped, оскільки ОС його повністю ігнорує.

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

HANDLE CreateWaritableTimer() – створення. Повертає дескриптор таймера. Спочатку таймер створюється в неактивному стані. Для активізації – BOOL SetWaritableTimer(). Тільки дана функція може змінити сигнальний стан таймера. Скасувати таймер - CanselWaritableTimer().

c:\masm32\bin\ml /c /coff m.asm

c:\masm32\bin\link /subsystem:windows m.obj

pause

.386P

.MODEL FLAT, stdcall

;директиви компонувальнику для підключення бібліотек

includelib e:\masm32\lib\user32.lib

includelib e:\masm32\lib\kernel32.lib

;константи

WM_DESTROY equ 2

WM_CREATE equ 1

WM_LBUTTONDOWN equ 201h

WM_RBUTTONDOWN equ 204h

;властивості вікна

CS_VREDRAW equ 1h

CS_HREDRAW equ 2h

CS_GLOBALCLASS equ 4000h

WS_OVERLAPPEDWINDOW equ 000CF0000H

style equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS

;ідентифікатор стандартної піктограми

Idi_application equ 32512

;ідентифікатор курсору

Idc_cross equ 32515

;режим показу вікна — нормальний

SW_SHOWNORMAL equ 1

;прототипи зовнішніх процедур

EXTERN …@...

;структури

MSGSTRUCT STRUC

MSHWND DD ?;ідентифікатор вікна, що отримує ;повідомлення

MSMESSAGE DD ? ;ідентифікатор повідомлення

MSWPARAM DD ? ;додаткова інформація про повідомлення

MSLPARAM DD ? ;додаткова інформація про повідомлення

MSTIME DD ? ;час посилання повідомлення

MSPT DD ?;положення курсору, під час посилання ;повідомлення

MSGSTRUCT ENDS

WNDCLASS STRUC

CLSSTYLE DD ? ;стиль вікна

CLWNDPROC DD ? ;вказівник на процедуру вікна

CLSCEXTRA DD ? ;інформація про додаткові байти для даної ;структури

CLWNDEXTRA DD ? ;інформація про додаткові байти для вікна

CLSHINSTANCE DD ? ;дескриптор додатка

CLSHICON DD ? ;ідентифікатор ікони вікна

CLSHCURSOR DD ? ;ідентифікатор курсору вікна

CLBKGROUND DD ? ;ідентифікатор пензля вікна

CLMENUNAME DD ? ;ім’я-ідентифікатор меню

CLNAME DD ? ;специфікує ім'я класу вікон

WNDCLASS ENDS

;сегмент даних

_DATA SEGMENT DWORD PUBLIC USE32 'DATA'

NEWHWND DD 0

MSG MSGSTRUCT <?>

WC WNDCLASS <?>

HINST DD 0 ;дескриптор додатку

TITLENAME DB 'Простеньке вікно', 0

CLASSNAME DB 'CLASS32', 0

CAP DB 'Повідомлення', 0

MES1 DB 'Ви натиснули ліву кнопку мишки', 0

MES2 DB 'Вихід з програми', 0

_DATA ENDS

; сегмент коду

_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'

START:

;отримати дескриптор додатку

PUSH 0

CALL GetModuleHandleA@4

MOV [HINST], EAX

REG_CLASS:

;заповнити структуру вікна

;стиль вікна

MOV [WC.CLSSTYLE], style

;процедура обробки повідомлень

MOV [WC.CLWNDPROC], OFFSET WNDPROC

MOV [WC.CLSCEXTRA], 0

MOV [WC.CLWNDEXTRA], 0

MOV EAX, [HINST]

MOV [WC.CLSHINSTANCE], EAX

;п іктограма вікна

PUSH IDI_APPLICATION

PUSH 0

CALL LoadIconA@8

MOV [WC.CLSHICON], EAX

;к урсор вікна

PUSH IDC_CROSS

PUSH 0

CALL LoadCursorA@8

MOV [WC.CLSHCURSOR], EAX

MOV [WC.CLBKGROUND], 17 ;колір вікна

MOV DWORD PTR [WC.CLMENUNAME],0

MOV DWORD PTR [WC.CLNAME], OFFSET CLASSNAME

PUSH OFFSET WC

CALL RegisterClassA@4

;створити вікно зареєстрованого класу

PUSH 0

PUSH [HINST]

PUSH 0

PUSH 0

PUSH 400 ; DY - висота вікна

PUSH 400 ; DX - ширина вікна

PUSH 100 ; Y — координата лівого верхнього кута

PUSH 100 ; X — координата лівого верхнього кута

PUSH WS_OVERLAPPEDWINDOW

PUSH OFFSET TITLENAME ;ім’я вікна

PUSH OFFSET CLASSNAME ;ім’я класу

PUSH 0

CALL CreateWindowExA@48

; перевірка на помилку

CMP EAX,0

JZ _ERR

MOV [NEWHWND], EAX ;дескриптор вікна

PUSH SW_SHOWNORMAL

PUSH [NEWHWND]

CALL ShowWindow@8 ;показати створене вікно

PUSH [NEWHWND]

CALL UpdateWindow@4 ; команда перемалювати видиму частину вікна, повідомлення WM_PAINT

;цикл обробки повідомлень

MSG_LOOP:

PUSH 0

PUSH 0

PUSH 0

PUSH OFFSET MSG

CALL GetMessageA@16

CMP EAX, 0

JE END_LOOP

PUSH OFFSET MSG

CALL TranslateMessage@4

PUSH OFFSET MSG

CALL DispatchMessageA@4

JMP MSG_LOOP

END_LOOP:

;вихід із програми (закрити процес)

PUSH [MSG.MSWPARAM]

CALL ExitProcess@4

_ERR:

JMP END_LOOP

; процедура вікна

; розташування параметрів у стеку

; [EBP+14H] LPARAM

; [EBP+10H] WPARAM

; [EBP+0CH] MES

; [EBP+8Н] HWND

WNDPROC PROC

PUSH EBP

MOV EBP, ESP

PUSH EBX

PUSH ESI

PUSH EDI

CMP DWORD PTR [EBP+0CH], WM_DESTROY

JE WMDESTROY

CMP DWORD PTR [EBP+0CH], WM_CREATE

JE WMCREATE

CMP DWORD PTR [EBP+0CH],WM_LBUTTONDOWN

JE LBUTTON

CMP DWORD PTR [EBP+0CH],WM_RBUTTONDOWN

JE RBUTTON

JMP DEFWNDPROC

;натиснення правої кнопки приводить до закриття вікна додатку

RBUTTON:

JMP WMDESTROY

;натиснення лівої кнопки приводить до появи вікна повідомлення

LBUTTON:

PUSH 0 ;MB_OK

PUSH OFFSET CAP

PUSH OFFSET MES1

PUSH DWORD PTR [EBP+08H]

CALL MessageBoxA@16

MOV EAX, 0

JMP FINISH

WMCREATE:

MOV EAX, 0

JMP FINISH

DEFWNDPROC:

PUSH DWORD PTR [EBP+14H]

PUSH DWORD PTR [EBP+10H]

PUSH DWORD PTR [EBP+0CH]

PUSH DWORD PTR [EBP+08H]

CALL DefWindowProcA@16

JMP FINISH

WMDESTROY:

PUSH 0

CALL PostQuitMessage@4 ;повідомлення WM_QUIT

MOV EAX, 0

FINISH:

POP EDI

POP ESI

POP EBX

POP EBP

RET 16

WNDPROC ENDP

_TEXT ENDS

END START

;сегмент даних

_DATA SEGMENT DWORD PUBLIC USE32 'DATA'

NEWHWND DD 0

MSG MSGSTRUCT <?>

WC WNDCLASS <?>

HINST DD 0 ;дескриптор додатку

TITLENAME DB 'Простеньке вікно', 0

CLASSNAME DB 'CLASS32', 0

CAP DB 'Повідомлення', 0

MES1 DB 'Ви натиснули ліву кнопку мишки', 0

MES2 DB 'Вихід з програми', 0

MES3 DB 'Ви натиснули праву кнопку мишки', 0

CPBUT DB 'Вихід', 0 ; ім‘я вікна

CPBUT2 DB 'Час', 0 ; ім‘я вікна

TIM DB "Дата %u/%u/%u Час %u:%u:%u",0

DATA SYSTEMTIME <?>

STRCOPY DB 200 DUP(?)

CLSBUTN DB 'BUTTON', 0 ; ім‘я класу

HWNDBTN2 DD 0 ; змінна для дескриптора кнопки

HWNDBTN DD 0 ; змінна для дескриптора кнопки

_DATA ENDS

WMCOMMAND:

MOV EAX, [EBP+14H]

CMP EAX, HWNDBTN

JE WMDESTROY

CMP EAX, HWNDBTN2

JE TIME

WMCREATE:

PUSH 0

PUSH [HINST]

PUSH 0

PUSH [EBP+08H]

PUSH 20

PUSH 50

PUSH 150

PUSH 150

PUSH STYLBTN

PUSH OFFSET CPBUT

PUSH OFFSET CLSBUTN

PUSH 0

CALL CreateWindowExA@48

MOV HWNDBTN, EAX

PUSH 0

PUSH [HINST]

PUSH 0

PUSH [EBP+08H]

PUSH 20

PUSH 50

PUSH 100

PUSH 150

PUSH STYLBTN

PUSH OFFSET CPBUT2

PUSH OFFSET CLSBUTN

PUSH 0

CALL CreateWindowExA@48

MOV HWNDBTN2, EAX

JMP FINISH

TIME:

;ініціалізація таймера

PUSH OFFSET DATA

CALL GetLocalTime@4

;Передати локальний час

MOVZX EAX, DATA.wSecond

PUSH EAX

MOVZX EAX, DATA.wMinute

PUSH EAX

MOVZX EAX,DATA.wHour

PUSH EAX

MOVZX EAX,DATA.wDay

PUSH EAX

MOVZX EAX,DATA.wMonth

PUSH EAX

MOVZX EAX,DATA.wYear

PUSH EAX

PUSH OFFSET TIM

PUSH OFFSET STRCOPY

CALL wsprintfA

;Відновити стек

ADD ESP,32

;Вивід кнопки

PUSH 0 ;MB_OK

PUSH OFFSET CAP

PUSH OFFSET STRCOPY

PUSH DWORD PTR [EBP+08H]

CALL MessageBoxA@16

JMP FINISH

; константи

STD_OUTPUT_HANDLE equ -11

STD_INPUT_HANDLE equ -10

; атрибути кольорів

FOREGROUND_BLUE equ 1h

FOREGROUND_GREEN equ 2h

FOREGROUND_RED equ 4h

FOREGROUND_INTENSITY equ 8h

BACKGROUND_BLUE equ 10h

BACKGROUND_GREEN equ 20h

BACKGROUND_RED equ 40h

BACKGROUND_INTENSITY equ 80h

COL1 = 1h+2h+8h+10h ; колір текста, який вводиться

COL2 = 4h+2h+8h+40h ; цвет текста, який виводиться

; прототипи зовнішніх процедур

EXTERN GetStdHandle@4:NEAR

; директиви для подключення бібліотек

includelib c:\masm32\lib\user32.lib

includelib c:\masm32\lib\kernel32.lib

include c:\masm32\include\kernel32.inc

includelib c:\masm32\lib\kernel32.lib

;------------------------------------------------------------

COOR STRUC

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]