Sources.RU Magazine Поиск по журналу
 

TopList

Автор: miksayer

Вот и пришло время для второй статьи цикла "Изучаем Ассемблер". В прошлой статье мы познакомились только с основами ассемблера. Теперь мы создадим окно, поместим на него кнопку и отловим нажатие на нее. Итак, начнем!

Создаем окно

Для начала нам нужно создать пустое окно. Для этого мы будем использовать API-функцию CreateWindowEx. Вот код:

.386
     .model flat,stdcall
     option casemap:none
     ; Подключаем необходимые библиотеки и описания их структур и функций
     include windows.inc
     include user32.inc
     include kernel32.inc
     include gdi32.inc
     include comdlg32.inc
     includelib user32.lib
     includelib kernel32.lib
     includelib gdi32.lib
     includelib comdlg32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD  ; описываем прототип функции
; Макрос, заносящий значения компонент палитры в регистр EAX
RGB macro red,green,blue
         mov     eax,blue shl 16 + green shl 8 + red
endm
; Макрос для вставки текста
szText MACRO Name,Text:VARARG
        .data
    Name     db Text,0
        .code
endm
.const
button1ID    equ 1
.data?
hwndbutton1  HWND ?
hInstance    HINSTANCE ?
CommandLine  LPSTR ?
.data
Textbutton1  db "Button1",0
;_______________
ClassName    db "MASM Builder",0
BtnClName    db "button",0
StatClName   db "static",0
EditClName   db "edit",0
LboxClName   db "listbox",0
CboxClName   db "combobox",0
ReditClName  db "richedit",0
RichEditLib  db "riched32.dll",0
Caption      db "Form",0
;_______________
.code
start:
         ; Получаем описатель нашего модуля
     invoke   GetModuleHandle,NULL
     mov      hInstance,eax
     ; Получаем адрес командной строки
     invoke   GetCommandLine
     ; Вызываем главную процедуру в стиле C++
     invoke   WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
     ; Завершаем процесс
     invoke   ExitProcess,eax
; Главная процедура в стиле C++
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL   wc  :WNDCLASSEX
LOCAL   msg     :MSG
LOCAL   hwnd    :HWND
         ; Заполняем структуру WNDCLASSEX, хранящую информацию о создаваемом классе окон
     mov      wc.cbSize,SIZEOF WNDCLASSEX  ; размер структуры
     mov      wc.style,CS_HREDRAW or CS_VREDRAW  ; стиль окна
     mov      wc.lpfnWndProc,OFFSET WndProc  ; адрес процедуры обработки сообщений
     mov      wc.cbClsExtra,NULL  ; кол-во дополнительных байт за структурой класса (0)
     mov      wc.cbWndExtra,NULL  ; кол-во дополнительных байт за экземпляром окна (0)
     push     hInst
     pop      wc.hInstance  ; описатель экземпляра процесса с процедурой обработки сообщений
     RGB      235,233,216  ; EAX = код серо-бежевого цвета
     invoke   CreateSolidBrush,eax  ; создаём кисть заполнения однородным цветом EAX
     mov      wc.hbrBackground,eax  ; кисть для заполнения фона (серо-
бежевым цветом)
     mov      wc.lpszClassName,OFFSET ClassName  ; имя класса
     invoke   LoadIcon,NULL,IDI_APPLICATION  ; загружаем стандартную иконку приложения
     mov      wc.hIcon,eax  ; большая иконка приложения
     mov      wc.hIconSm,eax  ; маленькая иконка приложения
     invoke   LoadCursor,NULL,IDC_ARROW  ; загружаем стандартный курсор
     mov      wc.hCursor,eax  ; курсор мыши в области окна
     mov      wc.lpszMenuName,NULL  ; имя или идентификатор меню (0)
     ; Регистрируем класс и создаём окно
     invoke   RegisterClassEx,addr wc
     invoke   CreateWindowEx,0,ADDR ClassName,ADDR Caption,WS_SYSMENU or WS_SIZEBOX,389,82,327,200,0,0,hInst,0
     ; Показываем окно
     mov      hwnd,eax
     INVOKE   ShowWindow,hwnd,SW_SHOWNORMAL
     INVOKE   UpdateWindow,hwnd
     ; Цикл обработки сообщений (стандартный)
     .WHILE TRUE
         INVOKE   GetMessage,ADDR msg,0,0,0  ; ожидаем и получаем сообщение
         .BREAK .IF (!eax)                   ; выходим из цикла, если получаем WM_QUIT (выход из приложения)
         INVOKE   TranslateMessage,ADDR msg  ; преобразуем символьные сообщения
         INVOKE   DispatchMessage,ADDR msg   ; обрабатываем сообщение
     .ENDW
     mov      eax,msg.wParam
     ret
WinMain endp
; Процедура обработки сообщений
WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
     .IF uMsg == WM_DESTROY  ; сообщение об уничтожении окна (передаётся во время закрытия окна)
         invoke   PostQuitMessage,NULL  ; отправляем в очередь сообщение WM_QUIT
     .ELSEIF uMsg == WM_CREATE  ; сообщение о создании окна (передаётся после создания окна)
             ; создаём кнопку с идентификатором = button1ID (кнопка - это тоже окно)
         invoke  CreateWindowEx,0,ADDR BtnClName,ADDR Textbutton1,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,114,71,75,25,hWnd,button1ID,hInstance,0

         mov      hwndbutton1,eax  ; сохраняем описатель кнопки
     .ELSEIF uMsg == WM_COMMAND  ; сообщение о команде (например, нажатии на кнопку)
         mov     eax,wParam
         .IF lParam != 0  ; описатель контрола (равен нулю, если это НЕ контрол формы)
            .IF ax == button1ID  ; младшее слово wParam определяет идентификатор контрола
                 shr eax,16
                 .IF ax == BN_CLICKED  ; старшее слово wParam определяет код команды
                         ; Выводим на экран сообщение
                     invoke   MessageBox,hWnd,addr Textbutton1,NULL,MB_ICONINFORMATION
                 .ENDIF
             .ENDIF
         .ENDIF;
     .ELSE  ; другое сообщение
             ; Вызываем стандартный обработчик сообщения
         invoke   DefWindowProc,hWnd,uMsg,wParam,lParam
         ret
     .ENDIF
     xor     eax,eax  ; сообщение обработано
     ret
WndProc endp
end start                              ; Конец программы с указанием точки в

В коде вы видите несколько новых конструкций .WHILE ... .ENDW и .IF ... .ELSE ... .ENDIF. Если вы раньше изучали какой-либо язык программирования, то понять смысл этих конструкций вам не составит большого труда, иначе давайте разберем, например, такую конструкцию из нашего кода:

	 .IF uMsg == WM_DESTROY
		 invoke   PostQuitMessage,NULL
	 .ELSEIF uMsg == WM_CREATE
	 .ELSE
		 invoke   DefWindowProc,hWnd,uMsg,wParam,lParam
		 ret
	 .ENDIF

Этот код означает, что если нашему окну послано сообщение WM_DESTROY(.IF), то мы выполняем API-функцию PostQuitMessage, если сообщение WM_CREATE(.ELSEIF), то ничего не делаем, а во всех остальных случаях(.ELSE) вызываем функцию DefWindowProc. Теперь давайте вернемся к нашей кнопке. Создавать мы ее будем с помощью такого кода:

invoke CreateWindowEx,0,ADDR BtnClName,ADDR Textbutton1,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,114,71,75,25,hWnd,button1ID,hInstance,0
mov 	  hwndbutton1,EAX

Теперь в переменной hwndbutton1 хранится хэндл нашей кнопки. Не забудьте объявить переменную hwndbutton1 в секции .data? так:

hwndbutton1	 HWND ?

и button1ID в .const так:

button1ID	 equ 1

Теперь нам нужно добавить такой код вместо нашей конструкции .IF ... .ELSE ... .ENDIF:

	 .IF uMsg == WM_DESTROY
		 invoke   PostQuitMessage,NULL
	 .ELSEIF uMsg == WM_CREATE
invoke CreateWindowEx,0,ADDR BtnClName,ADDR Textbutton1,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,114,71,75,25,hWnd,button1ID,hInstance,0
		 mov 	  hwndbutton1,EAX
	 .ELSEIF uMsg == WM_COMMAND
		 mov 	 eax,wParam
		 .IF lParam != 0
			 .IF ax == button1ID
				 shr eax,16
				 .IF ax == BN_CLICKED
					 invoke 	MessageBox,hWnd,addr Textbutton1,0,MB_ICONINFORMATION
				 .ENDIF
			 .ENDIF
		 .ENDIF;
	 .ELSE
		 invoke   DefWindowProc,hWnd,uMsg,wParam,lParam
		 ret
	 .ENDIF

Здесь мы отлавливаем событие WM_COMMAND и проверяем, не было ли оно передано нашей кнопке, далее проверяем, какое это событие (нам нужно BN_CLICKED), и, если это оно, выкидываем сообщение с текстом из переменной Textbutton1, которую объявляем в секции .data:

Textbutton1	 db "Button1",0

Все! Наша программа готова. Если что-то непонятно, то я привожу полный код нашей программы:

.386
	 .model flat,stdcall
	 option casemap:none
	 include \masm32\include\windows.inc
	 include \masm32\include\user32.inc
	 include \masm32\include\kernel32.inc
	 include \masm32\include\gdi32.inc
	 include \masm32\include\comdlg32.inc
	 includelib \masm32\lib\user32.lib
	 includelib \masm32\lib\kernel32.lib
	 includelib \masm32\lib\gdi32.lib
	 includelib \masm32\lib\comdlg32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
RGB macro red,green,blue
	 xor 	 eax,eax
	 mov 	 ah,blue
	 shl 	 eax,8
	 mov 	 ah,green
	 mov 	 al,red
endm
szText MACRO Name,Text:VARARG
	LOCAL	 lbl
	jmp	 lbl
	Name	 db Text,0
	lbl:
ENDM
.const
button1ID	 equ 1
.data?
hwndbutton1	 HWND ?
hInstance	 HINSTANCE ?
CommandLine	 LPSTR ?
.data
Textbutton1	 db "Button1",0
;_______________
ClassName	 db "MASM Builder",0
BtnClName	 db "button",0
StatClName	 db "static",0
EditClName	 db "edit",0
LboxClName	 db "listbox",0
CboxClName	 db "combobox",0
ReditClName	 db "richedit",0
RichEditLib	 db "riched32.dll",0
Caption		 db "Form",0
;_______________
.code
start:
	 invoke   GetModuleHandle,NULL
	 mov      hInstance,eax
	 invoke   GetCommandLine
	 invoke   WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
	 invoke   ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL 	wc 	:WNDCLASSEX
LOCAL 	msg 	:MSG
LOCAL 	hwnd	:HWND
	 mov      wc.cbSize,SIZEOF WNDCLASSEX
	 mov      wc.style,CS_HREDRAW or CS_VREDRAW
	 mov      wc.lpfnWndProc,OFFSET WndProc
	 mov      wc.cbClsExtra,NULL
	 mov      wc.cbWndExtra,NULL
	 push     hInst
	 pop      wc.hInstance
	 RGB      235,233,216
	 invoke   CreateSolidBrush,eax
	 mov      wc.hbrBackground,eax
	 mov      wc.lpszClassName,OFFSET ClassName
	 invoke   LoadIcon,NULL,IDI_APPLICATION
	 mov      wc.hIcon,eax
	 mov      wc.hIconSm,eax
	 invoke   LoadCursor,NULL,IDC_ARROW
	 mov      wc.hCursor,eax
	 invoke   RegisterClassEx,addr wc
invoke CreateWindowEx,0,ADDR ClassName,ADDR Caption,WS_SYSMENU or WS_SIZEBOX,389,82,327,200,0,0,hInst,0
	 mov      hwnd,eax
	 INVOKE   ShowWindow,hwnd,SW_SHOWNORMAL
	 INVOKE   UpdateWindow,hwnd
	 .WHILE TRUE
		 INVOKE   GetMessage,ADDR msg,0,0,0
		 .BREAK .IF (!eax)
		 INVOKE   TranslateMessage,ADDR msg
		 INVOKE   DispatchMessage,ADDR msg
	 .ENDW
	 mov      eax,msg.wParam
	 ret
WinMain endp
WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
	 .IF uMsg == WM_DESTROY
		 invoke   PostQuitMessage,NULL
	 .ELSEIF uMsg == WM_CREATE
invoke CreateWindowEx,0,ADDR BtnClName,ADDR Textbutton1,WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,114,71,75,25,hWnd,button1ID,hInstance,0
		 mov 	  hwndbutton1,EAX
	 .ELSEIF uMsg == WM_COMMAND
		 mov 	 eax,wParam
		 .IF lParam != 0
			 .IF ax == button1ID
				 shr eax,16
				 .IF ax == BN_CLICKED
					 invoke 	MessageBox,hWnd,addr Textbutton1,0,MB_ICONINFORMATION
				 .ENDIF
			 .ENDIF
		 .ENDIF;
	 .ELSE
		 invoke   DefWindowProc,hWnd,uMsg,wParam,lParam
		 ret
	 .ENDIF
	 xor      eax,eax
	 ret
WndProc endp
end start

Заключение

Хочу дать вам небольшое "домашнее задание". Добавьте еще одну кнопку и по нажатию на нее эмулируйте нажатие на другую кнопку (это делается с помощью API-функции SendMessage).

Надеюсь, вы все поняли из этой статьи, если нет, то пишите письма на miksayer@mail.ru. Удачи!

С уважением, Miksayer!



 Design by Шишкин Алексей (Лёха)  ©2004-2008 by sources.ru