Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование на ассемблере под Windows с нуля Выпуск№8


Информационный Канал Subscribe.Ru


В поисках истины

или программирование на ассемблере под Windows с нуля.


Выпуск №8

Сегодня в выпуске:


Обращение к подписчикам

   
Все-таки, это случилось!!! Вышел новый выпуск рассылки!!! Несмотря на все препятсвия (завал на работе и мой переезд в другой город на лето), мы снова вместе ;-).
Сегодня я вам дам адресок сайта моей команды вот он TLRH.nm.ru. На нем в данный момент находится архив рассылки и пара статей моего коллеги по команде. В ближайшее время на этом сайте появятся книги и различная документация по ассемблеру, которые по нашему мнению явяляются наиболее интересными.
Ну что же, приступаем к занятиям!!!



Глава №4

Hello World. Первая программа с графическим интерфейсом .

    Сегодня я решил дать вам немного отдохнуть от тонкостей низкоуровневого программирования. Сегодня мы напишем несколько разных программ, которые можно назвать полноценными Windows-программами. Я вам расскажу о двух API-функциях: функции выведения окна-сообщения (MessageBox) и выхода из программы (ExitPtrocess). Для первого знакомства предостаточно. Прежде чем мы приступим к написанию программы, немного теории (а как же без этого ;-) ). Основой в программирование под Windows, является использование использования специальных функций - API-функции. API - Application Programming Interface ( прикладной программный интерфейс). Windows API - это большая коллекция функций, располагающихся непосредственно в операционной системе. Эти функции находятся в нескольких динамически компонуемых библиотек (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех, существуют также другие библиотеки, которые вы можете использовать. Windows программы динамически подсоединяется к этим библиотекам, то есть код API функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и библиотек, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу. Существует две категории API функций: одна для ANSI и другая для Unicode. Hа конце имен API функций для ANSI стоит "A", например, MessageBoxА. В конце имен функций для Unicode находится "W". Windows 95 изначально поддерживает ANSI и WIndows NT Unicode. Мы обычно имеем дело с ANSI строками (массивы символов, оканчивающиеся нулем. Размер ANSI-символа - 1 байт. ANSI достаточна для европейских языков, но она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE - 2 байта, и поэтому может поддерживать 65536 уникальных символов. Сейчас мы шаг за шагом будем писать исходный текст нашей программы, и по ходу я буду объяснять, что мы делаем и почему так. Каркас программы вам хорошо знаком, вот он:

.486
.model flat, stdcall
option casemap:none
.data

<Здесь ваши инициализируемые данные>

.data?

<Здесь ваши не инициализируемые данные>

.const

<Здесь ваши константы>

.code
start:

<Здесь ваш код>

end start

    Для того чтобы программа работала с API-функциями, как уже было сказано, необходимо импортировать функции из динамически компонуемых библиотек Windows. Чтобы это сделать, необходимо подключить к вашей программе библиотеки импорта (*.lib). Это осуществляется с помощью директивы includelib. Эти библиотеки входят в состав пакета MASM32. Их имена соответствуют именам динамически компануемых библиотек (далее DLL), например, если мы импортируем функции из библиотеки Kernel32.dll, то нам необходимо подключить библиотеку kernel32.lib. С этим все просто. Обе API-функции, которые мы рассматриваем на сегодняшнем уроке находятся в kernel32.dll. Теперь наш каркас приобретает такой вид:

.486
.model flat, stdcall
option casemap:none


includelib C:\masm32\lib\kernel32.lib

.data

.data?

.const

.code start:

end start

    Как видите после директивы includelib пишется полный путь к библиотеке импорта. Если вы инсталлировали MASM32 по умолчанию в корневой каталог, то путь у вас будет именно, таким, если в другое место, то и путь придется писать другой. Кроме того, что нам необходимо импортировать API-функции, для того чтобы их использовать, нам необходимо описать их прототип. Вот как выглядит прототип функции ExitProcess

ExitProcess proto uExitCode:DWORD

    Прототип указывается, для того чтобы ассемблер знал, какую функцию мы будем вызывать, и с каким количеством данных, и какого размера, эта функция будет работать. Как узнать, каким образом описывать прототип? Для этого нам понадобиться справочник Win32 Programmer's Reference, он входит в пакет Delphi и Builder. Так же его можно скачать здесь. Открываем справочник и находим в нем ExitProcess. В нем вы увидите следующие строки

VOID ExitProcess (
UINT uExitCode
                                );

   Что это? Это API-функция в том виде, в котором она используется при программировании на языке Cи. Мы замечаем, что у этой функции всего один параметр - uExitCode. Как узнать размер этого параметра? Размер написан в начале этого параметра - UINT.

   Вы, естественно возмущаетесь, что это за размер такой? Мы таких не знаем! Знаем! Просто, господа, которые создавали API были с интересным складом ума. Они понапридумывали столько... Со всеми этими "наворотами" я буду вас знакомить по мере введения новых "терминов". UINT - размер Dworld. Значение этого параметра должно быть 0 (нуль)

    Теперь несколько слов о синтаксисе прототипа. Вот полный вид конструкции прототипа:

ИмяФункции PROTO [ИмяПаpаметpа]:ТипДанных,[ИмяПаpаметpа]:ТипДанных,...

   Как видите ничего сложного. Даже комментарии излишне. Теперь пришло время познакомится с одной ассемблерной командой, которая осуществляет вызов подпрограммы, в нашем случае API-функции. Имя этой команды - call. Как я говорил в предыдущем выпуске рассылки, данные, используемые API функции необходимо загружать в стек. В следствии этого, собственно вызов функции будет иметь следующий вид:

Push 0 ; кладем в стек нуль (параметр uint)
Call ExitProcess; вызов функции выхода.


   А текст программы будет таким.

.486
.model flat, stdcall
option casemap:none

includelib \masm32\lib\kernel32.lib

ExitProcess proto uExitCode:DWORD

.data

.code
start:

push 0
call ExitProcess

end start


Вот программа работающая, но ни как себя не проявляющая, с чего бы это ;-).

    Теперь когда вы обладаете небольшим запасом теоретических знаний переходим к написанию программы выводящей окно-сообщение. Это уже интересно!!! Сейчас мы пройдем все этапы, которые вы будете проходить при использовании не известных вам API-функций. Открываем Win32 Programmer's Reference, ищем функцию MessageBox. Выписываем Сишный вид API-функции:

int MessageBox (
HWND hWnd,  // хендл родительского окна
LPCTSTR lpText,  // адрес текста окна
LPCTSTR lpCaption,  // адрес заголовка окна
UINT uType  // стиль окна
);


    Как много всего нового, непонятного... Ничего, сейчас будем разбираться. Мы начнем разбираться с середины - с размера параметров.

   HWND  h     хэндл  DWORD 
 LPCTSTR  lp  указатель   DWORD 


Теперь разберемся с комментариями. Хендл - новое для вас понятие. Хендл - 32-разрядный номер, при помощи которого окно может быть однозначно идентифицировано. Что такое родительское окно? Мы к этому вопросу вернемся позже, сейчас скажу, что в данном случае у нас родительское окно отсутствует, поэтому этот параметр будет равен нулю. Про адреса вы немного уже знаете ;-), чтобы поместить куда-либо (в регистр, в стек) адрес строки необходимо применять директиву offset. Поясняю, если нам необходимо в регистр eax поместить адрес строки, то данная команда будет иметь следующий вид:

mov eax, offset Stroka


если необходимо поместить в стек, то вид такой
push offset Stroka

   Теперь про стиль окна. Я приведу текст из справочника, в моем переводе =).

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

Флажок                                           Значение

MB_ABORTRETRYIGNORE    Окно сообщений содержит три кнопки : Прервать, Повторить, и Игнорировать.

MB_OK                                          Окно сообщений содержит одну кнопку : OK. Это - значение по умолчанию.

MB_OKCANCEL                          Окно сообщений содержит две кнопки: OK и Отмена.

MB_RETRYCANCEL                 Окно сообщений содержит две кнопки: Повторить и Отмена.

MB_YESNO                                 Окно сообщений содержит две кнопки: Да и Нет

MB_YESNOCANCEL                 Окно сообщений содержит три кнопки: Да, Нет, и Отмена.

Определите один из следующих флажков, чтобы отобразить иконку в окне сообщений:

Флажок                                              Значение

MB_ICONEXCLAMATION,       иконка знака восклицания.
MB_ICONWARNING

MB_ICONINFORMATION,
MB_ICONASTERISK                  иконка, состоящая из символа " i " в круге.


MB_ICONQUESTION                 иконка вопроса.

MB_ICONSTOP,
MB_ICONERROR,                      иконка остановки.


   Мы рассмотрим несколько вариантов использования флажков. С теми, которые останутся, вы сможете разобраться самостоятельно. Пишем прототип функции.

MessageBox PROTO hWnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD

    Как я уже говорил, многие API-функции бывают двух видов, вызов окна с сообщением как раз к таким и относится. Поэтому прототип функции мы должны немного изменить. На конце слова "MessageBox", мы должны дописать либо "A" либо "W", мы, естественно, выбираем "A" (объяснение читайте выше), в связи с этим прототип нашей функции примет такой вид:

MessageBoxA PROTO hWnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD

Чтобы нам было проще работать с функцией мы в коде своей программы будем использовать не MessageBoxA, а MessageBox, но для этого мы должны "познакомить". Для этого мы напишем вот такую строку в исходном коде:

MessageBox equ <MessageBoxA>

   В данной строке вам встретилась новая "директива". Почему слово "директива", я поставил в кавычки? Дело в том, что это не совсем директива. Это так называемый "псевдооператор". Этот псевдооператор предназначен для присвоения некоторому выражению символического имени. Что мы и сделали. Более подробно разберем псведооператоры (он не один), когда будем разбирать макросредства MASM'a. Я думаю, вам это не о чем не говорит ;-), но мое дело предупредить. Для начала мы напишем программу, выводящую окно с одной кнопкой и без каких-либо стилей. Вот исходник программы:

.386
.model flat, stdcall
option casemap:none


includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib


MessageBoxA PROTO hWnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
MessageBox equ <MessageBoxA>
ExitProcess PROTO uExitCode:DWORD


.data
Caption db "SeDoYHg tutorial ;-)",0
text db "Asm is cool!!!",0

.code
start:
     push 0
     push offset Caption
     push offset text
     push 0
     call MessageBox
     push 0
     call ExitProcess
end start

    Откомпилировав этот код, и запустив его, вы увидите окошко. Ну, что же для начала совсем не плохо ;-). Теперь давайте попробуем немного "раскрасить" наше окошко, для этого применим один из стилей.

    Я хочу сделать окно с двумя кнопками, и с иконкой знака вопроса. Чтобы объединить стили, необходимо объединить их с помощью директивы or. Но тут еще один момент, неободимо заменить имена стилей на их значение. Что это значит? Каждому стилю соответствует определенное число - константа, эти значения известны. Вот "список соответствия" стилей и их числовые значения:

MB_OK                                   equ        0h

MB_OKCANCEL                       equ       1h

MB_ABORTRETRYIGNORE      equ       2h

MB_YESNOCANCEL                 equ       3h

MB_YESNO                             equ      4h

MB_RETRYCANCEL                 equ       5h

MB_ICONHAND                       equ       10h

MB_ICONQUESTION                equ       20h

MB_ICONEXCLAMATION          equ       30h

MB_ICONASTERISK                equ       40h

    Сразу возникает вопрос, где я нашел значения этих стилей? Скажу по секрету ;-), я их нашел в файле Winuser.h, который входит в состав пакета MS Visual C++. Стили в этом файле описаны немного по другому, но для меня это не проблема =). Предвижу вашу реакцию: "где мы возьмем этот файл?" Не много подождите я вам все объясню ;-). Прежде чем мы приступим к написанию кода, я вам должен рассказать об одной важной вещи. Неужели нужно каждый раз набивать строки с прототипами и описаниями стилей и прочее, прочее? В этом нет необходимости, мы можем создать файл, в котором будут находится все "описания", и подсоединять его к нашему коду. Каким образом это сделать? Ну, во-первых, такой файл должен быть с расширением *.inc (такие файлы принято называть инклуды), и в начале нашего кода мы должны написать такую строку:

include \masm32\include\*.inc

Главным словом этой строки является директива include, она означает, что нам необходимо "объединить" файл нашего кода и инклуд, путь к которому пишется после директивы include. На месте "звездочки" будет имя нашего инклуда. Если инклуд находится в одной папке с файлом кода, то путь можно не писать:

include *.inc

Давайте создадим три инклуда: kernel32.inc, user32.inc и windows.inc. В инклуде kernel32.inc будут описаны прототипы API-функций импортируемых из библиотеки kernel32.dll, в user32.inc - из user32.dll, а в инклуде windows.inc - будут описаны стили. Таким образом, у нас будет три инклуда следующего содержания:

kernel32.inc

ExitProcess PROTO :DWORD

user32.inc

MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD
MessageBox equ <MessageBoxA>


windows.inc

MB_OK                                   equ        0h

MB_OKCANCEL                       equ       1h

MB_ABORTRETRYIGNORE      equ       2h

MB_YESNOCANCEL                 equ       3h

MB_YESNO                             equ      4h

MB_RETRYCANCEL                 equ       5h

MB_ICONHAND                       equ       10h

MB_ICONQUESTION                equ       20h

MB_ICONEXCLAMATION          equ       30h

MB_ICONASTERISK                equ       40h

MB_ICONERROR                    equ       MB_ICONHAND

MB_ICONINFORMATION         equ       MB_ICONASTERISK

MB_ICONSTOP                      equ        MB_ICONHAND

MB_ICONWARNING               equ        MB_ICONEXCLAMATION

   Давайте создадим для инклудов отдельную папку в той же папке, где находятся ваши исходники, с названием include. Вот теперь мы готовы написать программу с двумя кнопками и иконкой!!! Вод код этой программы:

.386
.model flat, stdcall
option casemap:none


include include\windows.inc
include include\kernel32.inc
include include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib


.data
Caption db "SeDoYHg tutorial ;-)",0
text db "Asm is cool!!!",0

.code
start:
     push MB_YESNO or MB_ICONINFORMATION
     push offset Caption
     push offset text
     push 0
     call MessageBox
     push 0
     call ExitProcess
end start

    Тут есть один момент, который я не рассказал, догадайтесь сами (это будет вашим первым домашним заданием. Задание на внимание =).

    Теперь вы можете потренироваться с разными иконками, с разными кнопками, образец я вам дал. Конечно, "повествование" о MessageBox'е на этом не закончено, я продожу объяснение в следующем выпуске рассылки, который будет скоро (обещаю =). Я думаю вам хватит материала для размышления ;-), на ближайших 3-4 дня.

За сим прощаюсь. С уважением, SeDoYHg[TLRH].

    P.S. жду ваших вопросов и предложений. А то у меня складывается такое впечатление, что кроме меня эта рассылка никому не нужна, постарайтесь разубедить меня своими письмами =) или вопросами на форуме моей команды.

   

До новых встреч!!!
С уважением SeDoYHg[TLRH]

Обратная связь

   Если вы хотите что-то спросить по ассемблеру, крэку, или просто поболтать прошу на Форум моей команды , там вы сможете получить ответы от меня, и моих товарищей Mafia32, formatC Вы можете отправить письмо на мой почтовый ящик , только в том случае если вопрос имеет отношение к рассылке. Обязательно заполняйте поле "Тема", письма без темы, я не буду читать. Для вашего удобства я разместил в рассылке e-mail форму, вы можете прямо из нее отправлять свое письмо, но для этого должна быть настроена ваша почтовая программа.

Имя:
Тема:
Сообщение:



Копирайты

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

Copyright © 2004 SeDoYHg

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу


В избранное