Assembler для Windows


Часть II. Более подробное описание программирования в среде Windows

Глава 1. Примеры простейших программ

В данной главе мы серьезно начинаем работать с сообщением WM_PAINT. В главе 1.3 мы уже рассматривали это сообщение, но не применяли его. Причиной было то, что в окне у нас были лишь управляющие элементы, но не было текстовой информации и графики. Теперь мы исправляем положение. Кроме сообщения WM_PAINT, речь в этой главе пойдет о множестве проблем, возникающих при программировании в Windows.

I

Если вы только начинаете программировать под Windows, то в программе на Рис. 2.1.1 найдете много нового. Поэтому приступим к подробному разбору программы.

  1. В данной программе мы определяем цвет окна и текста через комбинацию трех цветов: красного, зеленого и синего. Цвет определяется одним 32-битным числом. В этом числе первый байт - интенсивность красного, второй байт - интенсивность зеленого, третий байт - интенсивность синего цвета. Последний байт равен нулю. Механизм получения этого числа продемонстрирован в определении константы RGBW.
  2. Цвет окна задается посредством определения кисти через функцию CreateSolidBrush.
  3. Поскольку при перерисовке окна системой посылается сообщение WM_PAINT, именно при получении этого сообщения и следует перерисовывать содержимое этого окна. В данном случае мы выводим всего лишь одну строку текста. Для того чтобы осуществить вывод информации в окно, необходимо сначала получить контекст окна (контекст устройства - Device Context). Для нас это — просто некоторое число, посредством которого осуществляется связь между приложением и окном. Обычно контекст устройства определяется посредством функции GetDC. При получении сообщения WM_PAINT контекст устройства получается посредством функции BeginPaint. Аргументом для этой функции является указатель на специальную структуру, которая у нас называется PAINTSTR и поля которой, впрочем, мы пока не используем.
  4. Текст, как Вы уже, надеюсь, поняли из текста программы, выводится посредством функции OutText. Предварительно, посредством функций SetBkColor и SetTextColor, мы определяем цвет фона и цвет букв. Цвет фона, соответственно, совпадает с цветом окна.
  5. Несколько слов о системе координат. Центр системы координат находится в левом верхнем углу, ось Y направлена вниз, ось Х - вправо. Впрочем, это общепринятый вариант для графических экранов.
  6. Еще один момент также связан с выводом текста в окно. Одним из параметров функции OutText является количество символов выводимой строки. И здесь начинается самое интересное. Определить длину строки (за минусом нулевого элемента) можно по-разному. Например, можно использовать операторы макроассемблера SIZEOF или LENGTHOF. Но вот беда, в Турбо Ассемблере этих операторов нет. Можно, конечно, решить эту проблему, поставив метку в конце строки или используя старые директивы LENGTH и SIZE. Но, как Вы, наверное, уже поняли, для того чтобы легко переходить от MASM32 к TASM32, следует как можно меньше использовать макросредства. Кроме того, раз уже мы употребляем определение строк, как это принято в Си, — естественно и определить функции для работы со строковыми переменными (см. замечание в конце главы). В данном примере мы определили функцию, которая возвращает длину строки. Не смущайтесь, что функция помещает результат в регистр EBX. Нам просто так удобнее. У функции, кроме того, есть одно очень важное преимущество перед макросредствами - она получает длину при выполнении программы, а не во время ее трансляции.

Теперь, чтобы добиться транслируемости программы на Турбо Ассемблере, нужно проделать те же манипуляции, которые мы производили раньше: убрать суффиксы @N и подключить библиотеку import32.lib.

; файл text1.inc
; константы
; сообщение приходит при закрытии окна
WM_DESTROY equ 2
; сообщение приходит при создании окна
WM_CREATE equ 1
; сообщение приходит при перерисовке окна
WM_PAINT equ 0FH
; свойства окна
CS_VREDRAW equ 1h
CS_HREDRAW equ 2h
CS_GLOBALCLASS equ 4000h
WS_OVERLAPPEDWINDOW equ 000CF0000H
stylcl equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS
DX0 equ 300
DY0 equ 200
; компоненты цветов
RED equ 50
GREEN equ 50
BLUE equ 255
RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16)
RGBT equ 255 ; красный
; идентификатор стандартной иконки
IDI_APPLICATION equ 32512
; идентификатор курсора
IDC_CROSS equ 32515
; режим показа окна — нормальный
SW_SHOWNORMAL equ 1
; прототипы внешних процедур
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
EXTERN BeginPaint@8:NEAR
EXTERN EndPaint@8:NEAR
EXTERN TextOutA@20:NEAR
EXTERN GetStockObject@4:NEAR
EXTERN CreateSolidBrush@4:NEAR
EXTERN SetBkColor@8:NEAR
EXTERN SetTextColor@8:NEAR
; структуры
; структура сообщения
MSGSTRUCT STRUC
MSHWND DD ? ; идентификатор окна,
; получающего сообщение
MSMESSAGE DD ? ; идентификатор сообщения
MSWPARAM DD ? ; доп. информация о сообщении
MSLPARAM DD ? ; доп. информация о сообщении
MSTIME DD ? ; время посылки сообщения
MSPT DD ? ; положение курсора во время
; посылки сообщения
MSGSTRUCT ENDS
WNDCLASS STRUC
CLSSTYLE DD ? ; стиль окна
CLSLPFNWNDPROC DD ? ; указатель на процедуру окна
CLSCBCLSEXTRA DD ? ; информация о доп. байтах для
; данной структуры
CLSCBWNDEXTRA DD ? ; информация о доп. байтах для окна
CLSHINSTANCE DD ? ; дескриптор приложения
CLSHICON DD ? ; идентификатор иконы окна
CLSHCURSOR DD ? ; идентификатор курсора окна
CLSHBRBACKGROUND DD ? ; идентификатор кисти окна
MENNAME DD ? ; имя-идентификатор меню
CLSNAME DD ? ; специфицирует имя класса окон
WNDCLASS ENDS
PAINTSTR STRUC
hdc DWORD 0
fErase DWORD 0
left DWORD 0
top DWORD 0
right DWORD 0
bottom DWORD 0
fRes DWORD 0
fIncUp DWORD 0
Reserv DB 32 dup(0)
PAINTSTR ENDS
; файл text1.asm
.386P
; плоская модель
.MODEL FLAT, stdcall
;------------------------------------------------------------
include text1.inc
; подключения библиотек
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
;------------------------------------------------------------
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
NEWHWND DD 0
MSG MSGSTRUCT <?>
WC WNDCLASS <?>
PNT PAINTSTR <?>
HINST DD 0
TITLENAME DB 'Текст в окне',0
NAM DB 'CLASS32',0
XT DWORD 30
YT DWORD 30
TEXT DB 'Текст в окне красный',0
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
; получить дескриптор приложения
PUSH 0
CALL GetModuleHandleA@4
MOV [HINST], EAX
REG_CLASS:
; заполнить структуру окна стиль
MOV [WC.CLSSTYLE] , stylcl
; процедура обработки сообщений
MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC
MOV [WC.CLSCBCLSEXTRA],0
MOV [WC.CLSCBWNDEXTRA],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
PUSH RGBW ; цвет кисти
CALL CreateSolidBrush@4 ; создать кисть
MOV [WC.CLSHBRBACKGROUND],EAX
MOV DWORD PTR [WC.MENNAME],0
MOV DWORD PTR [WC.CLSNAME], OFFSET NAM
PUSH OFFSET WC
CALL RegisterClassA@4
; создать окно зарегистрированного класса
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH DY0 ; DY0 - высота окна
PUSH DX0 ; DX0 - ширина окна
PUSH 100 ; координата Y
PUSH 100 ; координата X
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME ; имя окна
PUSH OFFSET NAM ; имя класса
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 ; перерисовать видимую часть окна
; петля обработки сообщений
MSG_LOOP:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG
CALL GetMessageA@16
CMP AX, 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+014Н] ; LPARAM
; [EBP+10H] ; WAPARAM
; [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_PAINT
JE WMPAINT
JMP DEFWNDPROC
WMPAINT:
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL BeginPaint@8
PUSH EAX ; сохранить контекст (дескриптор)
;---------------- цвет фона = цвет окна
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;---------------- контекст
POP EAX
PUSH EAX
;---------------- цвет текста (красный)
PUSH RGBT
PUSH EAX
CALL SetTextColor@8
;---------------- контекст
POP EAX
;---------------- вывести текст
PUSH OFFSET TEXT
CALL LENSTR
PUSH EBX ; длина строки
PUSH OFFSET TEXT ; адрес строки
PUSH YT ; Y
PUSH XT ; X
PUSH EAX ; контекст окна
CALL TextOutA@20
;---------------- закрыть
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL EndPaint@8
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
;----------- функция --------------------------
; длина строки
; [EBP+08H] - указатель на строку
LENSTR PROC
PUSH EBP
MOV EBP, ESP
PUSH ESI
MOV ESI, DWORD PTR [EBP+8]
XOR EBX, EBX
LBL1:
CMP BYTE PTR [ESI], 0
JZ LBL2
INC EBX
INC ESI
JMP LBL1
LBL2:
POP ESI
POP EBP
RET 4
LENSTR ENDP
_TEXT ENDS
END START

Puc. 2.1.1. Пример простейшей программы с текстом.

Еще один пример с выводом текста в окно. Теперь мы усложняем свою задачу. Зададимся целью, чтобы текстовая строка все время, чтобы ни случилось с окном, была бы в его середине. Для этого необходимо знать длину строки в пикселях и размеры окна. Длина строки в пикселях определяется с помощью функции GetTextExtentPoint32, а размеры окна - с помощью функции GetWindowRect. При этом нам понадобятся структуры типа SIZET и RECT. Надеюсь, читатель понимает, как определить положение строки, если известна ее длина и размеры окна, добавлю только, что необходимо учесть высоту заголовка окна.

; файл text2.inc
; константы
; сообщение приходит при закрытии окна
WM_DESTROY equ 2
; сообщение приходит при создании окна
WM_CREATE equ 1
; сообщение приходит при перерисовке окна
WM_PAINT equ 0FH
; свойства окна
CS_VREDRAW equ 1h
CS_HREDRAW equ 2h
CS_GLOBALCLASS equ 4000h
WS_OVERLAPPEDWINDOW equ 000CF0000H
stylcl equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS
DX0 equ 300
DY0 equ 200
; компоненты цветов
RED equ 80
GREEN equ 80
BLUE equ 255
RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16)
RGBT equ 00FF00H ; зеленый
; идентификатор стандартной иконки
IDI_APPLICATION equ 32512
; идентификатор курсора
IDC_CROSS equ 32515
; режим показа окна - нормальный
SW_SHOWNORMAL equ 1
; прототипы внешних процедур
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
EXTERN BeginPaint@8:NEAR
EXTERN EndPaint@8:NEAR
EXTERN TextOutA@20:NEAR
EXTERN GetStockObject@4:NEAR
EXTERN CreateSolidBrush@4:NEAR
EXTERN SetBkColor@8:NEAR
EXTERN SetTextColor@8:NEAR
EXTERN GetTextExtentPoint32A@16:NEAR
EXTERN GetWindowRect@8:NEAR
; структуры
; структура сообщения
MSGSTRUCT STRUC
MSHWND DD ? ; идентификатор окна, получающего сообщение
MSMESSAGE DD ? ; идентификатор сообщения
MSWPARAM DD ? ; доп. информация о сообщении
MSLPARAM DD ? ; доп. информация о сообщении
MSTIME DD ? ; время посылки сообщения
MSPT DD ? ; положение курсора, во время
; посылки сообщения
MSGSTRUCT ENDS
WNDCLASS STRUC
CLSSTYLE DD ? ; стиль окна
CLSLPFNWNDPROC DD ? ; указатель на процедуру окна
CLSCBCLSEXTRA DD ? ; информация о доп. байтах для данной структуры
CLSCBWNDEXTRA DD ? ; информация о доп. байтах для окна
CLSHINSTANCE DD ? ; дескриптор приложения
CLSHICON DD ? ; идентификатор иконы окна
CLSHCURSOR DD ? ; идентификатор курсора окна
CLSHBRBACKGROUND DD ? ; идентификатор кисти окна
MENNAME DD ? ; имя-идентификатор меню
CLSNAME DD ? ; специфицирует имя класса окон
WNDCLASS ENDS
PAINTSTR STRUC
hdc DWORD 0
fErase DWORD 0
left DWORD 0
top DWORD 0
right DWORD 0
bottom DWORD 0
fRes DWORD 0
fIncUp DWORD 0
Reserv DB 32 dup (0)
PAINTSTR ENDS
SIZET STRUC
X1 DWORD ?
Y1 DWORD ?
SIZET ENDS
RECT STRUC
L DWORD ? ; X - левого верхнего угла
T DWORD ? ; Y - левого верхнего угла
R DWORD ? ; X - правого нижнего угла
B DWORD ? ; Y - правого нижнего угла
RECT ENDS
; файл text2.asm
.386P
; плоская модель
.MODEL FLAT, stdcall
;------------------------------------------------------------
include text2.inc
; директивы компоновщику для подключения библиотек
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
NEWHWND DD 0
MSG MSGSTRUCT <?>
WC WNDCLASS <?>
PNT PAINTSTR <?>
SZT SIZET <?>
RCT RECT <?>
HINST DD 0
TITLENAME DB 'Текст в окне',0
NAM DB 'CLASS32',0
XT DWORD ?
YT DWORD ?
TEXT DB 'Текст в окне зеленый',0
CONT DWORD ?
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
; получить дескриптор приложения
PUSH 0
CALL GetModuleHandleA@4
MOV [HINST], EAX
REG_CLASS:
; заполнить структуру окна стиль
MOV [WC.CLSSTYLE],stylcl
; процедура обработки сообщений
MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC
MOV [WC.CLSCBCLSEXTRA], 0
MOV [WC.CLSCBWNDEXTRA], 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
PUSH RGBW ; цвет кисти
CALL CreateSolidBrush@4 ; создать кисть
MOV [WC.CLSHBRBACKGROUND], EAX
MOV DWORD PTR [WC.MENNAME],0
MOV DWORD PTR [WC.CLSNAME], OFFSET NAM
PUSH OFFSET WC
CALL RegisterClassA@4
; создать окно зарегистрированного класса
PUSH 0
PUSH [HINST]
PUSH 0
PUSH 0
PUSH DY0 ; DY0 - высота окна
PUSH DX0 ; DX0 - ширина окна
PUSH 100 ; координата Y
PUSH 100 ; координата X
PUSH WS_OVERLAPPEDWINDOW
PUSH OFFSET TITLENAME ; имя окна
PUSH OFFSET NAM ; имя класса
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 ; перерисовать видимую часть окна
; петля обработки сообщений
MSG_LOOP:
PUSH 0
PUSH 0
PUSH 0
PUSH OFFSET MSG
CALL GetMessageA@16
CMP AX, 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+014Н] LPARAM
; [EBP+10H] WAPARAM
; [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_PAINT
JE WMPAINT
JMP DEFWNDPROC
WMPAINT:
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL BeginPaint@8
MOV CONT,EAX ; сохранить контекст (дескриптор)
;---------------- цвет фона = цвет окна
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;---------------- цвет текста (красный)
PUSH RGBT
PUSH CONT
CALL SetTextColor@8
;- вычислить длину текста в пикселях текста
PUSH OFFSET TEXT
CALL LENSTR
PUSH EBX ; сохраним длину строки
PUSH OFFSET SZT
PUSH EBX
PUSH OFFSET TEXT
PUSH CONT
CALL GetTextExtentPoint32A@16
;---------------- размер окна
PUSH OFFSET RCT
PUSH DWORD PTR [EBP+8]
CALL GetWindowRect@8
;---------------- здесь вычисления координат
MOV EAX, RCT.R
SUB EAX, RCT.L
SUB EAX, SZT.X1
SHR EAX, 1 ; текст посередине
MOV XT, EAX
MOV EAX, RCT.B
SUB EAX, RCT.T
SHR EAX, 1
SUB EAX, 25 ; учтем заголовочную часть окна
MOV YT,EAX
; ---------------- вывести текст
; длина строки уже в стеке
PUSH OFFSET TEXT
PUSH YT
PUSH XT
PUSH CONT
CALL TextOutA@20
;---------------- закрыть контекст
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL EndPaint@8
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
; длина строки
; указатель на строку в - [EBP+08H]
LENSTR PROC
PUSH EBP
MOV EBP,ESP
PUSH ESI
MOV ESI, DWORD PTR [EBP+8]
XOR EBX, EBX
LBL1:
CMP BYTE PTR [ESI],0
JZ LBL2
INC EBX
INC ESI
JMP LBL1
LBL2:
POP ESI
POP EBP
RET 4
LENSTR ENDP
_TEXT ENDS
END START

Рис. 2.1.2. Текстовая строка все время в середине окна.

Не могу не воспользоваться случаем и не восхититься теми возможностями, которые открывает перед программистом ассемблер. Можете передавать параметры через стек, а можете и через регистры. Хотите - сохраняйте регистры в начале процедуры, а хотите - не сохраняйте. Ассемблерный код можно совершенствовать и еще раз совершенствовать. Кстати, для любителей цепочечных (строковых) команд ниже привожу другую процедуру определения длины строки, основанную на команде микропроцессора SCAS, позволяющей осуществлять поиск нужного элемента (байта - SCASB, слова - SCASW, двойного слова - SCASD) в строке.

; длина строки - [EBP+08Н]
LENSTR PROC
PUSH EBP
MOV EBP,ESP
PUSH EAX
CLD
MOV EDI, DWORD PTR [EBP+08H]
MOV EBX, EDI
MOV ECX,100 ; ограничить длину строки
XOR AL,AL
REPNE SCASB ; найти символ 0
SUB EDI, EBX ; длина строки, включая 0
MOV EBX, EDI
DEC EBX ; теперь здесь длина строки
POP EAX
POP EBP
RET 4
LENSTR ENDP

II

Рассмотрим теперь вопрос о том, как выводить текстовую информацию с различными типами шрифтов. Удобнее всего задать параметры шрифта при помощи функции CreateFontIndirect, параметром которой является указатель на структуру LOGFONT. Хотя название функции и начинается со слова Create, речь идет не о создании, а скорее изменении существующего шрифта согласно заданным параметрам. Существует и другая функция CreateFont, которая, на мой взгляд, менее удобна при использовании на ассемблере — поработайте с ней сами, если хотите. Выбор нужного шрифта осуществляется функцией SelectObject. Начнем с того, что разберем поля этой структуры.

LOGFONT STRUC
LfHeight DWORD ?
LfWidth DWORD ?
LfEscapement DWORD ?
LfOrientation DWORD ?
LfWeight DWORD ?
Lfitalic DB ?
LfUnderline DB ?
LfStrikeOut DB ?
LfCharSet DB ?
LfOutPrecision DB ?
LfClipPrecision DB ?
LfQuality DB ?
LfPitchAndFamily DB ?
LfFaceName DB 32 DUP(0)
LOGFONT ENDS

LfHeight - определяет высоту шрифта в логических единицах; если 0, то высота берется по умолчанию.
LfWidth - определяет ширину шрифта в логических единицах; если 0, то ширина по умолчанию.
LfEscapement - угол наклона текста в десятых долях градуса по отношению к горизонтальной оси в направлении против часовой стрелки.
LfOrientation - тоже, что и предыдущий параметр, но по отношению к отдельному символу (игнорируется в Windows 9х).
LfWeight - задает жирность шрифта (0-900).
Lfitalic - если 1, то курсив.
LfUnderline — если 1, то символы подчеркнуты.
LfStrikeOut — если 1, то символы перечеркнуты.
LfCharSet - задает множество символов шрифта, обычно определяется константой ANSI_CHARSET (=0).
LfOutPrecision - флаг точности шрифта; определяет, насколько точно созданный шрифт отвечает заданным параметрам. Возможные значения:

OUT_DEFAULT_PRECIS = 0
OUT_STRING_PRECIS = 1
OUT_CHARACTER_PRECIS = 2
OUT_STROKE_PRECIS = 3
OUT_TT_PRECIS = 4
OUT_DEVICE__PRECIS = 5
OUT_RASTER_PRECIS = 6
OUT_TT_ONLY_PRECIS = 7
OUT_OUTLINE_PRECIS = 8
OUT_SCREEN_OUTLINE_PRECIS = 9

LfClipPrecision - флаг точности прилегания шрифта; определяет, как будут отсекаться части шрифта, не попадающие в видимую область. Возможные значения:

CLIP_DEFAULT_PRECIS = 0
CLIP_CHARACTER_PRECIS = 1
CLIP_STROKE_PRECIS = 2
CLIP_MASK = 0FH
CLIP_LH_ANGLES = (1 SHL 4)
CLIP_TT_ALWAYS = (2 SHL 4)
CLIP_EMBEDDED = (8 SHL 4)

LfQuality - флаг качества шрифта; определяет соответствие логического шрифта и шрифта, допустимого данным устройством. Возможные значения:

DEFAULT_QUALITY = 0
DRAFT_QUALITY = 1
PROOF_QUALITY = 2

LfPitchAndFamily - определяет тип и семейство шрифта. Возможные значения определяются комбинацией (ИЛИ) двух групп констант:

DEFAULT_PITCH = 0
FIXED_PITCH = 1
VARIABLE_PITCH = 2

и
FF_DONTCARE = 0
FF_ROMAN = (1 SHL 4)
FF_SWISS = (2 SHL 4)
FF_MODERN = (3 SHL 4)
FF_SCRIPT = (4 SHL 4)
FF_DECORATIVE = (5 SHL 4)

LfFaceName - содержит название шрифта. Длина имени не может превосходить 32 символа.

Обратимся к примеру задания своего шрифта (результат работы программы - на Рис. 2.1.5). Однако поскольку большая часть программы будет совпадать с аналогичной частью предыдущих программ, я приведу здесь только необходимые фрагменты. Рассмотрим сначала фрагмент, выполняющийся при получении сообщения WM_PAINT (Рис.2.1.3).

WMPAINT:
;------- определить контекст
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL BeginPaint@8
MOV CONT,EAX ; сохранить контекст (дескриптор)
;------- цвет фона = цвет окна
PUSH RGBW
PUSH EAX
CALL SetBkColor@8
;------- цвет текста (красный)
PUSH RGBT
PUSH CONT
CALL SetTextColor@8
;------ здесь определение координат
MOV XT, 120
MOV YT, 140
;------ задать (создать) шрифт
MOV lg.LfHeight,12 ; высота фонта
MOV lg.LfWidth, 9 ; ширина фонта
MOV lg.LfEscapement,900 ; ориентация
MOV lg.LfOrientation, 0 ; вертикальная
MOV lg.LfWeight,400 ; толщина линий шрифта
MOV lg.LfItalic, 0 ; курсив
MOV lg.LfUnderline, 0 ; подчеркивание
MOV lg.LfStrikeOut, 0 ; перечеркивание
MOV lg.LfCharSet, 0 ; набор шрифтов
MOV lg.LfOutPrecision, 0
MOV lg.LfClipPrecision, 0
MOV lg.LfQuality,2
MOV lg.LfPitchAndFamily,0
PUSH OFFSET lg
; задать название шрифта
PUSH OFFSET NFONT
PUSH OFFSET lg.LfFaceName
CALL COPYSTR
CALL CreateFontIndirectA@4
;------ выбрать созданный объект
PUSH EAX
PUSH CONT
CALL SelectObject@8
PUSH EAX
;------ вычислить длину текста в пикселях текста
PUSH OFFSET TEXT
CALL LENSTR
;---------- вывести текст ----------------
PUSH EBX
PUSH OFFSET TEXT
PUSH YT
PUSH XT
PUSH CONT
CALL TextOutA@20
; удалить объект "FONT"
; идентификатор уже в стеке
CALL DeleteObject@4
;---------------- закрыть контекст
PUSH OFFSET PNT
PUSH DWORD PTR [EBP+08H]
CALL EndPaint@8
MOV EAX, 0
JMP FINISH

Рис. 2.1.3. Фрагмент программы, выводящей текст с заданным шрифтом (см. Рис. 2.1.5).

Как видно из фрагмента, создание шрифта производится по следующей схеме: надо задать шрифт при помощи функции CreateFontIndirect, выбрать шрифт функцией SelectObject, вывести текст заданным шрифтом, удалить созданный шрифт (объект). Поле LfFaceName структуры LOGFONT должно содержать название шрифта. Если такого шрифта нет, выводится шрифт по умолчанию. Название шрифта у нас задано в строке NFONT, и мы копируем его в поле LfFaceName при помощи функции COPYSTR, текст которой приводится на Рис. 2.1.4.

; процедура копирования одной строки в другую
; строка, куда копировать [EBP+08H]
; строка, что копировать [EBP+0CH]
COPYSTR PROC
PUSH EBP
MOV EBP,ESP
MOV ESI, DWORD PTR [EBP+0CH]
MOV EDI, DWORD PTR [EBP+08H]
L1:
MOV AL,BYTE PTR [ESI]
MOV BYTE PTR [EDI],AL
CMP AL,0
JE L2
INC ESI
INC EDI
JMP L1
L2:
POP EBP
RET 8
COPYSTR ENDP

Рис. 2.1.4. Процедура копирования одной строки в другую.

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

Рис. 2.1.5. Вывод текста под углом 90 градусов.

В заключении раздела мы рассмотрим один очень важный вопрос. При разборе предыдущих примеров этот вопрос, скорее всего, у Вас не возникал, и вот почему. Весь вывод информации происходил в программе по получению сообщения WM_PAINT. В реальных программах вывод информации в окно может происходить по различным событиям и из различных процедур. Кроме того, если информации в окне много, то непосредственный вывод при помощи функции TextOut достаточно медленный. Чтобы воспроизводить содержимое окна, необходимо где-то запомнить это содержимое. Возникает проблема сохранения информации (и не только текстовой), находящейся в окне.

Если кто-то программировал для операционной системы MS DOS, то там подобная проблема также возникает. Решается она следующим образом: используется фоновая видеостраница, на которую выводится вся информация. Затем фоновая страница копируется на видимую страницу. При этом создается впечатление, что информация появляется на экране мгновенно. В качестве фоновой страницы используется как область ОЗУ, так и область видеопамяти.

Аналогично в операционной системе Windows образуется виртуальное окно, и весь вывод информации производится туда. Затем по приходе сообщения WM_PAINT содержимое виртуального окна копируется на реальное окно. В целом общая схема такова:

1. При создании окна:

  1. Создается совместимый контекст устройства.
    Функция CreateCompatibleDC. Полученный контекст следует запомнить.
  2. Создается карта бит, совместимая с данным контекстом.
    Функция CreateCompatibleBitmap.
  3. Выбирается кисть цветом, совпадающим с цветом основного окна.
  4. Создается битовый шаблон путем выполнения растровой операции с использованием выбранной кисти. Функция PatBlt.

2. Вся информация выводится в виртуальное окно и дается команда перерисовки окна. Функция InvalidateRect.

3. При получении сообщения WM_PAINT содержимое виртуального окна копируется на реальное окно. Функция BitBlt. Изложенная теория будет применена на практике в следующем разделе.

III

Этот раздел главы посвящается графике. Впрочем, основы графики в Windows в принципе достаточно тривиальны, поэтому мы рассмотрим один простой пример - на вывод графических образов. Но в начале я изложу некоторые опорные моменты:

1. Система координат для вывода графических образов такая же, как и для ввода текстовой информации. Координаты измеряются в логических единицах, которые по умолчанию совпадают с пикселями. При желании эту пропорцию можно изменить.

2. Цвет рисования образуется тремя способами. При использовании функции SetPixel задается цвет данной точки. Для линий необходимо задать цвет пера. Для задания цвета замкнутых графических объектов следует задать цвет кисти.

3. Перо создается при помощи функции CreatePen, кисть - при помощи CreateSolidBrush (мы ее уже использовали). Для создания разноцветной картинки можно заранее создать несколько кистей и перьев, а затем в нужный момент выбирать при помощи функции SelectObject (мы также уже использовали эту функцию).

4. Для рисования можно использовать следующие функции API:

  1. SetPixel - установить заданный цвет пикселя.
  2. LineTo - провести линию от текущей точки до точки с заданными координатами, которая в свою очередь становится текущей.
  3. MoveToEx - сменить текущую точку.
  4. Arc - рисование дуги.
  5. Rectangle - нарисовать прямоугольник.
  6. RoundRect - нарисовать прямоугольник с округленными углами.
  7. Ellipse, Pie - рисование эллипсов и секторов эллипсов.

5. Если при рисовании замкнутой фигуры был установлен цвет кисти, отличный от цвета основного фона, то замкнутая фигура окрашивается этим цветом.

6. Для установки соотношения между логическими единицами и пикселями используется функция SetMapMode.

7. Можно установить область вывода при помощи функции SetViewportExtEx. С помощью функции SetViewportOrgEx можно задать начало области ввода.

После всего сказанного пора продемонстрировать программу. Программа достаточно проста, но в ней заложены основы работы с графикой. По щелчку левой кнопки мыши сначала появляется горизонтальная линия, по второму щелчку - наклонная линия, по третьему щелчку - заполненный прямоугольник. Программа показана на Рис. 2.1.6, результат ее работы - на Рис. 2.1.7.

; файл graph1.inc
; константы
; сообщение приходит при закрытии окна
WM_DESTROY equ 2
; сообщение приходит при создании окна
WM_CREATE equ 1
; сообщение при щелчке левой кнопкой мыши в области окна
WM_LBUTTONDOWN equ 201h
; сообщение приходит при перерисовке окна
WM_PAINT equ 0FH
; свойства окна
CS_VREDRAW equ 1h
CS_HREDRAW equ 2h
CS_GLOBALCLASS equ 4000h
WS_OVERLAPPEDWINDOW equ 000CF0000H
stylcl equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS
DX0 equ 600
DY0 equ 400
; компоненты цветов
RGBW equ (50 or (50 shl 8)) or (255 shl 16) ; цвет окна
RGBR equ 150 ; цвет региона
RGBL equ 0 ; цвет линии
RGBP equ 255 or (100 shl 8) ; цвет точки
; идентификатор стандартной иконки
IDI_APPLICATION equ 32512
; идентификатор курсора
IDC_CROSS equ 32515
; режим показа окна - нормальный
SW_SHOWNORMAL equ 1
; прототипы внешних процедур
EXTERN CreateWindowExA@48:NEAR
EXTERN DefWindowProcA@16:NEAR
EXTERN DispatchMessageA@4:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN GetMessageA@16:NEAR
EXTERN GetModuleHandleA@4:NEAR
EXTERN LoadCursorA@8:NEAR
EXTERN LoadIconA@8:NEAR
EXTERN PostQuitMessage@4:NEAR
EXTERN RegisterClassA@4:NEAR
EXTERN ShowWindow@8:NEAR
EXTERN TranslateMessage@4:NEAR
EXTERN UpdateWindow@4:NEAR
EXTERN BeginPaint@8:NEAR
EXTERN EndPaint@8:NEAR
EXTERN GetStockObject@4:NEAR
EXTERN CreateSolidBrush@4:NEAR
EXTERN GetSystemMetrics@4:NEAR
EXTERN GetDC@4:NEAR
EXTERN CreateCompatibleDC@4:NEAR
EXTERN SelectObject@8:NEAR
EXTERN CreateCompatibleBitmap@12:NEAR
EXTERN PatBlt@24:NEAR
EXTERN BitBlt@36:NEAR
EXTERN ReleaseDC@8:NEAR
EXTERN DeleteObject@4:NEAR
EXTERN InvalidateRect@12:NEAR
EXTERN GetStockObject@4:NEAR
EXTERN DeleteDC@4:NEAR
EXTERN CreatePen@12:NEAR
EXTERN SetPixel@16:NEAR
EXTERN LineTo@12:NEAR
EXTERN MoveToEx@16:NEAR
EXTERN Rectangle@20:NEAR
; структуры
; структура сообщения
MSGSTRUCT STRUC
MSHWND DD ? ; идентификатор окна, получающего сообщение
MSMESSAGE DD ? ; идентификатор сообщения
MSWPARAM DD ? ; доп. информация о сообщении
MSLPARAM DD ? ; доп. информация о сообщении
MSTIME DD ? ; время посылки сообщения
MSPT DD ? ; положение курсора во время
; посылки сообщения
MSGSTRUCT ENDS
WNDCLASS STRUC
CLSSTYLE DD ? ; стиль окна
CLSLPFNWNDPROC DD ? ; указатель на процедуру окна
CLSCBCLSEXTRA DD ? ; информация о доп. байтах для
; данной структуры

CLSCBWNDEXTRA DD ? ; информация о доп. байтах для окна CLSHINSTANCE DD ? ; дескриптор приложения CLSHICON DD ? ; идентификатор иконы окна CLSHCURSOR DD ? ; идентификатор курсора окна CLSHBRBACKGROUND DD ? ; идентификатор кисти окна MENNAME DD ? ; имя-идентификатор меню CLSNAME DD ? ; специфицирует имя класса окон WNDCLASS ENDS PAINTSTR STRUC hdc DD 0 fErase DD 0 left DD 0 top DD 0 right DD 0 bottom DD 0 fRes DD 0 fIncUp DD 0 Reserv DB 32 dup(0) PAINTSTR ENDS RECT STRUC L DD ? ; Х-левого верхнего угла T DD ? ; Y-левого верхнего угла R DD ? ; Х- правого нижнего угла B DD ? ; Y- правого нижнего угла RECT ENDS ; файл graph.asm .386P ; плоская модель .MODEL FLAT, stdcall ;------------------------------------------------------------------ include graph1.inc ; подключения библиотек includelib c:\masm32\iib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ;------------------------------------------------------------------ ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DWORD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> PNT PAINTSTR <?> HINST DWORD 0 TITLENAME BYTE 'Графика в окне',0 NAM BYTE 'CLASS32',0 XT DWORD 30 YT DWORD 30 XM DWORD ? YM DWORD ? HDC DWORD ? MEMDC DWORD ? HPEN DWORD ? HBRUSH DWORD ? P DWORD 0 ; признак вывода XP DWORD ? YP DWORD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; заполнить структуру окна ; стиль MOV [WC.CLSSTYLE],stylcl ; процедура обработки сообщений MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEXTRA],0 MOV [WC.CLSCBWNDEXTRA], 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 ;----------- PUSH RGBW ; цвет кисти CALL CreateSolidBrush@4 ; создать кисть MOV [WC.CLSHBRBACKGROUND], EAX MOV DWORD PTR [WC.MENNAME],0 MOV DWORD PTR [WC.CLSNAME], OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ; создать окно зарегистрированного класса PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DYO - высота окна PUSH DX0 ; DXO - ширина окна PUSH 100 ; координата Y PUSH 100 ; координата X PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; имя окна PUSH OFFSET NAM ; имя класса 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 ; перерисовать видимую часть окна ; петля обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 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+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [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_PAINT JE WMPAINT CMP DWORD PTR [EBP+0CH],WM_LBUTTONDOWN JE LBUTTON JMP DEFWNDPROC LBUTTON: CMP P,0 JNE F1 ; линия точками (горизонтальная) MOV YP,50 ; Y MOV XP,10 ; X MOV ECX,200 LL: PUSH ECX PUSH RGBP PUSH YP PUSH XP PUSH MEMDC CALL SetPixel@16 INC XP POP ECX LOOP LL INC P JMP F3 F1: CMP P,1 JNE F2 ; вначале установим текущие координаты на конец ; предыдущей линии PUSH 0 PUSH YP PUSH XP PUSH MEMDC CALL MoveToEx@16 ; линия пером PUSH 300 PUSH 550 PUSH MEMDC CALL LineTo@12 INC P JMP F3 F2: CMP P,2 JNE FIN ; замкнутая фигура - прямоугольник ; вначале выбрать кисть для заполнения области PUSH HBRUSH PUSH MEMDC CALL SelectObject@8 ; теперь рисуем заполненный прямоугольник если не выбирать ; кисть, то будет нарисован незаполненный прямоугольник PUSH 350 PUSH 400 PUSH 200 PUSH 200 PUSH MEMDC CALL Rectangle@20 INC P F3: ; дать команду перерисовать окно PUSH 0 PUSH OFFSET RECT PUSH DWORD PTR [EBP+08H] CALL InvalidateRect@12 FIN: MOV EAX, 0 JMP FINISH WMPAINT: PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV HDC,EAX ; сохранить контекст (дескриптор) ; скопировать виртуальное окно на реальное PUSH 0CC0020h ; SRCCOPY=изображение как есть PUSH 0 ; у-источника PUSH 0 ; х-источника PUSH MEMDC ; контекст источника PUSH YM ; высота - куда PUSH XM ; ширина - куда PUSH 0 ; у - куда PUSH 0 ; х-куда PUSH HDC ; контекст - куда CALL BitBlt@36 ;---------------- закрыть контекст окна PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 MOV EAX, 0 JMP FINISH WMCREATE: ; размеры экрана PUSH 0 ; X CALL GetSystemMetrics@4 MOV XM, EAX PUSH 1 ; Y CALL GetSystemMetrics@4 MOV YM, EAX ; открыть контекст окна PUSH DWORD PTR [EBP+08H] CALL GetDC@4 MOV HDC,EAX ; создать совместимый с данным окном контекст PUSH EAX CALL CreateCompatibleDC@4 MOV MEMDC, EAX ; создать в памяти растровое изображение, совместимое с hdc PUSH YM PUSH XM PUSH HDC CALL CreateCompatibleBitmap@12 ; выбрать растровое изображение в данном контексте PUSH EAX PUSH MEMDC CALL SelectObject@8 ; цвет кисти PUSH RGBW CALL CreateSolidBrush@4 ; создать кисть ; выбрать кисть в данном контексте PUSH EAX PUSH MEMDC CALL SelectObject@8 ; заполнить данную прямоугольную область PUSH 0F00021h ; РАТСОРУ=заполнить данным цветом PUSH YM PUSH XM PUSH 0 PUSH 0 PUSH MEMDC CALL PatBlt@24 ; создать кисть и перо для рисования ; цвет кисти PUSH RGBR CALL CreateSolidBrush@4 ; создать кисть MOV HBRUSH,EAX ; задать перо PUSH RGBR ; цвет PUSH 0 ; толщина=1 PUSH 0 ; сплошная линия CALL CreatePen@12 MOV HPEN, EAX ; удалить контекст PUSH HDC PUSH DWORD PTR [EBP+08H] CALL ReleaseDC@8 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 HPEN CALL DeleteDC@4 ; удалить кисть PUSH HBRUSH CALL DeleteDC@4 ; удалить виртуальное окно PUSH MEMDC CALL DeleteDC@4 ; выход 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

Puc. 2.1.6. Простая программа для демонстрации графики.

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

  1. Загрузить растровое изображение и запомнить его дескриптор.
  2. Получить контекст устройства для области памяти, где будет храниться изображение.
  3. Выбрать изображение в данном контексте.
  4. Скопировать изображение на экран (BitBlt).

С растровыми изображениями удобно работать при помощи ресурсов, но их можно создавать непосредственно в программе или считывать из файла. Но об этом позднее.

Рис. 2.1.7. Результат работы программы на Рис. 2.1.6.

Замечание. Специалисту в Windows-программировании, возможно, показалось странным, что мы пишем свои собственные строковые функции, вместо того чтобы воспользоваться существующими в Windows соответствующими API-функциями. Да-да, такие функции существуют, дорогой читатель. Причина моего пренебрежения этими функциями проста. Во-первых, я рассчитываю не только на "продвинутых" читателей, но и на людей, обучающихся программированию на ассемблере. Во-вторых, как я уже говорил в предисловии, данная книга - это попытка создания некоторого симбиоза ассемблера и программирования в Windows. Следуя этому принципу, мы не всегда будем решать задачи только средствами API-функций. Однако, понимая всю важность строковых API-функций, в свое время я приведу примеры их использования. Кроме того, в приложении будет дано их полное описание.