Шаг 24.
Создание консоли

    На этом шаге мы рассмотрим несколько простых консольных функций и их применение.

    Работать с чужой консолью не всегда удобно. А для того чтобы создать а консоль, используется функция AllocConsole. По завершении программы выделенные консоли автоматически освобождаются. Однако это можно сделать и принудительно, используя функцию FreeConsole. Для того чтобы получить дескриптор консоли, используется уже знакомая функция GetStdHandle, аргументом которой может являться следующая из трех констант:

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

    Для чтения из буфера консоли используется функция ReadConsole. Значения параметров этой функции (слева направо) следующие:

    Установить позицию курсора в консоли можно при помощи функции SetConsoleCursorPosition со следующими параметрами:

    Отметим, что вторым параметром является не указатель на структуру (что обычно бывает), а именно структура. На самом деле для ассемблера это просто двойное слово (DWORD), у которого младшее слово - координата X, а старшее слово - координата Y.

    Установить цвет выводимых букв можно с помощью функции SetConsoleTextAttribute. Первым параметром этой функции является дескриптор выходного буфера консоли, а вторым - цвет букв и фона. Цвет получается путем комбинации (сумма или операция "ИЛИ") двух или более из представленных ниже констант. Причем возможна "смесь" не только цвета и интенсивности, но и цветов (смотри программу ниже).

FOREGROUND_BLUE          equ    1h   ;Cиний цвет букв.
FOREGROUND_GREEN         equ    2h   ;Зеленый цвет букв.
FOREGROUND_RED           equ    4h   ;Красный цвет букв.
FOREGROUND_INTENSITY     equ    8h   ;Повышенная интенсивность.
BACKGROUND_BLUE          equ   10h   ;Синий цвет фона.
BACKGROUND_GREEN         equ   20h   ;Зеленый цвет фона.
BACKGROUND_RED           equ   40h   ;Красный цвет фона.
BACKGROUND_INTENSITY     equ   80h   ;Повышенная интенсивность.

    Для определения заголовка окна консоли используется функция SetConsoleTitle, единственным параметром которой является адрес строки с нулем на конце. Здесь следует оговорить следующее: если для вывода в само окно консоли требовалась DOS-кодировка, то для установки заголовка требуется Windows-кодировка. Чтобы покончить с этой проблемой раз и навсегда, посмотрим, как это можно решить средствами Windows.

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

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


    Пример создания собственной консоли. Результат работы следующей программы приведен на рисунке 1:


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

.386P
;Плоская модель памяти.
.MODEL FLAT, STDCALL
;Константы.
STD_OUTPUT_HANDLE   equ -11
STD_INPUT_HANDLE    equ -10
;Атрибуты цветов.
FOREGROUND_BLUE          equ  1h ;Cиний цвет букв.
FOREGROUND_GREEN         equ  2h ;Зеленый цвет букв.
FOREGROUND_RED           equ  4h ;Красный цвет букв.
FOREGROUND_INTENSITY     equ  8h ;Повышенная интенсивность.
BACKGROUND_BLUE          equ 10h ;Синий цвет фона.
BACKGROUND_GREEN         equ 20h ;Зеленый цвет фона.
BACKGROUND_RED           equ 40h ;Красный цвет фона.
BACKGROUND_INTENSITY     equ 80h ;Повышенная интенсивность.
COL1 = 2h+8h    ;Цвет выводимого текста.
COL2 = 1h+2h+8h ;Цвет выводимого текста 2.
;Прототипы внешних процедур.
EXTERN GetStdHandle@4:NEAR
EXTERN WriteConsoleA@20:NEAR
EXTERN ExitProcess@4:NEAR
EXTERN SetConsoleCursorPosition@8:NEAR
EXTERN SetConsoleTitleA@4:NEAR
EXTERN FreeConsole@0:NEAR
EXTERN AllocConsole@0:NEAR
EXTERN CharToOemA@8:NEAR
EXTERN SetConsoleCursorPosition@8:NEAR
EXTERN SetConsoleTextAttribute@8:NEAR
EXTERN ReadConsoleA@20:NEAR
EXTERN SetConsoleScreenBufferSize@8: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   ?
        STR1 DB "Введите строку:",13,10,0
        STR2 DB "Простой пример работы консоли",0
        BUF  DB 200 dup(?)
        LENS DWORD ?  ;Количество выведенных символов.
        CRD  COOR <?>
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE' 
START: 
;Перекодируем строки.
      PUSH OFFSET STR1 
      PUSH OFFSET STR1 
      CALL CharToOemA@8 
      PUSH OFFSET STR2 
      PUSH OFFSET STR2 
      CALL CharToOemA@8 
;Образовать консоль.
;Вначале освободить уже существующую.
      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
;Установить новый размер окна консоли.
      MOV  CRD.X,100 
      MOV  CRD.Y,25 
      PUSH CRD 
      PUSH EAX
      CALL SetConsoleScreenBufferSize@8 
;Задать заголовок окна консоли. 
      PUSH OFFSET  STR2 
      CALL SetConsoleTitleA@4 
;Установить позицию курсора. 
      MOV  CRD.X,0 
      MOV  CRD.Y,10
      PUSH CRD
      PUSH HANDL
      CALL SetConsoleCursorPosition@8 
;Задать цветовые атрибуты выводимого текста.
      PUSH COL1
      PUSH HANDL
      CALL SetConsoleTextAttribute@8 
;Вывести строку.
      PUSH OFFSET STR1
      CALL LENSTR ;В ЕВХ длина строки.
      PUSH 0
      PUSH OFFSET LENS
      PUSH EBX
      PUSH OFFSET STR1
      PUSH HANDL
      CALL WriteConsoleA@20 
;Ждать ввод строки.
      PUSH 0
      PUSH OFFSET LENS
      PUSH 200
      PUSH OFFSET BUF
      PUSH HANDL1
      CALL ReadConsoleA@20 
;Вывести полученную строку.
;Вначале задать цветовые атрибуты выводимого текста.
      PUSH COL2
      PUSH HANDL
      CALL SetConsoleTextAttribute@8 
;------------------------------------------
      PUSH 0
      PUSH OFFSET LENS
      PUSH [LENS] ;Длина вводимой строки.
      PUSH OFFSET BUF
      PUSH HANDL
      CALL WriteConsoleA@20 
;Небольшая задержка.
      MOV  ECX,01FFFFFFFH 
L1:
      LOOP L1 
;Закрыть консоль.
      CALL FreeConsole@0 
      CALL ExitProcess@4 
;Строка -   [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
Текст этой программы можно взять здесь.

    В приведенной программе, кроме уже описанных функций, появились еще две. SetConsoleCursorPosition - установить позицию курсора, и здесь все довольно ясно. Функция SetConsoleScreenBufferSize менее понятна. Она устанавливает размер буфера окна консоли. Этот размер не может уменьшить уже существующий буфер (существующее окно), а может только его увеличить.

    Заметим, кстати, что в функции LENSTR мы теперь используем пару команд ENTER - LEAVE вместо обычных сочетаний команд:

    PUSH BP
    MOV  BP,SP
    SUB  SP,N 
       и
    MOV  SP,BP 
    POP  BP 

    Со следующего шага мы начнем рассматривать обработку событий от мыши и клавиатуры в консольных приложениях.




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