На этом шаге мы рассмотрим пример создания окна со списком.
Пример этого шага - окно-список. При создании окна в список помещаются названия цветов. Если произвести двойной щелчок по цвету, то появится окно-сообщение с названием этого цвета.
Рис.1. Внешний вид приложения
Двойной щелчок по элементу списка определяется по следующей схеме: отслеживается событие, происходящее со списком, а далее по старшему слову параметра WPARAM определяется, какое событие имело место (параметр [ЕВР+10H], а его старшая часть [ЕВР+12Н]).
Заметим, что каждый элемент списка имеет ряд атрибутов, по которым он может быть найден в списке:
Последняя характеристика наиболее важна, так как позволяет однозначно идентифицировать элемент.
;Константы. WM_SETFOCUS equ 7h ;Сообщение приходит при закрытии окна. WM_DESTROY equ 2 ;Сообщение приходит при создании окна. WM_CREATE equ 1 ;Сообщение, если что-то происходит с элементами на окне. WM_COMMAND equ 111h ;Сообщение, позволяющее послать элементу строку. WM_SETTEXT equ 0Ch ;Сообщение, позволяющее получить строку. WM_GETTEXT equ 0Dh ;Сообщение - команда добавить строку. LB_ADDSTRING equ 180h LB_GETTEXT equ 189h LB_GETCURSEL equ 188h LBN_DBLCLK equ 2 ;Свойства окна. CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_TABSTOP equ 10000h WS_SYSMENU equ 80000h WS_THICKFRAME equ 40000h WS_OVERLAPPEDWINDOW equ WS_TABSTOP+WS_SYSMENU STYLE equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS CS_HREDRAW equ 2h BS_DEFPUSHBUTTON equ 1h WS_VISIBLE equ 10000000h WS_CHILD equ 40000000h WS_BORDER equ 800000h WS_VSCROLL equ 200000h LBS_NOTIFY equ 1h STYLBTN equ WS_CHILD+BS_DEFPUSHBUTTON+WS_VISIBLE+WS_TABSTOP STYLLST equ WS_THICKFRAME+WS_CHILD+WS_VISIBLE+WS_BORDER + WS_TABSTOP+ WS_VSCROLL+LBS_NOTIFY ;Идентификатор стандартной пиктограммы. IDI_APPLICATION equ 32512 ;Идентификатор курсора. IDC_ARROW equ 32512 ;Режим показа окна - нормальный. SW_SHOWNORMAL equ 1 ;Прототипы внешних процедур. EXTERN SetFocus@4:NEAR EXTERN SendMessageA@16:NEAR EXTERN MessageBoxA@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR ;Структуры ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS ;----------------------- WNDCLASS STRUC CLSSTYLE DD ? ;Стиль окна. CLWNDPROC DD ? ;Указатель на процедуру окна. CLSCBCLSEX DD ? ;Информация о доп. байтах для данной структуры. CLSCBWNDEX DD ? ;Информация о доп. байтах для окна. CLSHINST DD ? ;Дескриптор приложения. CLSHICON DD ? ;Идентификатор иконки окна. CLSHCURSOR DD ? ;Идентификатор курсора окна. CLBKGROUND DD ? ;Идентификатор кисти окна. CLMENNAME DD ? ;Имя-идентификатор меню. CLNAME DD ? ;Специфицирует имя класса окон. WNDCLASS ENDS
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr18_1.asm ;Директивы компоновщику для подключения библиотек. includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> HINST DD 0 ;Здесь хранится дескриптор приложения. TITLENAME DB 'Пример - окно LISTBOX',0 CLASSNAME DB 'CLASS32',0 CPBUT DB 'Выход',0 ;Выход. CPLST DB ' ',0 CLSLIST DB 'LISTBOX',0 CLSBUTN DB 'BUTTON',0 HWNDBTN DWORD 0 HWNDLST DWORD 0 CAP DB 'Сообщение', 0 CAP1 DB 'Выбран', 0 MES DB 'Конец работы программы',0 BUF DB 30 DUP(0) ;Массив строк. STR1 DB 'Красный',0 STR2 DB 'Зеленый',0 STR3 DB 'Синий',0 STR4 DB 'Желтый',0 STR5 DB 'Черный',0 STR6 DB 'Белый',0 ;Указатели на строки. PS DWORD OFFSET STR1 DWORD OFFSET STR2 DWORD OFFSET STR3 DWORD OFFSET STR4 DWORD OFFSET STR5 DWORD OFFSET STR6 _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ;Заполнить структуру окна. ;Стиль. MOV [WC.CLSSTYLE],STYLE ;Процедура обработки сообщений. MOV [WC.CLWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEX], 0 MOV [WC.CLSCBWNDEX], 0 MOV EAX, [HINST] MOV [WC.CLSHINST], EAX ;------------ пиктограмма окна PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;------------ курсор окна PUSH IDC_ARROW PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;------------ MOV [WC.CLBKGROUND], 17 ;Цвет окна. MOV DWORD PTR [WC.CLMENNAME],0 MOV DWORD PTR [WC.CLNAME], OFFSET CLASSNAME PUSH OFFSET WC CALL RegisterClassA@4 ;Создать окно зарегистрированного класса. PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH 200 ; DY - высота окна. PUSH 250 ; DX - ширина окна. PUSH 100 ; Y - координата левого верхнего угла. PUSH 100 ; X - координата левого верхнего угла. PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ;Имя окна. PUSH OFFSET CLASSNAME ;Имя класса. PUSH 0 CALL CreateWindowExA@48 ;Проверка на ошибку. CMP EAX,0 JZ _ERR MOV [NEWHWND], EAX ;Дескриптор окна. ;------------------------------------ PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ;Показать созданное окно. ;------------------------------------ PUSH [NEWHWND] CALL UpdateWindow@4 ;Команда перерисовать видимую ;часть окна, сообщение WM_PAINT. ;Цикл обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP EAX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ;Выход из программы (закрыть процесс). PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;----------------------------------------- ;Процедура окна. ;Расположение параметров в стеке: ;[ЕВР+14Н] LPARAM ;[ЕВР+10Н] WPARAM ;[ЕВР+0СН] MES ;[ЕВР+08H] HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH], WM_COMMAND JE WMCOMMND JMP DEFWNDPROC WMCOMMND: MOV EAX,HWNDBTN CMP DWORD PTR [EBP+14H], EAX ;Кнопка? ;На выход? JE WMDESTROY MOV EAX, HWNDLST CMP DWORD PTR [EBP+14H], EAX ;Список? JNE NOLIST ;Работаем со списком. CMP BYTE PTR [EBP+12H], LBN_DBLCLK JNE NOLIST ;Двойной щелчок есть, определяем выбранную строку. ;Сначала индекс. PUSH 0 PUSH 0 PUSH LB_GETCURSEL PUSH HWNDLST CALL SendMessageA@16 ;Теперь сам текст. PUSH OFFSET BUF PUSH EAX PUSH LB_GETTEXT PUSH HWNDLST CALL SendMessageA@16 ;Сообщить, что выбрано. PUSH 0 PUSH OFFSET CAP1 PUSH OFFSET BUF PUSH DWORD PTR [EBP+08H] CALL MessageBoxA@16 NOLIST: MOV EAX, 0 JMP FINISH WMCREATE: ;Создать окно-кнопку. PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ;DY PUSH 60 ;DX PUSH 10 ;Y PUSH 10 ;X PUSH STYLBTN PUSH OFFSET CPBUT ;Имя окна. PUSH OFFSET CLSBUTN ;Имя класса. PUSH 0 CALL CreateWindowExA@48 MOV HWNDBTN,EAX ;Запомнить дескриптор кнопки. ;Создать окно LISTBOX. PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 90 ;DY PUSH 150 ;DX PUSH 50 ;Y PUSH 10 ;X PUSH STYLLST PUSH OFFSET CPLST ;Имя окна. PUSH OFFSET CLSLIST ;Имя класса. PUSH 0 CALL CreateWindowExA@48 MOV HWNDLST,EAX ;Заполнить список. PUSH PS PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 PUSH PS+4 PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 PUSH PS+8 PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 PUSH PS+12 PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 PUSH PS+16 PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 PUSH PS+20 PUSH 0 PUSH LB_ADDSTRING PUSH HWNDLST CALL SendMessageA@16 ;------------------------------------ MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: PUSH 0 ;MB_OK PUSH OFFSET CAP PUSH OFFSET MES PUSH DWORD PTR [EBP+08H] ;Дескриптор окна. CALL MessageBoxA@16 PUSH 0 CALL PostQuitMessage@4 ;Сообщение WM_QUIT. MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
В заключение отметим, что с окном на экране могут происходить самые интересные и необычные вещи. Например, оно по желанию пользователя может перемещаться, менять свой размер, сворачиваться и разворачиваться, наконец, другие окна могут заслонять данное окно. В таких ситуациях система посылает окну сообщение WM_PAINT. Такое же сообщение посылается окну при выполнении некоторых функций, связанных с перерисовкой окна, таких, например, как UpdateWindow. Если сообщение WM_PAINT не обрабатывается процедурой окна, а возвращается системе посредством функции DefWindowProc, то система берет на себя не только перерисовку окна (что она и так делает), но и перерисовку содержимого окна. К содержимому окна, однако, относятся только дочерние окна, которыми являются кнопки, списки, окна редактирования и другие элементы управления. В следующих шагах мы будем говорить о том, как выводить в окно текстовую и графическую информацию. Здесь, чтобы информация сохранялась в окне, нам не обойтись без обработки сообщения WM_PAINT.
Заметим, что представленные в шагах 16-18 программы записаны для трансляции в MASM32. Для того чтобы использовать их с TASM32, как и ранее, достаточно удалить из всех API-имен суффикс @N и подключить вместо USER32.LIB и KERNEL32.LIB библиотеку IMPORT32.LIB.
На следующем шаге мы продолжим приводить примеры программ на Ассемблере.