assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 85
Текст из файла (страница 85)
Если продолжить изучение этого стартового кода в файле prg!6_l.lst, то можно увидеть вызов функции_М/тМа1п@1б. Найдемтеперь тело этой функции в тексте. Даже беглое сравнение функции_МтМа1п@1б(см. листинг 16.3) и функции WinMain (см. листинг 16.1) показывает, что мы нашлиместо, где содержится код ассемблера, функционально эквивалентный коду наC/C++.
В частности, хорошо видно, что в _WinMain@16 вызываются именно тефункции (и никакие другие), что и в функции WinMain программы на C/C++.Мы не будем сейчас обсуждать порядок и цель вызова каждой из этих функций, как это делалось для стартовой процедуры, по той причине, что следующий1Описания всех упомянутых в книге функций и структур данных Win32 API приводятся лишь частично. Полную информацию о них вы можете получить в многочисленных источниках, в которыхрассматриваются вопросы разработки Windows-приложений. Предпочтительно использовать первоисточник, находящийся в Интернете по адресу: http://www.microsoft.com/msdn/.Каркасное Windows-приложение на ассемблере379наш шаг — это создание программы на языке ассемблера.
В процессе ее разработкимы и рассмотрим эти вопросы достаточно подробно. Сейчас нам важно сделатьвывод, что функция WinMain не имеет прямого отношения к функциям Win32 API.Эта функция используется лишь для того, чтобы компилятор мог сгенерироватькод, выполняющий инициализацию приложения.Кстати, если вы внимательно посмотрите на листинг 16.3, то без труда обнаружите другие фрагменты кода исполняемого файла, прямо соответствующие тексту исходного файла на C/C++. Например, в качестве упражнения найдите текстоконной функции.В заключение обсуждения обратите внимание на код завершения программы:004012С6call _WinMain@16004012СВpush eax004012ССcall _exitВидно, что для завершения приложения вызывается процедура _exit.
Код, который она содержит, является обязательным для корректного завершения любогоWindows-приложения. Более подробно мы его обсудим при разработке каркасного Windows-приложения на языке ассемблера.Каркасное Windows-приложениена ассемблереОдним из главных критериев выбора языка разработки Windows-приложения является наличие в нем средств, способных поддержать строго определенную последовательность шагов. Язык ассемблера является универсальным языком и пригоден для реализации любых задач, поэтому можно смело предположить, что на немможно написать также любое Windows-приложение.
Материал, изложенный ранее, наглядно это доказал. Более того, стали видны некоторые подробности кода,который должно содержать Windows-приложение на ассемблере. Но мало написать сам текст Windows-приложения, необходимо знать средства пакета транслятора, специально предназначенные для разработки таких приложений, и уметьпользоваться этими средствами.В листинге 16.4 приведен текст каркасного приложения на ассемблере, функционально эквивалентного Windows-приложению на C/C++ (см.
листинг 16.1).Если не обращать внимания на особенности оформления кода, обусловленные требованиями синтаксиса ассемблера, то хорошо видно, что на уровне функций егоструктура аналогична рассмотренному ранее Windows-приложению на C/C++.Листинг 16.4. Каркасное Windows-приложение на ассемблере<1><2><3><4><5><б><7>;Пример каркасного приложения для W i n 3 2.386locals ;разрешает применение локальных меток в программе.model flat, STDCALL;модель памяти flat;STDCALL - передача параметров в стиле С (справа налево),; вызываемая процедура чистит за собой стекinclude windowA.incвключаемый файл с описаниями базовых структур;и 'констант Win32<8> Объявление внешними используемых в данной программе;функций Win32 (ASCII):<9>extrnGetModuleHandleA:PROCпродолжение &380Глава 16.
Создание Windows-приложений на ассемблереЛистинг 16.4 (продолжение)<20><22><23><24><25><26><27><28><29><30><32><33><34><35><36><37><38><39><40><42><43><44><45><46><47><48><49><50><52><53><54><55><56><57><58><59><60><62><63><64><65>extrnGetVersionExA:PROCextrnGetComraandLineA:PROCextrnGetEnvironmentStringsA:PROCGetEnvi ronmentStringsA:PROCextrnextrnGetStartupInfoA:PROCLoadIconA:PROCextrnLoadCursorA:PROCextrnGetStockObject:PROCextrnextrnRegisterClassExA:PROCCreateWindowExA:PROCextrnextrnShowWindow:PROCUpdateWindow:PROCextrnextrnGetMessageA:PROCTranslateMessage:PROCextrnDi spatchMessageA:PROCextrnextrnExitProcess:PROCextrnPostQuitMessage:PROCDefWindowProcA:PROCextrnextrnPlaySoundA:PROCextrnReleaseDC:PROCextrnTextOutA:PROCGetDC:PROCextrnBeginPaint:PROCextrnextrnEndPaint:PROCобъявление оконной функции объектом,;видимым за пределами данного кодаpublic WindowProc.datahwnddd 0hlnstdd 0hdcdd 0IpVersionlnformationOSVERSIONINFO<?>wcl WNDCLASSEX <?>message MSG <?>ps PAINTSTRUCT <?>szClassName db 'Приложение Win32', 0szTitleName db 'Каркасное приложение Win32 на ассемблере',MesWindowdb 'Привет! Как звук в наушниках.Не нравится? - Отключите!!!'MesWindowLen=$-MesWindowplayFileCreate db 'create.wav', 0db 'paint.wav', 0playFilePaintplayFileDestroy db 'destroy.wav', 0.codestartproc nearточка входа в программу:начало стартового кодавызовы расположенных ниже функцийможно при необходимости раскомментировать,но они не являются обязательными в данной программевызов BOOL GetVersionEx(LPOSVERSIONINFO IpVersionlnformation)pushoffset IpVersionlnformationcallGetVersionExAдалее можно вставить коддля анализа информации о версии Windowsвызов LPTSTR GetCommandLine(VOID) - получить указательна командную строкуcallGetCommandLineA ;в регистре еах адресвызов LPVOID GetEnvironmentStrings (VOID) - получить указательна блок с переменными окруженияcallGetEnvironmentStringsA ;в регистре еах адресвызов VOID GetStartupInfo(LPSTARTUPINFO IpStartupInfo) -указательна структуру STARTUPINFOКаркасное Windows-приложение на ассемблере<66><67><68><69><70><71><72><73><74><75><76><77><78><79><80><81><82><83><84><85><86><87><88><89><90><91><92><93><94><95><96><97><98><99><100><101><102><103><104><105><10б><107><108><109><110><111><112><113><114><115><116><117><118>381pushoffset IpStartupInfocallGetStartupInfoAвызов HMODULE GetModuleHandleA (LPCTSTR IpModuleName)pushNULL;0->GetModuleHandlecallGetModuleHandleA;получить значение базового адреса,mov hlnst, eax;по которому загружен модуль.;далее hlnst будет использоваться в качестве;дескриптора данного приложения;конец стартового кодаWinMain:определить класс окна;АТОМ RegisterClassEx(CONST WNDCLASSEX *lpWndClassEx),;где *lpWndClassEx - адрес структуры WndClassEx;для начала инициализируем поля структуры WndClassExmovwcl.cbSize, type WNDCLASSEX ;размер структуры;в wcl.cbSizemovwcl.style, CS_HREDRAW+CS_VREDRAWmovwcl.IpfnWndProc, offset WindowProc ;адрес оконнойпроцедурыmovwcl.cbClsExtra, 0movwcl.cbWndExtra, 0moveax, hlnst-.дескриптор приложения в поле hlnstance структуры wclmovwcl.hlnstance, eax;готовим вызов;HICON Loadlcon (HINSTANCE hlnstance, LPCTSTR IpIconName)pushIDI_APPLICATION стандартный значокpush0;NULLcallLoadlconAmovwcl.hlcon, eax ;дескриптор значка в поле hlcon;структуры wcl;готовим вызов;HCURSOR LoadCursorA (HINSTANCE hlnstance, LPCTSTR IpCursorName)pushIDC_ARROWстандартный курсор - стрелкаpush0callLoadCursorAmov wcl.hCursor, eax;дескриптор курсора в поле hCursorструктуры wcl;определим цвет фона окна - белый;готовим вызов HGDIOBJ GetStockObject(int fnObject)pushWHITE_BRUSHcallGetStockObjectmovwcl.hbrBackground, eaxmovdwordptr wcl.IpszMenuName, 0 ;без главного менюmovdword ptr wcl.IpszClassName, offset szClassName ;имя;класса окнаraovwcl.hlconSm, 0регистрируем класс окна - готовим вызов;RegisterClassExA (&wndclass)pushoffset wclcallRegisterClassExAtestax, ax ;проверить на успех регистрации класса окнаjzend_cycl_msg;неудачасоздаем окно:;готовим вызов;HWND CreateWindowExA(DWORD dwExStyle, LPCTSTR IpClassName,;LPCTSTR IpWindowName, DWORD dwStyle, int x, int y, int nWidth,lint nHeight, HWND hWndParent, HMENU hMenu, HANDLE hlnstance,;LPVOID IpParam)push0;lpParampushhlnst;hlnstance'pushNULL;menupushNULL;parent hwndpushCWJJSEDEFAULT;высота окнаpushCW_USEDEFAULT;ширина окнапродолжение &382Глава 16.
Создание Windows-приложений на ассемблереЛистинг 16.4 (продолжение)<119><120><121><122><123><124><125><126><127><128><129><130><131><132><133><134><135><136><137><138><139><140><141><142><143?<144><145><146><147><148><149><150><151><152><153><154><155><156><157><158><159><160><161><162><163><164><165><166><167><168><169><170><171><172><173><174><175><17б><177><178><179><180>pushCW_USEDEFAULT;координата у левого верхнего угла окнаpushCW_USEDEFAULT;координата х левого верхнего углаpushWS_OVERLAPPEDWINDOW ;стиль окнаpushoffset szTitleName ;строка заголовка окнаpushoffset szClassName ; имя класса окнаpushNULLcallCreateWindowExAmovhwnd, eax;hwnd - дескриптор окна;показать окно:;готовим вызов BOOL ShowWindow( HWND hWnd, int nCmdShow )pushSW_SHOWNORMALpushhwndcallShowWindow;перерисовываем содержимое окна;готовим вызов BOOL UpdateWindow( HWND hWnd )pushhwndcallUpdateWindow;запускаем цикл сообщений:;готовим вызов BOOL GetMessageA( LPMSG IpMsg, HWND hWnd,;UINT wHsgFilterMin, UINT wMsgFi UerMax )cycl_msg:push0push0pushNULLpushoffset messagecallGetMessageAcmpax, 0jeend_cycl_msgтрансляция ввода с клавиатуры;готовим вызов BOOL TranslateMessage( CONST MSG *lpMsg )pushoffset messagecallTranslateMessage;отправим сообщение оконной процедуре;готовим вызов LONG DispatchMessage( CONST MSG *lpmsg)pushoffset messagecallDispatchMessageAjmpcycl_msgend_cycl_msg:;выход из приложения;готовим вызов VOID ExitProcess ( UINT uExitCode )pushNULLcallExItProcessstartendp;WindowProc-WindowProc procarg @@hwnd:DWORD, @@mes:DWORD, @@wparam:DWORD, @@lparam:DWORDuses ebx, edi, esi ;эти регистры обязательно должны сохранятьсяlocal@@hdc:DWORDcmp @@mes, WM_DESTROYje wmdestroycmp @@mes, WM_CREATEje wmcreatecmp @@mes, WM_PAINTje wmpaintjmp defaultwmcreate:юбозначим создание окна звуковым эффектом;готовим вызов функции;BOOL PlaySound(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound)pushSND_SYNC+SND_FILENAMEpushNULLpushoffset playFileCreateКаркасное Windows-приложение на ассемблере<182><183><184><185><186><187><188><189><190><191><192><193><194><195><196><197><198><199><200><201><202><203><204><205><206><207><208><209><210><211>callPlaySoundAmoveax, 0 {возвращаемое значение - 0jmpexit_wndprocwmpaint:pushSND_SYNC+SND_FILENAMEpushNULLpushoffset playFilePaintcallPlaySoundA{получим контекст устройства;HDC BeginPaintC HWND hwnd, LPPAINTSTRUCT IpPaint )pushoffset pspush@@hwndcallBeginPaintmov@@hdc, eax{выведем строку текста в окно{BOOL TextOut( HOC hdc, int nXStart.