![]() | |
![]() |
Off-line версия журнала "Sources.RU Magazine". Выпуск "Август 2005" |
· АСМ для новичков · Языковой барьер · Простые растровые операции · BWT-кодинг · Hello World · Функции DLL · Охота за шпионом (АнтиКейлоггер) · Практика создания защиты · Создание поверхностей |
АСМ для новичковАвтор: 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! |
![]() | |
Журнал "Исходники.RU". Copyright (c) 2004 by Исходники.RU. Designed by Mastilior. |