Ассемблер для Windows


Рассмотрим несколько простых консольных функций



II

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

STD_INPUT_HANDLE equ -10 ; для ввода

STD_OUTPUT_HANDLE equ -11 ; для вывода

STD_ERROR_HANDLE equ -12 ; для сообщения об ошибке

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

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

  • 1-й, дескриптор входного буфера.
  • 2-й, адрес буфера, куда будет помещена вводимая информация.
  • 3-й, длина этого буфера.
  • 4-й, количество фактически прочитанных символов.
  • 5-й, зарезервировано.


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

    • 1-й, дескриптор входного буфера консоли.
    • 2-й, структура COORD:
    • COORD STRUC Х WORD ? Y WORD ? COORD ENDS

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

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




      FOREGROUND_BLUE equ 1h ; синий цвет букв

      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. Первым параметром этой функции является указатель на строку, которую следует перекодировать, а вторым параметром - на строку, куда следует поместить результат. Причем поместить результат можно и в строку, которую перекодируем. Вот и все, проблема перекодировки решена. В дальнейшем, в консольных приложениях, мы будем использовать данную функцию без особых оговорок.

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

      Ну что же, пора приступать к разбору следующих примеров.

      .386P ; плоская модель .MODEL FLAT, stdcall

      ; константы STD_OUTPUT_HANDLE equ -11 STD_INPUT_HANDLE equ -10

      ; атрибуты цветов FOREGROUND_BLUE equ 1h ; синий цвет букв 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 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 ; образовать консоль ; вначале освободить уже существующую 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 ; в EBX длина строки 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] ; длина в EBX 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

      Рисунок 2.2.3. Пример создания собственной консоли.

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

      Заметим, кстати, что в функции LENSTR мы теперь используем пару команд ENTER-LEAVE (см. Гл. 1.2) вместо обычных сочетаний. Честно говоря, никаких особых преимуществ такое использование не дает. Просто пора расширять свой командный запас.

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


      Содержание раздела