Воскресенье, 20.07.2025, 10:48 Приветствую Вас Гость

On-line: Книги, учебники, статьи

Главная | Регистрация | Вход | RSS

Глава 2. Основы программирования в операционной системе Windows(2)

III

После разбора программы на Рис. 1.2.2 возникает вопрос по поводу ее реализации на ассемблере TASM. В действительности здесь требуются минимальные изменения: вместо библиотек user32.lib и kernel32.lib надо подключить библиотеку import32.lib, удалить во всех именах библиотечных процедур @N и далее выполнить команды tasm32 /ml prog.asm и tlink32 -aa prog.obj.

.386P
; плоская модель
MODEL FLAT, stdcall
; константы
; сообщение приходит при закрытии окна
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 MessageBoxA:NEAR
EXTERN CreateWindowExA:NEAR
EXTERN DefWindowProcA:NEAR
EXTERN DispatchMessageA:NEAR
EXTERN ExitProcess:NEAR
EXTERN GetMessageA:NEAR
EXTERN GetModuleHandleA:NEAR
EXTERN LoadCursorA:NEAR
EXTERN LoadIconA:NEAR
EXTERN PostQuitMessage:NEAR
EXTERN RegisterClassA:NEAR
EXTERN ShowWindow:NEAR
EXTERN TranslateMessage:NEAR
EXTERN UpdateWindow:NEAR
; директивы компоновщику для подключения библиотек
includelib c:\tasm32\lib\import32.lib
;--------------------------------------------------
; структуры
; структура сообщения
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 'Простой пример 32-битного приложения',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
 MOV [HINST], EAX
REG_CLASS:
; заполнить структуру окна стиль
 MOV [WC.CLSSTYLE], style
; процедура обработки сообщений
 MOV [WC.CLWNDPROC], OFFSET WNDPROC
 MOV [WC.CLSCSEXTRA], 0
 MOV [WC.CLWNDEXTRA], 0
 MOV EAX, [HINST]
 MOV [WC.CLSHINSTANCE], EAX
; ---------- иконка окна
 PUSH IDI_APPLICATION
 PUSH 0
 CALL LoadIconA
 MOV [WC.CLSHICON], EAX
;----------- курсор окна
 PUSH IDC_CROSS
 PUSH 0
 CALL LoadCursorA
 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
; создать окно зарегистрированного класса
 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
; проверка на ошибку
 CMP EAX, 0
 JZ _ERR
 MOV [NEWHWND], EAX
 PUSH SW_SHOWNORMAL
 PUSH [NEWHWND]
 CALL ShowWindow ; показать созданное окно
 PUSH [NEWHWND]
 CALL UpdateWindow ; команда перерисовать видимую 
 ; часть окна, сообщение WM_PAINT

; петля обработки сообщений
MSG_LOOP:
 PUSH 0
 PUSH 0
 PUSH 0
 PUSH OFFSET MSG
 CALL GetMessageA
 CMP EAX, 0
 JE END_LOOP
 PUSH OFFSET MSG
 CALL TranslateMessage
 PUSH OFFSET MSG
 CALL DispatchMessageA
 JMP MSG_LOOP
END_LOOP:

; выход из программы (закрыть процесс)
 PUSH [MSG.MSWPARAM]
 CALL ExitProcess
_ERR:
 JMP END_LOOP
;--------------------------------------------------
; процедура окна
; расположение параметров в стеке 
; [EBP+014Н] LPARAM
; [EBP+10H] WAPARAM
; [EBP+0СН] 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 ; МВ_ОК
 PUSH OFFSET CAP
 PUSH OFFSET MES1
 PUSH DWORD PTR [EBP+08H]
 CALL MessageBoxA
 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
 JMP FINISH
WMDESTROY:
 PUSH 0 ; MB_OK
 PUSH OFFSET CAP
 PUSH OFFSET MES2
 PUSH DWORD PTR [EBP+08H] ; дескриптор окна
 CALL MessageBoxA
 PUSH 0
 CALL PostQuitMessage ; сообщение WM_QUIT
 MOV EAX, 0
FINISH:
 POP EDI
 POP ESI
 POP EBX
 POP EBP
 RET 16
WNDPROC ENDP
_TEXT ENDS
END START

Рис. 1.2.3. Простой пример программы для Windows (TASM32).

Кстати заметьте, что исполняемый модуль, транслируемый в TASM32, всегда несколько длиннее аналогичного модуля, транслируемого с помощью пакета MASM32.

Обратите внимание на то, как задаются свойства окон. Описание этих свойств можно найти в программе помощи для функции CreateWindow.

В принципе, подбор необходимых сочетаний свойств окна — довольно кропотливое занятие. Это одна из причин, из-за которой понятие ресурса стало неотъемлемой частью загружаемого модуля. С понятием ресурса мы познакомимся в последующих главах.

Приведенные примеры показывают, что процедура окна должна правильно реагировать на пришедшие сообщения. В дальнейшем Вы узнаете, что и сама процедура может посылать сообщения приложению, другим окнам, а также своему окну21.

Часть API-функций получила суффикс "А". Дело в том, что функции имеют два прототипа: с суффиксом "А" - поддерживают ANSI, а с суффиксом "W" - Unicode.


21 Собственно PostQuitMessage есть пример посылки сообщения своему приложению.


IV

Здесь мне хотелось бы рассмотреть подробнее вопрос о передачи параметров через стек.

Это не единственный способ передачи параметров, но именно через стек передаются параметры API-функциям, поэтому на это необходимо обратить внимание.

Состояния стека до и после вызова процедуры приводится на Рис. 1.2.4.

Рис. 1.2.4 демонстрирует стандартный вход в процедуру, практикующийся в таких языках высокого уровня, как Паскаль и Си.

При входе в процедуру выполняется стандартная последовательность команд:

PUSH EBP
MOV EBP, ESP
SUB ESP, N ; N - количество байт для локальных переменных.

Адрес первого параметра определяется как [EBP+8Н], что мы уже неоднократно использовали. Адрес первой локальной переменной, если она зарезервирована, определяется как [EBP-4] (имеется в виду переменная типа DWORD). На ассемблере не очень удобно использовать локальные переменные, и мы не будем резервировать для них место (смотрите, однако, Главу 2.5).

В конце процедуры идут команды:

MOV ESP, EBP
POP EBP
RET М

Здесь M - объем, взятый у стека для передачи параметров.

Такого же результата можно добиться, используя команду ENTER N,0 (PUSH EBP\MOV EBP,ESP\SUB ESP) в начале процедуры и LEAVE (MOV ESP,EBP\POP EBP) в конце процедуры.

Эти команды появились еще у 286-ого процессора и дали возможность несколько оптимизировать транслируемый код программы, особенно в тех случаях, когда речь идет о больших по объему модулях, создаваемых на языке высокого уровня.

Хотелось бы остановиться еще на одном вопросе, связанным со структурой процедуры и ее вызова.

Существуют два основных подхода (см. [1]). Условно первый подход можно назвать Си-подходом, а второй — Паскаль-подходом.

Первый подход предполагает, что процедура "не знает", сколько параметров находится в стеке. Естественно, в этом случае освобождение стека от параметров должно происходить после команды вызова процедуры, например, с помощью команды POP или команды ADD ESP,N (N - количество байт в параметрах).

Второй подход основан на том, что количество параметров фиксировано, а стек можно освободить в самой процедуре. Это достигается выполнением команды RET N (N - количество байт в параметрах). Как Вы уже, наверное, догадались, вызов функций API осуществяется по второй схеме. Впрочем, есть и исключения, о которых вы узнаете несколько позже (см. Гл. 2.1).

Вход на сайт
Поиск
Календарь
«  Июль 2025  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
28293031
Архив записей
Наш опрос
Как Вам удобнее??
Всего ответов: 341
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0