Организация низкоуровнего консольного ввода-вывода
Организация низкоуровнего консольного ввода-вывода
I Низкий уровень консольного ввода-вывода по сравнению с высоким уровнем И бладает более широкими и гибкими возможностями. Низкоуровневые функции консольного ввода-вывода обеспечивают прямой доступ к входному и экранным буферам консоли, предоставляя приложению доступ к событиям мыши и клавиатуры, а также к информации об изменении размеров окна консоли. Функции низкоуровневого ввода-вывода позволяют приложению иметь доступ по чтению-записи к указанному числу последовательных символьных ячеек в экранном буфере или к прямоугольному блоку символьных ячеек в указанной позиции экранного буфера.
Обсудим возможности низкоуровневого ввода-вывода на примере работы с входным буфером (входной очередью) и буферами экрана. Отметим, для работы с ними существуют разные группы команд. Так, для работы с входным буфером используются функции низкоуровневого ввода-вывода — Wn'teConsolelnputXRead-Consolelnput. Группа функций для работы с буферами экрана будет конспективно рассмотрена в конце этого раздела.
Поддержка работы с мышью в консоли
Большое достоинство консольных приложений — встроенная средствами Windows поддержка мыши. Она реализуется с помощью функции ReadConsolelnput. Важно отметить, что эта функция используется для получения информация о событиях не только мыши, но и о событиях клавиатуры, изменении размера окна и т. д.
B00L ReadConsoleInput(HANDLE hConsolelnput. PINPUT_RECORD lpBuffer, DWORD nLength. LPDWORD lpNumberOfEventsRead);
Параметры этой функции:
действительное число прочитанных записей входного буфера.
Запись входного буфера консоли имеет структуру, называемую INPUTRECORD. Ее описание на языке C++ выглядит так:
typedef struct _INPUT_RECORD { WORD EventType; union {
KEYJVENT_RECORD KeyEvent;
MOUSE_EVENT_RECORD MouseEvent;
WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
MENU_EVENT_RECORD MenuEvent:
FOCUSJVENT_RECORD FocusEvent;
} Event: } INPUT_RECORD;
В этой структуре первое поле EventType размером в слово содержит тип события, а второе поле Event является объединением различных структур. Поля какой из структур будут заполнены, определяется типом события, то есть первым полем, которое может принимать значения:
MOUSEEVENTRECORD с информацией относительно движения мыши или нажатия кнопки;
WINDOW_ BUFFER_SIZE_RECORD с информацией относительно нового размера экранного буфера;
событие используется внутри Windows и должно игнорироваться).
Для обработки события мыши структура MOUSEEVENTRECORD выглядит так:
typedef struct _MOUSE_EVENT_RECORD {
COORD dwMousePosition;
DWORD dwButtonState;
DWORD dwControlKeyState:
DWORD dwEventFlags;
} MOUSE_EVENT_RECORD;
Исходя из вышесказанного структура INPUTRECORD для обработки событий мыши в программе на ассемблере должна выглядеть так:
INPUT_RECORD struc EventType dw 0 dwMousePosition struc x dw 0 у dw 0 ends
dwButtonState dw 0 dwControlKeyState dw 0 DwEventFlags dw 0 ends
Поле EventType для события мыши содержит значение MOUSE_EVENT=0002h, а поля структуры MOUSEEVENTRECORD соответственно означают следующее:
ш dwMousePosition — координаты мыши в окне консоли (в символьных координатах);
м dwButtonState — состояние кнопок мыши в момент возникновения события, при нажатии кнопок устанавливаются следующие биты (при одновременном нажатии устанавливается несколько соответствующих битов):
• LEFT_ALT_PRESSED=0002h - нажата левая клавиша Alt;
• RIGHT_CTRL_PRESSED=0004h — нажата правая клавиша
Ctrl; LEFT_CTRL_PRESSED=OOO8h — нажата левая клавиша Ctrl;
• SHIFT_PRESSED=OOlOh - нажата любая клавиша SHIFT;
• NUMLOCK_ON=0020h - индикатор NumLock включен;
• SCROLLLOCK_ON=0040h — индикатор ScrollLock включен;
• CAPSLOCK_ON=0080h — индикатор CapsLock включен;
ENHANCED_KEY=0100h — нажата клавиша расширенной клавиатуры (101 и 102 клавиши): Ins, Del, Home, End, Page Up, Page Down, «-, t, -», I, / или Enter;
Ш dwEventFl ags — поле содержит одно из двух значений: »
MOUSE_MOVED=0001h — перемещение мыши;
• DOUBLE_CLICK=0002h — выполнен двойной щелчок мыши.
Ве приведена демонстрационная программа обработки событий мыши (prg05_13. asm), которые отслеживаются следующим образом: нажатие левой кнопки приводит к выводу сообщения в позиции нажатия, нажатие правой кнопки приводит к завершению работы программы.
В заключение обращу внимание читателя на то, что API Win32 имеет функцию Mouse_Event, которая позволяет генерировать события, соответствующие реальным движениям мыши и щелчкам ее кнопок. Тем самым API Win32 предоставляет механизм для создания обучающих и демо-версий программ. Формат этой функции:
VOID mouse_event( DWORD dwFlags. DWORD dx. DWORD dy, DWORD dwData. DWORD dwExtralnfo)
Расширенная поддержка клавиатуры в консоли
Функции работы с текстом высокого уровня не дают других возможностей работы с клавиатурой, кроме как примитивного ввода текста. При разработке программ текстового режима часто требуется информация о состоянии управляющих клавиш, о факте удержания клавиши, что может свидетельствовать о желании пользователя повторить ввод некоторого символа или просто о желании получить тривиальный скан-код клавиши. Эти и другие события клавиатуры доступны программе посредством описанной выше функции ReadConsolelnput.
События клавиатуры генерируются при нажатии любой клавиши. Процесс их обработки аналогичен обработке событий мыши. В первую очередь заполняется
о нажатии некоторых управляющих клавиш. Для всех остальных клавиш просто фиксируется факт нажатия. При этом необходимо помнить, что однократному нажатию клавиши реально соответствуют два события — нажатие и отпускание клавиши. В связи с этим программа выводит два сообщения. На практике этого можно избежать, анализируя поле bKeyDown: bKeyDown=l, когда клавиша нажата; bKeyDown=0, когда клавиша отпущена. Выход из программы — при выполнении любых действий с мышью.
Окно консоли и экранный буфер
И в заключение обсуждения особенностей работы с консольными приложениями поясним, что представляет собой экранный буфер консоли и какие средства представляет API Win32 для работы с ним.
Для того чтобы читатель мог легко понять соотношение понятий «окно консоли» и «экранный буфер консоли», представьте себе офисный календарь, на котором текущее число отмечается квадратной рамкой, закрепленной на прозрачной целлофановой ленте и перемещающейся вдоль нее. Теперь представим, что содержимое листа календаря вне этой рамки невидимо, то есть доступно только через окошко, которое образует рамка. Для того чтобы увидеть содержимое всего листа календаря, необходимо двигать рамку. В контексте этой ассоциации — лист календаря — это экранный буфер, а площадь внутри рамки — окно консоли, то есть видимая часть экранного буфера.
Возможна поддержка нескольких экранных буферов, связанных с данной консолью, но только один из них может подвергаться отображению в окне консоли — его называют активным экранным буфером. Другие экранные буферы, если они были созданы, являются неактивными. Для создания экранного буфера используется функция CreateConsoleScreenBuffer. К неактивным экранным буферам можно обращаться для чтения и записи, но отображаться в окне консоли будет только активный экранный буфер (или его часть). Для того чтобы сделать экранный буфер активным, используется функция SetConsoleActiveScreenBuffer. Функция CreateConsoleScreenBuffer имеет показанный ниже формат.
HANDLE CreateConsoleScreenBuffer(DWORD dwDesiredAccess, DWORD dwShareMode,
CONST LPSECURITY_ATTRIBUTES ipSecurityAttributes. DWORD dwFlags. LPVOID lpScreenBufferData):
Параметры функции:
быть разделен, ненулевое состояние этого буфера может быть одним из следующих значений или их комбинацией:
FILESHAREWRITE — другие операции открытия могут быть выполнены для экранного буфера консоли с доступом для записи;
lpScreenBufferData — зарезервирован и должен быть равен NULL.
Функция CreateConsoleScreenBuffer формирует дескриптор созданного экранного буфера, который затем используется функциями для доступа к этому буферу.
Для того чтобы сделать буфер активным, используют функцию SetConsole-Acti veScreenBuf f er.
B00L SetConsoleActiveScreenBuffertHANDLE hConsoleOutput):
Функция имеет единственный параметр — hConsoleOutput — дескриптор экранного буфера, созданного функцией CreateConsoleScreenBuffer. Как уже было отмечено, консоль может иметь много экранных буферов. Функция SetConsoleActiveScreenBuffer определяет, какой из них будет отображен. Приложение может производить запись в неактивный экранный буфер и затем использовать функцию SetConsoleActiveScreenBuffer для отображения содержимого буфера. Чтение и запись в неактивный (и активный тоже) экранный буфер производится функциями низкоуровневого ввода-вывода — WriteConsoleOutputNWriteConsoleOutput-Character и ReadConsoleOutput\ReadConsoleOutputCharacter, которым при вызове передается дескриптор нужного экранного буфера, полученного предварительно функцией CreateConsol eScreenBuf fer.
Каждый из созданных экранных буферов поддерживает собственный текущий прямоугольник окна, определяемый координатами верхней левой и нижней правой символьных ячеек, которые будут отображены в окне консоли. Для определения видимого в окне консоли прямоугольника экранного буфера используется функция GetConsol eScreenBufferlnfo.
B00L GetConsoleScreenBufferInfo(HANDLE
hConsoleOutput. PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferlnfo);
Параметрами этой функции являются:
Структура CONSOLESCREENBUFFERINFO имеет следующий вид:
typedef struct _CONSOLE_SCREEN_BUFFER_INFO
COORD dwSize: :размер экранного буфера в колонках и строках COORD dwCursorPosition: //координаты столбца и строки курсора в экранном буфере
WORD wAttributes: //цвет фона и текста, с которыми записываются
//и отображаются символы в экранном буфере функциями //WriteFile\WriteConsole и ReadFile\ReadConsole
SMALL_RECT srWindow; // определяет структуру
SMALL_RECT. которая содержит координаты // левого верхнего и нижнего правого углов экранного буфера, //видимого в окне консоли на экране дисплея COORD dwMaximumWindowSize; //определяет максимальный размер окна консоли
//с учетом текущего размера экранного буфера и шрифт } CONSOLE_SCREEN_BUFFER_INFO :
Для приложения интерес, в частности, может представлять параметр srWindow с координатами видимой части экранного буфера. Далее, руководствуясь действиями пользователя (выполняющего прокрутку окна или изменение его размера) по отношению к окну консоли, приложение может изменять значения в структуре SMALLRECT и передавать ее на вход функции SetConsoleWindowInfo, которая устанавливает текущий размер и позицию окна консоли относительно экранного буфера.
B00L SetConsoleWindowInf0ChANDLE hConsoleOutput.
B00L bAbsolute. CONST SMALL RECT *lpConsoleWindow);
Параметрами этой функции являются: .
Структура SMALL_RECT имеет следующий вид:
typedef struct _SMALL_RECT {
SHORT Left; //х-координата верхнего левого угла
SHORT Top: //у-координата верхнего левого угла
SHORT Right: //х-координата нижнего правого угла
SHORT Bottom: //у-координата нижнего правого угла } SMALL_RECT:
При работе с функцией SetConsoleWindowInfo следует иметь в виду, что она возвращает ошибку (нулевое значение), если координаты видимой части экранного буфера указывают за его действительные границы. Максимально допустимый раз мер окна для данной консоли можно получить с помощью функции GetConsole-ScreenBufferlnfo. Таким образом, обе эти функции можно использовать для листания экранного буфера.
Для закрытия экранного буфера используется функция CloseHandle, которой передается дескриптор закрываемого экранного буфера.
B00L CloseHandle(HANDLE hObject);
Для того чтобы завершить рассмотрение функций, предназначенных для поддержки консольного приложения, перечислим те из них, что остались «за кадром».
функция | Назначение |
FiConsoleOutputAttribute | Устанавливает цвет текста и фона для указанного числа символьных ячеек, начинающихся по указанным координатам в экранном буфере |
Fi П ConsoleOutputCharacter | Запись символа в экранный буфер указанное число раз по указанным координатам |
Fl ushConsolelnputBuffer | Запись на диск входного буфера консоли. Все входные записи во входном буфере консоли до настоящего момента времени удаляются |
GenerateConsoleCtrl Event | Посылка сигнала, определенного этой функцией, совместно использующим консоль процессам |
GetConsoleCursorlnfo | Предоставление информации о размере и видимости курсора для указанного экранного буфера |
GetConsoleMode | Предоставление информации о текущем входном режиме входного буфера консоли или текущем режиме вывода экранного буфера консоли |
GetConsoleTitie | Извлечение строки из области заголовка для текущего окна консоли |
GetLargestConsoleWi ndowSIze | Возвращает размер самого большого возможного окна консоли, основанного на текущем шрифте и размере изображения |
GetNumberOfConsolelnputEvents | Возвращает число непрочитанных записей ввода во входном буфере пульта |
GetNumberOfConsoleMouseButtons | Возвращает число кнопок на мыши, используемых текущей консолью |
PeekConsolelnput | Чтение данных из входного буфера консоли без их удаления |
Scrol1ConsoleScreenBuffer | Перемещение блока данных в экранном буфере. Действие перемещения может быть ограничено путем определения отсекающего прямоугольника. Содержание экранного буфера вне отсекающего прямоугольника будет неизменным |
SetConsoleCursorlnfo | Установка размера и видимости курсора для указанного экранного буфера консоли |
SetConsoleMode | Установка режима входного буфера консоли или режима вывода экранного буфера консоли |
SetConsoleScreenBufferS1ze | Изменение размера указанного экранного буфера консоли |
SetStdHandle | Установка некоторого дескриптора как дескриптора стандартного ввода, стандартного вывода или устройства ошибки. Может использоваться при перенаправлении ввода-вывода |