- •Системне програмування, характерні особливості.
- •Асемблер. Варіанти спільного використання асемблера і мов високого рівня.
- •Інструментальні засоби розробки програм на мові Асемблер.
- •Регістри.
- •Користувацькі регістри.
- •Сегментні регістри.
- •Організація роботи з пам’ятю.
- •Сегментна модель пам’яті.
- •Специфікація типів даних
- •Набір команд мікропроцесора.
- •11. Використання арі-функцій.
- •12. Структура програми, приклад простої програми для Windows.
- •13. Передача параметрів через стек
- •14. Використання ресурсів при роботі з діалоговими вікнами.
- •15.Використання ресурсів при роботі з меню.
- •16. Використання акселераторів у файлах ресурсу.
- •17. Ресурси. Переваги використання ресурсів у програмах.
- •18. Використання ресурсів при роботі з іконками, рядками, курсорами.
- •21. Потоки, взаємодія потоків.
- •22. Семафори блять! Події нахуй!
- •23. Критичні секції.
11. Використання арі-функцій.
Отже, почнемо з декількох загальних положень про програмування в Windows.
Програмування в Windows грунтується на використанні функцій API (Application Program Interface, тобто інтерфейс програмного продукту). їх кількість сягає двох тисяч. Ваша програма в значній мірі буде складатися з таких викликів. Вся взаємодія з зовнішніми пристроями та ресурсами операційної системи буде відбуватися за допомогою таких функцій.
Список функцій АРІ і їх опис найкраще брати з файлу WIN32.HLP, який поставляється, наприклад, з пакетом Borland C + +.
Головним елементом програми в середовищі Windows є вікно. Для кожного вікна визначається своя процедура обробки повідомлень .
Вікно може містити елементи управління: кнопки, списки, вікна редагування та ін Ці елементи, по суті, також є вікнами, але володіють особливими властивостями. Події, що відбуваються з цими елементами (і самим вікном), призводять до приходу повідомлень в процедуру вікна.
Операційна система Windows використовує лінійну модель пам'яті. Іншими словами, всю пам'ять можна розглядати як один сегмент. Для програміста мовою асемблер це означає, що адреса будь- якої комірки пам'яті буде визначатися вмістом одного 32-бітного регістра, наприклад ЕВХ. Операційна система Windows є багатозадачному середовищем. Кожна задача має свій адресний простір і свою чергу повідомлень. Більш того, навіть в рамках однієї програми може бути здійснена багатозадачність - будь-яка процедура може бути запущена як самостійна задача.
12. Структура програми, приклад простої програми для Windows.
Почнемо з того, як можна викликати функції АРІ. Звернемося до файлу допомоги та виберемо будь-яку функцію АРІ, наприклад, MessageBox:
int MessageBox (HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
Ця функція виводить на екран вікно з повідомленням і кнопкою (або кнопками) виходу. Зміст параметрів: hWnd-дескриптор вікна, в якому буде з'являтися вікно-повідомлення, IpText - текст, який буде з'являтися в вікні, lpCaption - текст у заголовку вікна, uType - тип вікна, зокрема можна визначити кількість кнопок виходу. Тепер про типи параметрів. Всі вони в дійсності 32-бітні цілі числа: HWND - 32-bit ціле, LPCTSTR - 32-бітний покажчик на рядок, UINT - 32-bit ціле. З причин, про яку буде сказано нижче, до імені функцій нам доведеться додавати суфікс "А", крім того, при використанні MASM необхідно також в кінці імені додати @ 16. Таким чином, виклик зазначеної функції буде виглядати так: CALL MessageBoxA @ 16. А як же бути з параметрами? їх слід акуратно помістити в стек. Запам'ятайте правило: Зліва направо - знизу вгору. Отже, нехай дескриптор вікна розташований за адресою HW, строки - за адресами STR1 і STR2, а тип вікна- повідомлення - це константа. Найпростіший тип має значення 0 і називається MB ОК.
Маємо наступне:
МВ_ОК equ 0
STR1 DB ''Неправильне введення", 0 STR2 DB "Повідомлення про помилку.", 0 HW DWORD?
Push МВ_ОК PUSH OFFSET STR1
PUSH OFFSET STR2
PUSH HW
CALL MessageBoxA @ 16
Результат виконання будь-якої функції - це, як правило, ціле число, яке повертається в регістр! ЕАХ.
Аналогічним чином в Асемблері легко відтворити ті чи інші Сі-структури. Розглянемо, наприклад, структуру, яка визначає системне повідомлення:
typedef struct tagMSG (/ / MSG HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM IParam;
DWORD time;
POINT pt;
) MSG;
На Асемблері ця структура буде мати вигляд:
MSGSTRUCT STRUC MSHWND DD?
MSMESSAGE DD?
MS WPARAM DD?
MSLPARAM DD?
MSTIME DD?
MSPT DD?
MSGSTRUCT ENDS
Тепер звернемося до структури всієї програми. Як я вже казав, в цьому розділі ми будемо розглядати класичну структуру програми під Windows. У такій програмі є головне вікно, а отже, і процедура головного вікна. В цілому, в коді програми можна виділити наступні секції.
Реєстрація класу вікон Створення головного вікна Цикл обробки черги повідомлень Процедура головного вікна
Звичайно, в програмі можуть бути й інші розділи, але дані розділи утворюють основний кістяк програми. Разберем ці розділи по порядку.
Реєстрація класу вікон. Реєстрація класу вікон здійснюється заї лошогою функції RegisterClassA, єдиним параметром якої є покажчик на структуру WNDCLASS, що містить
Стаорення вікна. На основі зареєстрованого класу за допомогою функції CreateWmdowExA (або CreateWindowA) можна створити екземпляр вікна.
Функція GetMessage () "відловлює" чергове повідомлення з ряду повідомлень програми та
TranslateMessage, то її компетенція стосується повідомлень WM KEYDOWN і WMJCEYUP, які транслюються в WMJTHAR і WMDE^CHAR, *а також WM~SYSKEYDOWN і WMJSYSKEYUP, перетворюються у WM_bYbLHAK і WM_SYSDEADCHAR Сенс трансляції полягає не в заміні, а в відправці додаткових повідомлень. Так наприклад, при натисненні і відпускання алфавітно-цифрової клавші у вікно спочатку прииде повідомлення WMJCEYDOWN, потім WMJCEYUP, а потім вже WM_CHAR.
Процедура головного вікна.
Ось прототип функціі вікна на мові С:
LRESULT CALLBACK WindowFunc (HWND hwnd, UINT message. WPARAM wParam, LPARAM IParam)
Звернемо увагу на представлені параметри. Ось зміст цих параметрів: hwnd - ідентифікатор вікна, Message - ідентифікатор повідомлення, wParam і IParam - параметри, уточнюючі зміст повідомлення (для кожного повідомлення можуть грати різні ролі чи не грати ніяку). Всі чотири параметри, як ви, напевно, вже здогадалися, мають тип DWORD.
А тепер розглянемо "скелет" цієї функції на мові асемблер.
WNDPROC PROC PUSH EBP
MOV EBP, ESP проковтнений EBP вказує на вершину стека PUSH ЕВХ PUSH ESI PUSH EDI
PUSH DWORD PTR [EBP +14 H]; LPARAM (IParam)
PUSH DWORD PTR [EBP +10 H]; WPARAM (wParam)
PUSH DWORD PTR [EBP +0 CH]; MES (Message)
PUSH DWORD PTR [EBP +08 H]; HWND (hwnd)
CALL DefWindowProcA @ 16 POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP
Прокоментуємо фрагмент коду:
RET 16 - вихід зі звільненням стека від чотирьох параметрів (16 = 4*4).
Доступ до параметрів здійснюється через регістр EBP:
DWORD PTR [EBP +14 H]; LPARAM (IParam)
DWORD PTR [EBP +10 H]; WPARAM (wParam)
DWORD PTR [EBP +0 CH]; MES (Message) - код повідомлення DWORD PTR [EBP +08 H]; HWND (hwnd) - дескриптор вікна.
Функція DefWindowProc викликається для тих повідомлень, які не обробляються у функції вікна. У даному прикладі, як Ви розумієте, не обробляються всі прихідні у функцію вікна повідомлення.
