Шаг 28.
Событие WINDOW_BUFFER_SIZE_EVENT (изменение размеров окна)

    На этом шаге мы дадим краткую характеристику события WINDOW_BUFFER_SIZE_EVENT и рассмотрим программу, использующую обработку событий от мыши и клавиатуры.

    В структуре события WINDOW_BUFFER_SIZE_EVENT по смещению +4 находится двойное слово, содержащее новый размер консольного окна. Младшее слово - это размер по X, старшее слово - размер по Y. Да, когда речь идет о консольном окне, все размеры и координаты даются в "символьных" единицах.

    Что касается последних событий KEY_EVENT и MOUSE_EVENT, то там также значимым является двойное слово по смещению +4. В следующем листинге дана простая программа обработки консольных событий. При перемещении мыши в консольном окне выводятся ее координаты. По гажатию клавиши ESC осуществляется выход из приложения.


    Пример обработки событий от мыши и клавиатуры для консольного приложения.
.386P
;Плоская модель памяти.
.MODEL FLAT, STDCALL
;Константы.
STD_OUTPUT_HANDLE   equ -11
STD_INPUT_HANDLE    equ -10
;Тип события.
KEY_EV              equ 1h 
MOUSE_EV            equ 2h
;Прототипы внешних процедур.
EXTERN wsprintfA:NEAR 
EXTERN GetStdHandle@4:NEAR
EXTERN WriteConsoleA@20:NEAR
EXTERN SetConsoleCursorPosition@8:NEAR
EXTERN SetConsoleTitleA@4:NEAR
EXTERN FreeConsole@0:NEAR
EXTERN AllocConsole@0:NEAR
EXTERN CharToOemA@8:NEAR
EXTERN SetConsoleTextAttribute@8:NEAR
EXTERN ReadConsoleInputA@16:NEAR 
EXTERN ExitProcess@4:NEAR
;Директивы компоновщику для подключения библиотек.
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
;------------------------------------------------
COOR STRUC 
     X WORD ? 
     Y WORD ? 
COOR ENDS 
;Сегмент данных.
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
        HANDL  DWORD  ?
        HANDL1 DWORD  ?
        TITL DB "Обработка событий мыши",0
        BUF  DB 200 dup(0)
        LENS DWORD ?  ;Количество выведенных символов.
        CO   DWORD  ?
        FORM DB "Координаты: %u  %u  ",0
        CRD  COOR <?> 
        STR1 DB "Для выхода нажмите ESC",0
        MOUS_KEY WORD 19 dup(?)
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE' 
START: 
;Образовать консоль.
;Вначале освободить уже существующую.
        CALL FreeConsole@0
        CALL AllocConsole@0 
;Получить HANDL1 ввода.
        PUSH STD_INPUT_HANDLE
        CALL GetStdHandle@4
        MOV  HANDL1,EAX 
;Получить HANDL вывода.
        PUSH STD_OUTPUT_HANDLE
        CALL GetStdHandle@4
        MOV  HANDL,EAX 
;Задать заголовок окна консоли.
        PUSH OFFSET TITL 
        PUSH OFFSET TITL 
        CALL CharToOemA@8 
        PUSH OFFSET TITL
        CALL SetConsoleTitleA@4
;*************************************
;Перекодировка строки.
        PUSH OFFSET STR1
        PUSH OFFSET STR1
        CALL CharToOemA@8 
;Длина строки.
        PUSH OFFSET STR1
        CALL LENSTR 
;Вывести строку.
        PUSH 0
        PUSH OFFSET LENS
        PUSH EBX
        PUSH OFFSET STR1
        PUSH HANDL
        CALL WriteConsoleA@20 
;Цикл ожиданий: движение мыши или нажатие ESC.
LOO:
;Координаты курсора. 
        MOV  CRD.X,0 
        MOV  CRD.Y,10 
        PUSH CRD 
        PUSH HANDL
        CALL SetConsoleCursorPosition@8 
;Прочитать одну запись о событии.
        PUSH OFFSET CO ;Количество реально почитанных записей.
        PUSH 1         ;Количество получаемых записей.
        PUSH OFFSET MOUS_KEY ;Буфер для размещения записей.
        PUSH HANDL1          ;Дексриптор входного буфера консоли.
        CALL ReadConsoleInputA@16 
;Проверим, не с мышью ли что?
        CMP  WORD PTR MOUS_KEY,MOUSE_EV 
        JNE  LOO1 
;Здесь преобразуем координаты мыши в строку.
        MOV  AX,WORD PTR MOUS_KEY+6   ;У - мышь. 
;Копирование с обнулением старших битов. 
        MOVZX EAX,AX 
        PUSH EAX
        MOV  AX,WORD PTR MOUS_KEY+4   ;Х - мышь. 
;Копирование с обнулением старших битов. 
        MOVZX EAX,AX 
        PUSH EAX
        PUSH OFFSET FORM 
        PUSH OFFSET BUF 
        CALL wsprintfA 
;Восстановить стек.
        ADD  ESP,16
;Перекодировать строку для вывода. 
        PUSH OFFSET BUF 
        PUSH OFFSET BUF 
        CALL CharToOemA@8 
;Длина строки.
        PUSH OFFSET BUF 
        CALL LENSTR
;Вывести на экран координаты курсора. 
        PUSH 0
        PUSH OFFSET LENS
        PUSH EBX
        PUSH OFFSET BUF
        PUSH HANDL
        CALL WriteConsoleA@20
        JMP  LOO ;К началу цикла. 
LOO1: 
;Нет ли события от клавиатуры?
        CMP  WORD PTR MOUS_KEY,KEY_EV
        JNE  LOO ;Есть, какое?
        CMP  BYTE PTR MOUS_KEY+14,27
        JNE  LOO
;*************************************
;Закрыть консоль.
        CALL FreeConsole@0
        PUSH 0
        CALL ExitProcess@4
        RET

;Процедура определения длины строки.
;Строка -   [EBP+08H].
;Длина в ЕВХ 
LENSTR PROC
      ENTER 0,0
      PUSH  EAX
;----------------------
      CLD
      MOV   EDI,DWORD PTR  [EBP+08H]
      MOV   EBX,EDI
      MOV   ECX,100 ;Ограничить длину строки.
      XOR   AL,AL
      REPNE SCASB   ;Найти символ 0.
      SUB   EDI,EBX ;Длина строки, включая 0.
      MOV   EBX,EDI
      DEC   EBX
;----------------------
      POP   EAX 
      LEAVE 
      RET   4
LENSTR ENDP
_TEXT  ENDS
       END START
Текст этой программы можно взять здесь.

    Результат работы приложения изображен на рисунке 1:


Рис.1. Результат работы приложения

    После того как вы познакомились с приведенной программой, давайте ее подробнее обсудим.

    Начнем с функции wsprintfA. Она имеет переменное число параметров. Первые два параметра обязательны. Вначале идет указатель на буфер, куда будет скопирована результирующая строка. Вторым идет указатель на форматную строку. Форматная строка может содержать текст, а также собственно формат выводимых параметров. Поля, содержащие информацию о параметре, начинаются с символа %. Формат этих полей в точности соответствует формату полей, используемых в стандартных С-функциях printf, sprintf и др. Исключением является отсутствие в формате для функции wsprintfA вещественных чисел. Нет нужды рассматривать этот формат подробно, заметим только, что каждое поле в форматной строке соответствует параметру (начиная с третьего). В нашем случае форматная строка была равна: "Координаты: %u %u ". Это означало, что далее в стек будет отправлено два числовых параметра типа WORD. Конечно, в стек мы отправили два двойных слова, позаботившись лишь о том, чтобы старшие слова были обнулены. Для такой операции очень удобна команда микропроцессора MOVZX, которая копирует второй операнд в первый так, чтобы биты старшего слова были заполнены нулями. Если бы параметры были двойными словами, то вместо поля %u мы бы поставили %lu. В случае, если поле форматной строки определяет строку-параметр, например "%s", в стек следует отправлять указатель на строку (что естественно).

    Поскольку функция "не знает", сколько параметров может быть в нее отправлено, разработчики не стали усложнять текст этой функции и оставили нам задачу освобождения стека. Стек освобождается командой ADD ESP,N. Здесь N - количество удаляемых байтов.

    Обратимся теперь к функции ReadConsoleInputA. К уже сказанному о ней добавлю только, что если буфер событий пуст, то функция будет ждать, пока "что-то" не случится с консольным окном, и только тогда возвратит управление. Кроме того, мы можем указать, чтобы функция возвращала не одну, а несколько записей о происшедших с консолью событиях. В этом случае в буфер будет помещена не одна, а несколько информационных записей. Но мы на этом останавливаться не будем.

    Отметим, как откомпилировать данную программу в TASM32. Как обычно, удаляем все значки @N, указываем библиотеку import32.lib и, наконец, wsprintfA меняем на _wsprintfA.

    На следующем шаге мы рассмотрим использование таймера в консольном приложении.




Предыдущий шаг Содержание Следующий шаг