API функции ввода-вывода в терминальных приложениях Win32 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

API функции ввода-вывода в терминальных приложениях Win32



Ввод/вывод информации в 32-разрядных приложениях Windows реализуется иначе, чем в 16-разрядных приложениях MS DOS. В данном параграфе рассматриваются способы ввода/вывода информации, использующиеся только в терминальных 32-разрядных программах. Терминальные программы используют окна в текстовом, а не в графическом режиме, для отображения вводимой/выводимой информации.

32-разрядные терминальные программы для Windows вызывают функции операционной системы, используя стандартный интерфейс прикладных программ (Application Programming Interface, API). API представляет собой набор определений структур данных, констант и функций, хранящихся в файлах динамически загружаемых библиотек. Эти файлы являются неотъемлемой частью операционной системы. Все программы, написанные для Win32, используют эти библиотеки для прорисовки и взаимодействия элементов графического интерфейса (окна, меню, кнопки, переключатели, списки и т.п.), а также для обмена данными с оборудованием компьютера (таймером, клавиатурой, монитором, диском и т.п.). Таким образом, API Win32 позволяет использовать объекты и методы, составляющие собственно 32-разрядную систему MS Windows. Однако терминальные программы могут использовать библиотеки только частично, без GUI-функций, тогда как графические приложения – полностью. API Win32 подробно описан в документе, озаглавленном Platform SDK (Software Development Kit), который расположен по адресу www.msdn.microsoft.com.

В отличие от 32-разрядных приложений, программы для MS DOS используют функции BIOS и DOS, вызываемые посредством программных прерываний, механизм которых был придуман ещё при разработке первой модели персонального компьютера IBM PC. DOS-приложения в данном учебном пособии не рассматриваются.

В терминальных программах данные читаются со стандартного устройства ввода (клавиатура, мышь, файл и пр.), а выводятся либо на стандартное устройство вывода (монитор, файл, принтер и пр.), либо на стандартное устройство вывода сообщений об ошибках. Для терминального приложения Windows создаёт один входной буфер и один или несколько буферов для вывода на экран:

· входной буфер состоит из очереди входных записей, каждая из которых содержит информацию об одном событии, поступившем от какого-либо устройства ввода;

· буфер экрана представляет собой двумерный массив, элементы которого содержат данные и атрибуты, влияющие на внешний вид текста, отображаемого на экран терминала.

В Windows есть два типа кодировки символов: 8-разрядная кодировка стандарта ASCII/ANSI и 16-разрядная кодировка стандарта Unicode. Поэтому для работы с текстовыми данными существует два набора одинаковых функций API, отличающихся в названии только последней буквой. Функции, оканчивающиеся на букву “A” обрабатывают ASCII-данные (буква “A” может быть опущена). Функции, оканчивающиеся на букву “W” (wide, расширенный) обрабатывают Unicode-данные. Например, функция:

WriteConsole осуществляет вывод на экран ASCII-строки;

WriteConsoleA осуществляет вывод на экран ASCII-строки;

WriteConsoleW осуществляет вывод на экран Unicode-строки.

Для обеспечения компромисса между простотой использования и полнотой управления предусмотрено два уровня доступа к терминальным функциям:

· Терминальные функции высокого уровня обеспечивают чтение только потока символов из входного буфера, а также запись данных в буфер экрана терминала. Оба этих потока могут быть перенаправлены.

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

Обратите внимание, что API функции не сохраняют содержимое регистров EAX, EBX, ECX и EDX.

Дескрипторы терминала

Практически во всех терминальных API функциях в качестве первого параметра указывается дескриптор (handle) – 32-разрядное целое число без знака, которое уникальным образом идентифицирует некоторый объект в системе.

Дескриптор соответствующего потока можно получить с помощью функции GetStdHandle Идентификатор_стандартного_устройства, которая возвращает в регистре EAX дескриптор стандартного устройства, константа которого указана в качестве параметра.

Идентификаторы стандартных устройств являются константами, значения которых заданы в файле windows.inc и дублируются в файле smallwin.inc:

STD_INPUT_HANDLE EQU -10; стандартное устройство ввода

STD_OUTPUT_HANDLE EQU -11; стандартное устройство вывода

STD_ERROR_HANDLE EQU -12; стандартное устройство для вывода

; сообщений об ошибках

Поэтому в своих программах можно использовать эти значения констант непосредственно. Вот пример вызова функции GetStdHandle для получения дескриптора стандартного устройства ввода:

.data

In dword?

.code

invoke GetStdHandle, STD_INPUT_HANDLE

mov In,eax

C другой стороны этот же вызов функции можно сделать указав значение константы непосредственно:

.data

In dword?

.code

invoke GetStdHandle, -10

mov In,eax

Чтение данных с терминала

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

При использовании API функции высокого уровня ReadConsole эти записи отфильтровываются, и программе возвращается поток введённых символов. Сами же символы удаляются из буфера ввода. При вызове этой функции необходимо ей передать пять параметров, первые три из которых являются входными, четвёртый – выходным, а пятый не используется:

1. Handle; дескриптор устройства ввода (DWORD)

2. Buffer; адрес буфера (PTR BYTE)

3. MaxBytes; максимальное число вводимых символов (DWORD)

4. BytesRead; адрес числа введённых байтов (DWORD)

5. notUsed; зарезервировано (DWORD)

Все указанные параметры могут иметь произвольное имя, т.к. являются переменными. Входной параметр Handle является дескриптором стандартного устройства ввода, полученным функцией GetStdHandle. Входной параметр Buffer является адресом массива неинициализированных символов. Именно в этот массив функция ReadConsole будет помещать коды введённых символов. Массив Buffer может быть и инициализированным, но функция ReadConsole всё равно заменит его кодами вновь ведённых символов. Параметр MaxBytes задаёт максимальное число вводимых символов. Как правило, значение MaxBytes определяется как размер массива Buffer минус два, т.к. в этот массив помещаются два байта управляющего символа конца строки 0Dh, 0Ah. Поэтому при определении размера массива эту особенность надо учитывать. Однако если пользователь введёт большее количество символов, чем размер массива, то массив Buffer будет содержать только байты символов, а оба байта символа конца строки в массиве будут отсутствовать.

Выходным параметром является число реально введённых байтов, но не более чем размер массива. Количество реально введённых байтов помещается в память по адресу BytesRead. Последний параметр функции не используется.

Функция ReadConsole переводит программу в режим ожидания ввода. Способ завершение ввода символов определяется режимом работы стандартного устройства ввода (таблица 1). После завершения ввода функция ReadConsole в регистре eax вернёт ненулевое значение, если ввод успешен, иначе eax будет обнулён. При необходимости этот факт можно использовать для проверки успешного осуществления ввода.

Режим работы стандартного устройства ввода определяется состоянием битов регистра режима ввода. Функция GetConsoleMode позволяет прочитать содержимое этого регистра режима ввода или регистра вывода. Она имеет два параметра, первый входной, второй выходной:

1. Handle; дескриптор устройства ввода или вывода (DWORD)

2. InFlags; адрес переменной куда помещается режим (PTR DWORD)

Для установки нового режима служит функция SetConsoleMode, которая имеет два входных параметра:

1. Handle; дескриптор устройства ввода или вывода (DWORD)

2. Mode; новые значения флагов состояния терминала (DWORD)

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

Таблица 1.5.1. Краткий список возможных значений параметра Mode для стандартного устройства ввода

Параметр режима Бит регистра режима Значение параметра Описание
ENABLE_PROCESSED_INPUT   1h Управляющие клавиши обрабатываются системой и не записываются в буфер ввода
ENABLE_LINE_INPUT   2h Ввод прекращается только после нажатия клавиши Enter
ENABLE_ECHO_INPUT   4h Вводимые символы автоматически повторяются на стандартном устройстве вывода (т.н. эхо-вывод). Этот режим работает только совместно с режимом ENABLE_LINE_INPUT

ВАЖНО: все консольные программы необходимо ассемблировать с помощью команды меню: Project ® Console_Buld_All.

Пример 1.5.1. Ввод с клавиатуры последовательности символов в кодировке ASCII длиной не более шести байтов с эхом на экран.

.data

BufSize = 6h; размер массива вводимых байтов

buffer BYTE BufSize DUP(?); захват памяти под массив ввода

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE ReadConsole, InHandle, offset buffer, BufSize, offset bytesRead, 0

PrintHex eax; вывод значения функции ReadConsole

cmp eax, 0

jz exit; если ввод неуспешен, то на выход

PrintHex bytesRead; вывод числа реально введённых байтов

DbgDump offset buffer, 16; вывод массива байтов

exit:

INVOKE ExitProcess, 0; завершение программы

end start

В упрощённом варианте эта программа выглядит так:

.data

BufSize = 6h; размер массива вводимых байтов

buffer BYTE BufSize DUP(?); захват памяти под массив ввода

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE ReadConsole, InHandle, offset buffer, BufSize, offset bytesRead, 0

DbgDump offset buffer, 16; вывод массива байтов

INVOKE ExitProcess, 0; завершение программы

end start

Пример 1.5.2. Ввод с клавиатуры последовательности символов в кодировке ASCII длиной не более шести байтов без эха на экран.

.data

BufSize = 6h; размер массива вводимых байтов

buffer BYTE BufSize DUP(?); захват памяти под массив ввода

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

Mode DWORD?; режим работы клавиатуры

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE GetConsoleMode, InHandle, offset Mode; сохранение флагов

mov eax,Mode

and eax,1111111111111011B; сброс второго бита

INVOKE SetConsoleMode, InHandle, eax; отмена режима эхо-вывода

INVOKE ReadConsole, InHandle, offset buffer, BufSize, offset bytesRead, 0

DbgDump offset buffer, 16; вывод массива байтов

INVOKE SetConsoleMode, InHandle, Mode; восстановление режима

INVOKE ExitProcess, 0; завершение программы

end start

При запуске примера 1.5.2 ввод символов не будет отображаться на экране.

Пример 1.5.3. Ввод с клавиатуры одиночного символа (без клавиши “Enter”) в кодировке ASCII без эха на экран:

.data

BufSize = 1h; размер массива вводимых байтов

buffer BYTE BufSize DUP(?); захват памяти под массив ввода

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

Mode DWORD?

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE GetConsoleMode, InHandle, offset Mode; сохранение флагов

mov eax,Mode

and eax,1111111111111001B; сброс первого и второго бита

INVOKE SetConsoleMode, InHandle, eax; режима ввода без “Enter”

INVOKE ReadConsole, InHandle, offset buffer, BufSize, offset bytesRead, 0

DbgDump offset buffer, 16; вывод массива байтов

INVOKE ExitProcess, 0; завершение программы

end start

Пример 1.5.4. Ввод с клавиатуры трёх одиночных символов в кодировке ASCII без эха на экран:

.data

BufSize = 3h; размер массива вводимых байтов

buffer BYTE BufSize DUP(?); захват памяти под массив ввода

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

Mode DWORD?

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE GetConsoleMode, InHandle, offset Mode; сохранение флагов

mov eax,Mode

and eax,1111111111111001B; сброс первого и второго бита

INVOKE SetConsoleMode, InHandle, eax; режима ввода без “Enter”

INVOKE ReadConsole, InHandle, offset buffer, BufSize, offset bytesRead, 0

INVOKE ReadConsole, InHandle, offset buffer+1, BufSize,offset bytesRead,0

INVOKE ReadConsole, InHandle, offset buffer+2, BufSize,offset bytesRead,0

DbgDump offset buffer, 16; вывод массива байтов

INVOKE ExitProcess, 0; завершение программы

end start

Упражнение 1.5.1. Разработать программу ввода 20 символов в ASCII-кодировке с эхо-выводом на экран.

Упражнение 1.5.2. Разработать программу ввода 15 символов в кодировке Unicode с эхо-выводом на экран. Для этого надо воспользоваться функцией ввода Unicode-символов ReadConsoleW.

Пример 1.5.5. Программа ввода произвольных числовых данных, содержащих 4 цифры. Для разработки такой программы надо реализовать цикл ввода цифр с заданным числом повторений, равным 10. Тело цикла должно содержать проверку того, что введённый символ лежит в диапазоне “0”…“9”. Если символ находится внутри диапазона, то надо скопировать введённый символ из ячейки ввода в массив цифр. Если символ находится вне диапазона, то надо повторить ввод. Так как функция ReadConsole изменяет регистр ECX, то перед вызовом этой функции счётчик цикла надо сохранять в стеке, а после – восстанавливать из стека.

.data

X BYTE?; один байт под массив ввода

BufLength = 4; размер массива вводимых цифр

Buf BYTE BufLength DUP(?); массив вводимых цифр

InHandle DWORD?; дескриптор устройства ввода

bytesRead DWORD?; количество введённых байтов

Mode DWORD?; режим работы устройства ввода

.code

start:

INVOKE GetStdHandle, STD_INPUT_HANDLE; получение дескриптора

mov InHandle,eax; сохранение дескриптора в памяти

INVOKE GetConsoleMode, InHandle, offset Mode; сохр-е флагов

mov eax,Mode

and eax,1111111111111001B; сброс первого и второго бита

INVOKE SetConsoleMode, InHandle, eax; режим ввода без "Enter"

mov esi,0; обнуление индекса вводимых цифр

mov ecx,BufLength; инициализация счётчика цифр

M:; начало цикла

push ecx; сохранение счётчика цикла

INVOKE ReadConsole, InHandle, offset X, 1, offset bytesRead, 0

pop ecx; восстановление счётчика цикла

cmp X,'0'; сравнение введённого символа с цифрой “0”

jl M; если меньше, то на повторный ввод

cmp X,'9'; сравнение введённого символа с цифрой “9”

jg M; если больше, то на повторный ввод

mov al,X; если введена цифра, то копирование её

mov Buf[esi],al; в массив цифр

inc esi; инкремент индекса вводимых цифр

Loop M; зацикливание если введено меньше 4 цифр

DbgDump offset Buf, 4; вывод массива байтов

INVOKE ExitProcess, 0; завершение программы

end start

Упражнение 1.5.3. На основе примера 1.5.5 разработать программу ввода восьми прописных букв латинского алфавита.

Вывод данных на терминал

API функции WriteConsole и WriteConsoleA осуществляют вывод на экран ASCII-строки. Эти функции обрабатывают стандартные управляющие ASCII-символы, такие как табуляция (09h), возврат каретки (0Dh), перевод строки (0Ah), звуковой сигнал (07h) и возврат на один символ (08h). API функция WriteConsoleW осуществляет вывод на экран Unicode-строки. При вызове этих функций необходимо передать пять параметров, первые три из которых являются входными, четвёртый – выходным, а пятый не используется:

1. Handle; дескриптор устройства ввода (DWORD)

2. Buffer; адрес буфера выводимых символов (PTR BYTE)

3. BufSize; количество выводимых символов (DWORD)

4. BytesRead; адрес числа выведённых байтов (DWORD)

5. notUsed; зарезервировано (DWORD)

Режим работы стандартного устройства вывода определяется состоянием битов регистра режима вывода. Чтение и запись содержимого регистра режима вывода осуществляется функциями GetConsoleMode и SetConsoleMode так, как это было показано для устройства ввода. Возможные параметры режима вывода приведены в таблице 1.5.2.

После вывода символов на экран необходимо вызвать функцию ввода с терминала с целью организации задержки до нажатия клавиши на клавиатуры. Если это не сделать, то сразу после вывода информации на экран выполнится функция ExitProcess, которая завершит консольное приложение и передаст управление системе Windows.

Таблица 1.5.2. Краткий список возможных значений параметра Mode для стандартного устройства вывода

Параметр режима Бит регистра режима Значение параметра Описание
ENABLE_PROCESSED_OUTPUT   1h Управляющие ASCII последовательности выполняются (возврат на один символ, табуляция, звонок, перевод каретки и перевод строки)
ENABLE_WRAP_AT_EOL_OUTPUT   2h Автоматический перевод курсора на следующую строку при достижении им конца строки. Автоматическая прокрутка экрана вверх при его заполнении

Пример 1.5.6. Вывод сообщения на экран терминала.

.data

M byte "This program is a simple demonstration "

byte "of console mode output.",0Ah,0Ah,"Press Enter"

MSize = ($-M); длина сообщения в байтах

outHandle DWORD 0; дескриптор стандартного устройства вывода

bytesWritten DWORD?; число выведенных байтов

inHandle DWORD 0; дескриптор стандартного устройства ввода

bytesRead DWORD?; число введённых байтов

X BYTE?; один байт под массив ввода

.code

start:

INVOKE GetStdHandle, STD_OUTPUT_HANDLE; дескриптор для вывода

mov outHandle,eax

INVOKE WriteConsole, outHandle, offset M, MSize, offset bytesWritten, 0

INVOKE GetStdHandle, STD_INPUT_HANDLE; дескриптор для ввода

mov inHandle,eax

INVOKE ReadConsole, inHandle, offset X, 1, offset bytesRead, 0

INVOKE ExitProcess,0; завершение программы

END start

Пример 1.5.7. Вывод сообщения на экран терминала с использованием управляющих символов.

.data

M byte 0Ah,0Ah,0Ah,0Ah,0Ah,0Ah,09h; управляющие символы

byte "This program is a simple demonstration of console mode output."

byte 0Ah,0Ah,0Ah,0Ah,0Ah,0Ah,09h,09h,09h,09h,07h,"Press Enter"

MSize = ($-M); длина сообщения в байтах

outHandle DWORD 0; дескриптор стандартного устройства вывода

bytesWritten DWORD?; число выведенных байтов

inHandle DWORD 0; дескриптор стандартного устройства ввода

bytesRead DWORD?; число введённых байтов

X BYTE?; один байт под массив ввода

.code

start:

INVOKE GetStdHandle, STD_OUTPUT_HANDLE; дескриптор для вывода

mov outHandle,eax

INVOKE WriteConsole, outHandle, offset M, MSize, offset bytesWritten, 0

INVOKE GetStdHandle, STD_INPUT_HANDLE; дескриптор для ввода

mov inHandle,eax

INVOKE ReadConsole, inHandle, offset X, 1, offset bytesRead, 0

INVOKE ExitProcess,0; завершение программы

END start

Упражнение 1.5.4. На основе примера 1.5.7 разработать программу построчного вывода своей фамилии, имени и отчества буквами латинского алфавита с отступом от левого края на две табуляции и выходом из программы по нажатию любой клавиши. Для определения факта нажатия любой клавиши необходимо в конце программы перевести устройство ввода в режим одиночного ввода.

Упражнение 1.5.5. Вывести на экран все 256 символов ASCII таблицы. Для этого необходимо организовать цикл с заданным числом повторений, в котором сформировать массив 256 чисел от 0 до 255. После завершения цикла достаточно вывести сформированный массив с помощью функции WriteConsole.

API функция WriteConsoleOutputCharacter копирует массив символов в буфер экрана терминала, начиная с указанной позиции. При вызове функции необходимо передать пять параметров, первые четыре из которых являются входными, а пятый – выходным:

1. Handle; дескриптор стандартного устройства вывода (DWORD)

2. Buffer; адрес буфера выводимых символов (PTR BYTE)

3. BufSize; количество выводимых символов (DWORD)

4. xyPos; координаты первого выводимого символа (DWORD)

5. BytesRead; адрес числа выведённых байтов (DWORD)

Окно терминального Windows-приложения работает в текстовом режиме и имеет 25 строк, нумеруемых от 0 до 24, и 80 столбцов, нумеруемых от 0 до 79. В таблице 1.5.3 приведены примеры координат расположении курсора на экране. Координаты вывода определяются числом, имеющим размер DWORD. Старшее слово задаёт координату Y, т.е. номер столбца. Младшее слово – координату X, т.е. номер строки.



Поделиться:


Последнее изменение этой страницы: 2016-06-29; просмотров: 809; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.218.48.62 (0.095 с.)