Ассемблерное программирование в Windows

15.09.2004
Новиков Максим Глебович.

Глава 4. Исходный текст второй программы

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

bloknot.asm — основной файл программы
bloknot.rc — файл ресурсов
def32.inc — описания структур и присвоения константам имен.
kernel32.inc — объявления внешних функций, отвечающих за управление программой
user32.inc — объявления внешних функций, отвечающих за оконный интерфейс


Файл «bloknot.asm» - основной файл программы
include def32.inc    ;Включить в исходник файл с определениями разных констант 
                     ;и структур.
include kernel32.inc ;Включить файл с объявлениями внешних функций 
                     ;из библиотеки kernel32.dll.
include user32.inc   ;Включить файл с объявлениями внешних функций 
                     ;из библиотеки user32.dll, в которую входят
                     ;все основные функции, отвечающие за оконный интерфейс.
.386          ;Пишем код в командах процессора 80386
.model flat   ;Плоская модель памяти

.data         ;Инициализированные или частично инициализированные данные

class_name    db "NotePadWiindow",0     ;Имя класса окна.
window_name   db "Блокнотик",0          ;Название окна (выводится в его заголовке)
win_menu      db "Выбран пункт меню:",0 ;Название окна, вызываемого через меню

about_msg     db "О программе",0        ; \
open_msg      db "Открыть",0            ;  > Тексты для окон сообщений
save_msg      db "Сохранить",0          ; /

;Определим структуру wc типа WNDCLASSEX, описывающую класс нашего окна.
;Для создания объекта структуры wc используем описание структуры WNDCLASSEX 
;из файла def32.inc. В структуре помимо прочих параметров прописываем
;идентификатор меню, созданного в файле ресурсов (помечен красным цветом).

wc WNDCLASSEX <4*12,CS_HREDRAW or CS_VREDRAW,offset win_proc,\
    0,0,?,?,?,COLOR_WINDOW+1,1,offset class_name,0>

.data?    ;Неинициализированные данные

;Определим структуру msg_ типа MSG, в которую будут записываться сообщения 
;после вызова GetMessage.
;Для создания объекта структуры msg_ используем описание структуры MSG 
;из файла def32.inc.

msg_    MSG <>

.code

_start:
    xor     ebx,ebx              ;Занести в bx ноль
    ;Создадим класс нашего окна, заполнив недостающие поля структуры wc 
    ;и зарегистрировав его.
    push    ebx
    call    GetModuleHandle      ;Определить идентификатор нашей программы
    mov     esi,eax                    ;Занести его в esi
    mov     dword ptr wc.hInstance,eax ;Заполним идентификатор нашего процесса
    push    IDI_APPLICATION            ;Стандартная иконка приложения
    push    ebx                  ;Идентификатор модуля с иконкой — нулевой
    call    LoadIcon             ;Загрузить иконку
    mov     wc.hIcon,eax         ;Идентификатор иконки для нашего класса
    push    IDC_ARROW            ;Стандартная форма курсора
    push    ebx                  ;Идентификатор модуля с курсором — нулевой
    call    LoadCursor           ;Загрузить курсор
    mov     wc.hCursor,eax       ;Идентификатор курсора для нашего класса
    push    offset wc            ;Смещение к описанию класса окна
    call    RegisterClassEx      ;Зарегистрировать класс окна
    ;Создать окно нашего класса
    mov     ecx,CW_USERDEFAULT   ;Идентификатор значения по умолчанию занесем в cx
    push    ebx                  ;Адрес структуры CREATESTRUCT (ноль)
    push    esi                  ;Идентификатор нашего процесса,
                                 ;который будет получать сообщения от окна
    push    ebx                  ;Идентификатор меню (или окна-потомка)
    push    ebx                  ;Идентификатор окна-предка — 0, ибо предка нет.
    push    ecx                  ;Высота(в ecx идентификатор значения по умолч.)
    push    ecx                  ;Ширина
    push    ecx                  ;Координата у
    push    ecx                  ;Координата х
    push    WS_OVERLAPPEDWINDOW  ;стиль окна
    push    offset window_name   ;Заголовок окна
    push    offset class_name    ;Любой зарегистрированный класс
    push    ebx                  ;Дополнительный стиль
    call    CreateWindowEx       ;Создать окно (в eax идентификатор окна)
    push    eax                  ;Идентификатор окна для UpdateWindow
    push    SW_SHOWNORMAL        ;Тип показа для ShowWindow
    push    eax                  ;Идентификатор окна для ShowWindow
    call    ShowWindow           ;Отобразить окно
    call    UpdateWindow         ;Перерисовать рабочую область окна
    
    mov     edi, offset msg_     ;зарядим регистр адресом сообщения
;Цикл обработки сообщений
message_loop:
    push    ebx                  ;Последнее сообщение
    push    ebx                  ;Первое сообщение
    push    ebx                  ;Идентификатор окна (0-любое наше сообщение)
    push    edi                  ;Адрес структуры для размещения сообщения
    call    GetMessage           ;Ждать получение сообщения из очереди сообщений
    test    eax,eax              ;Если получен 0 (WM_QUIT), 
    jz      exit_msg_loop        ;то выйти.
    push    edi                  ;Иначе снова забить адрес структуры с сообщением
    call    TranslateMessage     ;преобразовать сообщение типа WM_KEYUP в WM_CHAR
    push    edi                  ;Снова забить адрес структуры с сообщением
    call    DispatchMessage      ;и послать его оконной процедуре
    jmp     short message_loop   ;Продолжить цикл

exit_msg_loop:
    push    ebx
    call    ExitProcess          ;Выход из программы
    
;Процедура вызывается Windows, для передачи  сообщения в  окно, 
;после чего процедура снова передает управление Windows.
win_proc proc
    wp_hWnd   equ dword ptr [ebp+08h]  ;Присвоить названия разным глубинам стека, 
    wp_uMsg   equ dword ptr [ebp+0ch]  ;в которых при вызове этой процедуры будут
    wp_wParam equ dword ptr [ebp+10h]  ;лежать переданные ей параметры.
    wp_lParam equ dword ptr [ebp+14h]  ;Четыре параметра этой процедуры идентичны
                                       ;первым четырем параметрам
                                       ;структуры передачи сообщений msg_.

    push    ebp                  ;Сохранить регистр ebp
    mov     ebp,esp              ;Сохранить регистр указателя стека
    pusha                        ;Сохранить все регистры в стеке
    cmp     wp_uMsg, WM_DESTROY  ;Пришло сообщение о закрытии окна?
    jne     not_wm_destroy       ;Если нет, перейти на наш
                                 ;обработчик сообщений
    push    ebx                  ;иначе загрузить код выхода (WM_QUIT)
    call    PostQuitMessage      ;и послать его основной программе, чтобы считать
                                 ;его в следующий раз и выйти из программы.
    jmp     short end_wm_check   ;Выйти из процедуры проверки сообщений
    
not_wm_destroy:
    cmp     wp_uMsg,WM_COMMAND   ;Если мы получили WM_COMMAND,
                                 ;то сообщение от нашего меню.
    jne     not_wm_command       ;иначе перейти на стандартный 
                                 ;обработчик Windows.
    mov     eax,wp_wParam        ;Забираем идентификатор пункта меню из wp_wParam 
                                           ;в eax (0,1,2 или 3)
    jmp     dword ptr menu_handlers[eax*4] ;и осуществляем переход на метку, 
                                           ;вытащенную из адреса, полученного
                                 ;умножением значения идентификатора
                                 ;на размерность следующих данных:
menu_handlers dd    offset menu_about, offset menu_open
              dd    offset menu_save, offset menu_exit

;Выбор текста «О программе»
menu_about:
    mov     eax, offset about_msg ;В eax заносим смещение к тексту
    jmp     short show_msg       ;Переходим на вывод окна с текстом

;Выбор текста «Открыть»
menu_open:
    mov     eax, offset open_msg ;В eax заносим смещение к тексту
    jmp     short show_msg       ;Переходим на вывод окна с текстом    

;Выбор текста «Сохранить»
menu_save:
    mov     eax, offset save_msg ;В eax заносим смещение к тексту
    jmp     short show_msg       ;Переходим на вывод окна с текстом

;Вывод окна с текстом    
show_msg:
    push    MB_OK                ;Зададим стиль окна
    push    offset win_menu      ;Название окна
    push    eax                  ;Текст в окне
    push    wp_hWnd              ;Идентификатор окна-предка
    call    MessageBox           ;Вывод окна на экран
    jmp     short end_wm_check   ;Выйти из процедуры

;Уничтожить окно
menu_exit:
    push    wp_hWnd              ;Идентификатор уничтожаемого окна
    call    DestroyWindow        ;Уничтожить окно

;Выйти из процедуры    
end_wm_check:    
    popa                         ;Восстановить все регистры
    leave                        ;Восстановить esp и ebp
    xor     eax,eax              ;В качестве кодв аозврата вернуть ноль
    ret     16                   ;Очистить стек от параметров и выйти

;Переход на стандартный обработчик сообщений Windows
not_wm_command:
    popa                         ;Восстановить все регистры
    leave                        ;Восстановить esp и ebp
    jmp     DefWindowProc        ;Вызвать стандартный обработчик сообщений
win_proc endp

end _start

[Вернуться в начало]


Файл «Bloknot.rc» - файл ресурсов
1 MENUEX                              ;Организовать меню и присвоить идентификатор
BEGIN                                 ;Идентификатор выделен красным цветом.
  POPUP "&Файл",,,                    ;Организовать раскрывающийся пункт меню
  BEGIN                               ;Начать раскрывающийся пункт меню
    MENUITEM "&Открыть\tCtrl+J",1,,   ;Организовать в нём пункты и присвоить
    MENUITEM "&Сохранить\tCtrl+C",2,, ;им идентификаторы.
    MENUITEM "",,0x00000800,          ;Вставить линию-разделитель
    MENUITEM "&Выход\tCtrl+D",3,,     ;
  END                                 ;Завершить раскрывающийся пункт меню
  MENUITEM "&О программе",0,,         ;
END                                   ;

[Вернуться в начало]


Файл «def32.inc» — содержит описания структур и присвоения константам имен
;Зададим имена константам

IDI_APPLICATION     EQU 32512
WM_DESTROY          EQU 2
CS_HREDRAW          EQU 2
CS_VREDRAW          EQU 1
CW_USERDEFAULT      EQU 80000000H
WS_OVERLAPPEDWINDOW EQU 0CF0000H
IDC_ARROW           EQU 32512
SW_SHOWNORMAL       EQU 1
COLOR_WINDOW        EQU 5
WM_COMMAND          EQU 111h       ;сообщение от меню
MB_OK               EQU 0


;Опишем тип стандартной структуры для описания класса окна

WNDCLASSEX struc
    cbSize          dd ? ;Размер этой структуры в байтах (12 записей по 4 байта)
    style           dd ? ;Стили окна (перерисовывать ли окно при изменении 
                         ;размеров и перемещении и т.п.)
    lpfnWndProc     dd ? ;Смещение к оконной процедуре, которую вызывает Windows, 
                         ;чтобы обратиться к окну
    cbClsExtra      dd ? ;Количество байт, резервируемых после
                         ;структуры класса окна
    cbWndExtra      dd ? ;Количество байт, резервируемых после экземпляра окна
    hInstance       dd ? ;Идентификатор процесса класса окна
    hIcon           dd ? ;Идентификатор класса иконки
    hCursor         dd ? ;Идентификатор класса курсора
    hbrBackground   dd ? ;Цвет фона окна
    lpszMenuName    dd ? ;Имя меню или его идентификатор
    lpszClassName   dd ? ;Имя класса окна
    hIconSm         dd ? ;Идентификатор класса маленькой иконки окна
WNDCLASSEX ends

;Опишем тип структуры, содержащейся в структуре MSG.

POINT struc
    x dd ?    ;Координата x курсора
    y dd ?    ;Координата Y курсора
POINT ends

;Опишем тип стандартной структуры для передачи сообщений 

MSG struc 
    hwnd       dd ?     ;Идентификатор окна, которое получает сообщение;
    message    dd ?     ;Определяет номер сообщения
    wParam     dd ?     ;Дополнительная информация о сообщении
    lParam     dd ?     ;Дополнительная информация о сообщении
    time       dd ?     ;Время послания сообщения
    pt         POINT <> ;Позиция курсора в экранных координатах,
                        ;которая была при отправке сообщения
MSG ends

[Вернуться в начало]


Файл «kernel32.inc» — содержит объявления внешних функций, отвечающих за управление программой
includelib kernel32.lib ;Будем использовать функции из этой библиотеки

;Будем использовать эти функци
extrn __imp__GetModuleHandleA@4:dword
extrn __imp__ExitProcess@4:dword

;Упростим их названия
GetModuleHandle equ __imp__GetModuleHandleA@4
ExitProcess equ __imp__ExitProcess@4

[Вернуться в начало]


Файл «user32.inc» — содержит объявления внешних функций, отвечающих за оконный интерфейс
includelib user32.lib ;Будем использовать функции из этой библиотеки

;Будем использовать эти функци
extrn __imp__DispatchMessageA@4:dword
extrn __imp__TranslateMessage@4:dword
extrn __imp__GetMessageA@16:dword
extrn __imp__LoadIconA@8:dword
extrn __imp__UpdateWindow@4:dword
extrn __imp__ShowWindow@8:dword
extrn __imp__CreateWindowExA@48:dword
extrn __imp__DefWindowProcA@16:dword
extrn __imp__PostQuitMessage@4:dword
extrn __imp__RegisterClassExA@4:dword
extrn __imp__LoadCursorA@8:dword
extrn __imp__LoadMenuA@8:dword
extrn __imp__DestroyWindow@4:dword
extrn __imp__MessageBoxA@16:dword

;Упростим их названия
DispatchMessage  equ __imp__DispatchMessageA@4 
TranslateMessage equ __imp__TranslateMessage@4
GetMessage       equ __imp__GetMessageA@16
LoadIcon         equ __imp__LoadIconA@8
UpdateWindow     equ __imp__UpdateWindow@4
ShowWindow       equ __imp__ShowWindow@8
CreateWindowEx   equ __imp__CreateWindowExA@48
DefWindowProc    equ __imp__DefWindowProcA@16
PostQuitMessage  equ __imp__PostQuitMessage@4
RegisterClassEx  equ __imp__RegisterClassExA@4
LoadCursor       equ __imp__LoadCursorA@8
LoadMenu         equ __imp__LoadMenuA@8
DestroyWindow    equ __imp__DestroyWindow@4
MessageBox       equ __imp__MessageBoxA@16

[Вернуться в начало]
Глава 3 Глава 5 [Оставить отзыв в гостевой]
Hosted by uCoz