III
Второй пример касается использования окна редактирования. Результат работы программы показан на Рис. 1.3.3, а сама программа — на Рис. 1.3.2. При нажатии кнопки "Выход" в начале появляется окно-сообщение с отредактированной строкой.
Обратите внимание на то, как осуществляется посылка сообщения окну
(управляющему элементу). Для этого используют в основном две функции:
SendMessage
и PostMessage
. Отличие их друг от друга
заключается в том, что первая вызывает процедуру окна с соответствующими
параметрами и ждет, когда та возвратит управление; вторая функция ставит
сообщение в очередь и сразу возвращает управление.
; файл edit.inc ; константы WM_SETFOCUS equ 7h ; сообщение приходит при закрытии окна WM_DESTROY equ 2 ; сообщение приходит при содании окна WM_CREATE equ 1 ; сообщение, если что-то происходит с элементами на окне WM_COMMAND equ 111h ; сообщение, позволяющее послать элементу строку WM_SETTEXT equ 0Ch ; сообщение, позволяющее получить строку WM_GETTEXT equ 0Dh ; свойства окна CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_TABSTOP equ 10000h WS_SYSMENU equ 50000h WS_OVERLAPPEDWINDOW equ 0+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 STYLBTN equ WS_CHILD+BS_DEFPUSHBUTTON+WS_VISIBLE+WS_TABSTOP STYLEDT equ WS_CHILD+WS_VISIBLE+WS_BORDER+WS_TABSTOP ; идентификатор стандартной иконки 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 ; файл edit.asm .386P ; плоская модель .MODEL FLAT, stdcall include edit.inc ; директивы копоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\iib\kernel32.lib ;------------------------------------------------------------ ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> HINST DD 0 ; дескриптор приложения TITLENAME DB 'Пример — окно редактирования',0 CLASSNAME DB 'CLASS32',0 CPBUT DB 'Выход',0 ; выход CPEDT DB ' ',0 CLSBUTN DB 'BUTTON',0 CLSEDIT DB 'EDIT',0 HWNDBTN DWORD 0 HWNDEDT DWORD 0 CAP DB 'Сообщение',0 MES DB 'Конец работы программы',0 TEXT DB 'Строка редактирования',0 DB 50 DUP(0) ; продолжение буфера _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 150 ; DY - высота окна PUSH 400 ; 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] CALLShowWindow@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 ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; 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 JNE NODESTROY ; получить отредактированную строку PUSH OFFSET TEXT PUSH 150 PUSH WM_GETTEXT PUSH HWNDEDT CALL SendMessageA@16 ; показать эту строку PUSH 0 PUSH OFFSET CAP PUSH OFFSET TEXT PUSH DWORD PTR [EBP+08H] ; дескриптор окна CALL MessageBoxA@16 ; на выход JMP WMDESTROY NODESTROY: 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 ; запомнить дескриптор кнопки ; создать окно редактирования PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ; DY PUSH 350 ; DX PUSH 50 ; Y PUSH 10 ; X PUSH STYLEDT PUSH OFFSET CPEDT ; имя окна PUSH OFFSET CLSEDIT ; имя класса PUSH 0 CALL CreateWindowExA@48 MOV HWNDEDT,EAX ;--------- поместить строку в окно редактирования PUSH OFFSET TEXT PUSH 0 PUSH WM_SETTEXT PUSH HWNDEDT CALL SendMessageA@16 ;--------- установить фокус на окне редактирования PUSH HWNDEDT CALL SetFocus@4 ;------------------------------------------------------------ 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
Рис. 1.3.2. Пример окна со строкой редактирования.
Рис. 1.3.3. Работа программы со строкой редактирования (Программа на Рис. 1.3.2).
IV
Наконец последний в этой главе пример — окно-список. При создании окна в список помещаются названия цветов. Если произвести двойной щелчок по цвету, то появится окно-сообщение с названием этого цвета.
Двойной щелчок по элементу списка определяется по следующей схеме:
отслеживается событие, происходящее со списком, а далее по старшему слову
параметра WPARAM
определяется, какое событие имело место (параметр
[EBP+10Н]
, а его старшая часть [EBP+12Н]
).
Не имея возможности подробно останавливаться на различных свойствах управляющих элементов, укажу основополагающую идею такого элемента, как список. Каждый элемент списка имеет ряд атрибутов, по которым он может быть найден в списке: порядковый номер в списке, название элемента, уникальный номер. Последняя характеристика наиболее важна, так как позволяет однозначно идентифицировать элемент.
; файл list.inc ; константы 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 ; файл list.asm .386P ; плоская модель .MODEL FLAT, stdcall include list.inc ; директивы копоновщику для подключения библиотек 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 CLSBUTN DB 'BUTTON',0 CLSLIST DB 'LISTBOX',0 HWNDBTN DWORD 0 HWNDLST DWORD 0 CAP DB 'Сообщение',0 CAP1 DB 'Выбран',0 MES DB 'Конец работы Программы',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 BUF DB 30 dup(0) _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH О 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 ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; 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 WORD 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
Puc. 1.3.4. Пример окна с простым списком.
В конце главы я бы хотел поговорить вот на какую тему. С окном на экране могут происходить самые интересные и необычные вещи.
Например, оно по желанию пользователя может перемещаться, менять свой размер, сворачиваться и разворачиваться, наконец, другие окна могут заслонять данное окно.
В таких ситуациях система посылает окну сообщение WM_PAINT
.
Такое же сообщение посылается окну при выполнении некоторых функций, связанных с
перерисовкой окна, таких, например, как UpdateWindow
.
Если сообщение WM_PAINT
не обрабатывается процедурой окна, а
возвращается системе посредством функции DefWindowProc
, то система
берет на себя не только перерисовку окна (что она и так делает), но и
перерисовку содержимого окна. К содержимому окна, однако, относятся только
дочерние окна, которыми являются кнопки, списки, окна редактирования и другие
элементы управления. В следующих главах мы, в частности, будем говорить о том,
как выводить в окно текстовую и графическую информацию.
Здесь, чтобы информация сохранялась в окне, нам не обойтись без обработки
сообщения WM_PAINT
.
В заключение заметим, что представленные в данной главе программы записаны для трансляции в MASM32.
Для того чтобы использовать их с TASM32, как и ранее, достаточно удалить из
всех API-имен суффикс @N
и подключить вместо
USER32.LIB
и KERNEL32.LIB
библиотеку
IMPORT32.LIB
.