>
message
>
ps PAINTSTRUCT >
db
db
db
>
0
приложение Win32 на
Как
в наушниках.
Не нравится? -
db
db
0
0
0
playFileDestroy db
start proc near
точка входа в программу:
начало стартового кода
вызовы расположенных ниже функций
можно при необходимости
но они не являются обязательными в данной программе
вызов BOOL GetVersionEx(LPOSVERSIONINFO IpVersionlnformation)
push offset IpVersionlnformation
call GetVersionExA
далее можно вставить код
для анализа информации о версии Windows
вызов LPTSTR
- получить указатель
на командную строку
call
регистре еах адрес
вызов LPVOID GetEnvironmentStrings (VOID) - получить указатель
на блок с переменными окружения
call GetEnvironmentStringsA ;в регистре еах адрес
вызов VOID GetStartupInfo(LPSTARTUPINFO
на структуру STARTUPINFO
<66>
<67>
<68>
Каркасное Windows-приложение на ассемблере
push offset
call
вызов
<69> push
<70> call GetModuleHandleA ;получить значение базового адреса,
<71>
eax ;по которому загружен модуль.
<72> ;далее hlnst будет использоваться в качестве
;дескриптора данного приложения
<73>
стартового кода
<74>
<75>
класс окна
WNDCLASSEX
<76>
*lpWndClassEx - адрес структуры
<77> ;для начала инициализируем поля структуры WndClassEx
<78> mov
type WNDCLASSEX ;размер структуры
;в wcl.cbSize
<79> mov
<80> mov
offset
;адрес оконной
<81> mov
0
<82> mov
0
<83> mov eax, hlnst
<84>
приложения в поле
структуры wcl
<85> mov
eax
<86>
вызов
;HICON
(HINSTANCE hlnstance, LPCTSTR
<87> push IDI_APPLICATION
значок
<88> push 0 ;NULL
<89> call
<90> mov
eax ;дескриптор значка в поле
wcl
<91>
вызов
;HCURSOR LoadCursorA (HINSTANCE hlnstance, LPCTSTR
<92> push IDC_ARROW
курсор - стрелка
<93> push 0
<94> call LoadCursorA
<95> mov
курсора в поле hCursor
структуры wcl
<96>
цвет фона окна - белый
<97>
вызов HGDIOBJ
fnObject)
<98> push WHITE_BRUSH
<99> call GetStockObject
<100> mov
eax
<101> mov dword ptr
0 ;без главного меню
<102> mov dword ptr
offset
;имя
;класса окна
<103> raov
0
<104>
класс окна - готовим вызов
;RegisterClassExA (&wndclass)
<105> push offset wcl
<10б> call RegisterClassExA
<107> test ax, ax ;проверить на успех регистрации класса окна
<108> jz
<109> создаем окно:
<110>
вызов
;HWND CreateWindowExA(DWORD dwExStyle, LPCTSTR
<111> ;LPCTSTR
DWORD dwStyle, int x, int y, int nWidth,
nHeight, HWND hWndParent, HMENU
HANDLE hlnstance,
<112> ;LPVOID
<113> push 0 ;lpParam
<114> push hlnst
<115> push NULL;menu
<116> push
hwnd
<117> push
окна
<118> push CW_USEDEFAULT
окна продолжение
382 Глава
Создание Windows-приложений на ассемблере
Листинг
(продолжение)
push
у левого верхнего угла окна
<120> push CW_USEDEFAULT ;координата х левого верхнего угла
<121> push
;стиль окна
<122> push offset
;строка заголовка окна
<123> push offset
; имя класса окна
<124> push NULL
<125> call
<126>
eax
- дескриптор окна
<127> ;показать окно:
<128>
вызов BOOL
HWND
)
push
push hwnd
<131> call
<132>
содержимое окна
<133>
вызов BOOL
HWND hWnd )
<134> push hwnd
UpdateWindow
цикл сообщений:
<137>
вызов BOOL
HWND
<138> ;UINT
wMsgFi
)
<139>
<140> push 0
<141> push 0
<142> push NULL
push offset message
<144> call
<145>
ax, 0
<146> je end_cycl_msg
<147>
ввода с клавиатуры
<148>
вызов BOOL TranslateMessage( CONST MSG *lpMsg )
<149> push offset message
<150> call
<151> ;отправим сообщение оконной процедуре
<152>
вызов LONG
CONST MSG
<153> push offset message
<154> call DispatchMessageA
<155> jmp
<156> end_cycl_msg:
<157>
<158>
из приложения
<159>
вызов VOID
( UINT uExitCode )
<160> push N U L L
<161> call
<162> start endp
<163> ;
<164> WindowProc proc
<165> arg
<166> uses ebx,
;эти регистры обязательно должны сохраняться
local
<168> cmp @@mes, WM_DESTROY
<169> je
<170> cmp
<171> je
<172> cmp
WM_PAINT
<173> je
<174> jmp default
<175> wmcreate:
<17б>
создание окна звуковым эффектом
<177>
вызов функции
;BOOL PlaySound(LPCSTR pszSound,
DWORD fdwSound)
push
<179> push NULL
<180> push offset playFileCreate
Каркасное Windows-приложение на ассемблере 383
call PlaySoundA
<182> mov eax, 0
значение - 0
<183> jmp
<184>
<185> push SND_SYNC+SND_FILENAME
<186> push NULL
<187> push offset
<188> call PlaySoundA
<189>
контекст устройства
;HDC
HWND
LPPAINTSTRUCT
)
<190> push offset ps
<191>
<192>
<193>
<194>
<195>
<196>
<197>
<198>
<199>
<200>
<201>
<202>
<203>
<204>
<205>
<206>
<207>
<208>
<209>
<210>
<211>
<212>
<213>
<214>
<215>
<216>
<217>
<218>
<219>
<220>
<221>
<222>
<223>
<224>
<225>
<226>
<227>
<228>
<229>
<230>
<231>
<232>
<233>
push
call
mov
eax
строку текста в окно
TextOut(
hdc, int nXStart. int nYStart,
int cbString )
push
push offset MesWindow
push 100
push 10
push @@hdc
call TextOutA
контекст
;BOOL EndPaint( HWND hWnd, CONST PAINTSTRUCT *lpPaint )
push offset ps
push
call
mov eax, 0
значение - 0
jmp
push SND SYNC+SND FILENAME
push NULL
push offset playFileDestroy
call PlaySoundA
сообщение WM_QUIT
вызов VOID
int nExitCode )
push 0
call
mov eax, 0
значение - 0
jmp
по умолчанию
вызов LRESULT
HWND hWnd,
Msg,
LPARAM
)
push
push
push
push
call
jmp
• • •
exit
ret
endp
end start
Каркасное Windows-приложение на
содержит один сегмент данных
и один сегмент кода
Сегмент стека в исходных текстах Windows-прило-
жений непосредственно описывать не нужно. Windows выделяет для стека объем
памяти, размер которого задан программистом в файле с расширением
Текст
листинга 16.4 довольно большой. Поэтому для обсуждения разобьем его
384 Глава
Создание Windows-приложений на ассемблере
тариями на характерные фрагменты, каждый из которых затем поясним с необхо-
димой степенью детализации.
Строка 3. Все символические имена в программе на ассемблере по умолчанию
являются глобальными. Задание директивы
включает в трансляторе меха-
низм контроля областей видимости имен и позволяет использовать в программе
локальные имена (см. главу 10).
Строка 4. Директива
модель сегментации (flat) и стиль генерации
(см. главу 15) кода при входе в процедуры программы и выходе из них (stdcall).
Модель памяти flat обозначает плоскую модель памяти. В соответствии с этой мо-
делью компилятор создает
которая содержит один 32-разрядный сег-
мент для данных и кода программы. Код загрузочного модуля, генерируемый
с параметром flat, будет работать на процессорах
и выше. По этой причине
директиве
должна предшествовать одна из директив: .386,
или .586.
Указание этой модели памяти заставляет компоновщик создать исполняемый файл
с расширением .ехе. В программе с плоской моделью памяти используется адреса-
ция программного кода и данных типа near. Это же
и атрибута расстояния
в директиве PROC, который также имеет тип near. Параметр stdcall определяет по-
рядок передачи параметров через стек так, как это принято в языке C/C++, то есть
справа налево. Этот параметр задает генерацию кода эпилога, который очищает
стек по завершении работы процедуры (в стиле языка Pascal) от аргументов, пере-
данных в эту процедуру при вызове. Такая передача параметров очень удобна при
разработке Windows-приложений: программист может помещать параметры в стек
в прямом порядке, и ему не нужно заботиться об очистке стека после окончания
работы процедуры.
Строка 7. Директива INCLUDE включает в программу файл
Для чего
нужен этот файл? Вы, наверняка, имеете некоторый опыт разработки Windows-
приложений на C/C++ и знаете, что функции Win32 API в качестве передаваемых
им параметров используют множество констант и данных определенной структу-
ры. В пакете компилятора C/C++ эти данные расположены в заголовочных фай-
лах, совокупность которых можно рассматривать как дерево с корнем в файле
windows.h.
того чтобы эти описания стали доступны приложению на C/C++, в его
начало включается директива
(см. листинг 16.1). Windows-
приложение на ассемблере также использует вызовы функций Win32 API, поэто-
му в нем должны быть выполнены аналогичные действия по определению необхо-
димых констант и структур. Но просто взять из пакета C/C++ и затем использовать
в программе на ассемблере файл
и связанные с ним файлы напрямую
нельзя. Причина банальна: транслятор ассемблера не понимает синтаксиса C/C++.
Что делать? Пакет
предоставляет в распоряжение программиста утилиты
h2ash.exe и h2ash32.exe, которые должны помочь ему конвертировать содержимое
включаемых файлов на языке C/C++ во включаемые файлы ассемблера. Однако,
исходя из опыта, не стоит питать особых надежд на эффективность их работы.
Поэтому проще формировать ассемблерный включаемый файл с описанием кон-
стант и структур Windows самостоятельно. Кстати, пакет TASM 5.0 предоставля-
ет в распоряжение программиста вариант такого включаемого файла
Его
можно найти среди прилагаемых к книге файлов в каталоге к данной главе, но
Каркасное Windows-приложение на ассемблере 385
пользоваться им нужно с осторожностью, так как его содержимое не совсем акту-
ально. Например, файл
содержит описание структуры WNDCLASS, но в нем
отсутствует расширенный вариант этой структуры WNDCLASSEX, который требует-
ся в расширенном варианте
Как решить проблему акту-
альности описаний? Проще всего сделать это, используя включаемые файлы по-
следних на
момент времени версий компилятора C/C++ (сейчас это,
например, VC++ 6.0 и 7.0). Это один из основных источников подобной информа-
ции, актуальность которой к тому же гарантирована разработчиком компиля-
тора, являющегося одновременно и создателем операционной системы Windows.
Поэтому программирование на ассемблере для Windows предполагает, что вы хо-
рошо умеете ориентироваться во включаемых файлах компилятора C/C++. Пол-
ностью пытаться конвертировать эти файлы не имеет смысла. Более правильно
извлекать из них по мере необходимости описание нужных
приводить их
в соответствие требованиям
ассемблера и после этого записывать в не-
который свой файл, который будет играть роль файла
Для Windows-
приложений данной главы вам предлагается вариант такого включаемого фай-
ла —
который включается в текст приложения директивой i n c l u d e
Файл windowA.inc имеется среди
прилагаемых к книге. Его
можно взять за основу для дальнейшей работы и наращивать по мере необхо-
димости.
Строки 9-33. Функции Win32 API, используемые в программе, должны быть
объявлены внешними с помощью директивы EXTRN. Это необходимо для того, что-
бы компилятор мог сгенерировать правильный код, так как тела функций Win32
API содержатся в библиотеках DLL системы Windows.
Имейте в виду, что в различных источниках встречаются разные
ка-
залось бы, одной и той же функции. Необходимо правильно понимать их. Напри-
мер, уже упомянутая функция
может иметь названия RegisterClassExA
или RegisterClassExW. На самом деле две последние функции являются более совре-
менными вариантами RegisterClass. Для разрешения подобных коллизий приходится
обращаться к первоисточникам — документации с описанием функций Win32 API
(помните, что она также может оказаться устаревшей). Самый лучший источник
такой информации — включаемые файлы компилятора C/C++. Например, описа-
ние функции
содержится в файле winuser.h. В листинге 16.5 при-
строки из этого файла.
Листинг
Фрагмент файла winuser.h (VC++ 6.0)
<1>
ATOM WINAPI
<2> WINUSERAPI ATOM WINAPI
<3>
UNICODE
<4> #define
#else
<6>
<7>
//
<8>
>= 0x0400)
<9> WINUSERAPI ATOM WINAPI
WNDCLAS5EXA
<10> WINUSERAPI ATOM WINAPI
WNDCLASSEXW *);
<11>
UNICODE
<12>
Regi sterClassEx
продолжение
13
256
12>11>10>9>8>7>6>4>3>2>1>233>232>231>230>229>228>227>226>225>224>223>222>221>220>219>218>217>216>215>214>213>212>211>210>209>208>207>206>205>204>203>202>201>200>199>198>197>196>195>194>193>192>191>190>189>188>187>186>185>184>183>182>180>179>177>175>174>173>172>171>170>169>168>166>165>164>163>162>161>160>159>158>157>156>155>154>153>152>151>150>149>148>147>146>145>144>142>141>140>139>138>137>134>133>132>131>128>127>126>125>124>123>122>121>120>118>117>116>115>114>113>112>111>110>109>108>107>105>104>103>102>101>100>99>98>97>96>95>94>93>92>91>90>89>88>87>86>85>84>83>82>81>80>79>78>77>76>75>74>73>72>71>70>69>68>67>66> Do'stlaringiz bilan baham: |