Консольное приложение на ассемблере

Метки:  , , , , , , , , , ,

После разработки серии оконных приложений, мы познакомились с основными принципами программирования графических примитивов. И тут дернула меня нечистая написать простую программку для решения специфической микрозадачи. Понятное дело, что использовать оконный интерфейс для подобного случая не очень то и хотелось, поэтому было решено сделать приложение консольным .. лучше бы я в этот огород вообще не лез! :) Ну уж, как говорится "взялся за гуж, не говори что не дюж".
Было бы бессмысленно отрицать, что в последние десятилетия роль графического интерфейса существенно возросла, тем не менее в новейших операционных системах текстовые режимы всё еще сохраняются в виде разнообразных графических подсистем, в частности - текстового пользовательского интерфейса (окна командной строки/терминала). Из этого следует, что если уж текстовый режим до сей поры присутствует в операционных системах, значит он не является исключительно аппаратным рудиментом, поддержка которого существует лишь на уровне видеоадаптера, а представляет собой актуальный и активно используемый механизм. Основная причина по которой текстовый режим всё еще существует, заключается в следующем:

Большое количество системных утилит, входящих в состав операционной системы, используют исключительно текстовый интерфейс и рассчитаны на использование аргументов командной строки.

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

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

Помимо класса консольных утилит, в практике разработчика часто возникают задачи, в которых разворачивать оконный графический интерфейс соразмерно напрасной трате своего и процессорного времени. В подобного рода задачах вполне достаточным условием является использование текстового (для вывода диагностических сообщений) или вовсе неинтерактивного (вывод данных в файл) режимов. Действительно, зачем пытаться изображать графический интерфейс там, где он явно излишен или вовсе не обязателен, не проще ли в таких задачах от него отказаться вовсе? Поэтому, сегодня мы акцентируем внимание на особенностях языка Ассемблера (FASM) при написании консольного приложения на ассемблере под Windows, и темой данной статьи будет создание серии простейших консольных приложений, демонстрирующих основные алгоритмы взаимодействия с консолью. Разработанные шаблоны (в примерах) могут быть в дальнейшем использованы в качестве базовых в различного рода проектах.
Итак, в словосочетании консольное приложение на ассемблере присутствует ключевое слово "консоль", на которое стоит обратить особое внимание, поскольку именно оно даст нам понимание основных принципов работы. Скорее всего, люди уже знакомые с компьютером в общем и операционными системами в частности, знают что это такое, хотя могут понимать это в широком, так сказать, смысле этого слова: механизм для ввода информации с клавиатуры и вывода ее на экран. Поэтому, не лишним будет дать серию расширенных определений:

Текстовый пользовательский интерфейс (Text user interface, TUI / Character-Mode User Interface, CUI) - разновидность интерфейса, использующая для ввода-вывода/представления информации текстовый режим работы видеоадаптера и набор буквенно-цифровых символов и символов псевдографики (таблица ASCII).

что является базой для:

Консоль (интерфейс командной строки) - текстовый интерфейс (имитирующий на экране бесконечную бумажную ленту, прокручивающуюся в обе стороны), ввод-вывод в рамках которого может осуществляться через стандартные потоки: ввод (stdin), вывод (stdout), ошибка (stderr), а так же прямыми чтением/записью из буферов ввода-вывода. По умолчанию к потоку ввода "подключена" клавиатура (мышь), а к потоку вывода — экран (монитора).

что, в свою очередь, формирует понятие:

Консольное приложение Windows - класс приложений, использующих для взаимодействия с системой/пользователем объекты консоли: текстовый интерфейс и стандартные потоки ввода-вывода.

Консольное приложение операционной системы Windows обеспечивает взаимодействие с пользователем через так называемое окно консоли. Примером подобных консольных приложений могут являться: окно командной строки (cmd), файловые менеджеры (например, Far Commander), и ряд типовых системных консольных утилит:

консоль cmd

Различия оконного и консольного приложений

Консольные приложения являются одним из типов исполняемых образов (приложений) Windows, наряду с типовыми оконными (GUI), библиотеками (DLL), драйверами (native) и некоторыми другими. По сути это полноценные приложениями, имеющие ряд специфических отличий:

  • Значение поля Subsystem заголовка результирующего PE-файла равно 3 (IMAGE_SUBSYSTEM_WINDOWS_CUI). На основании значения данного поля, загрузчик образов при подготовке приложения к выполнению производит свойственную консольным приложениям последовательность загрузки.
  • При запуске консольного приложения, загрузчик образов пытается наследовать консоль от процесса-родителя (приложение, из-под которого был произведен запуск):
    • Создается новая консоль, если родительский процесс не имеет консоли.
    • Консоль наследуется, если процесс-родитель тоже является консольным приложением (окно командной строки и прочее).
  • Консоль создается/наследуется с тремя стандартными потоками (объектами) ввода/вывода, каждый из которых имеет свой описатель (для организации программного обмена).
  • [визуально] На основе параметров в ветви реестра HKEY_CURRENT_USER\Console, на рабочем столе создается/открывается отдельное окно с текстовым режимом отображения содержимого;
  • [визуально] При запуске из проводника (explorer.exe), операционная система открывает окно консольного приложения, производит выполнение кода консольного приложения и затем закрывает окно консоли. То есть, если не предпринято дополнительных мер, окно консольного приложения очень быстро "промелькивает" на экране.
  • Имеется возможность создания приложения, которое функционально ограничено единственной библиотекой kernel32.dll (использование функции работы с консолью и сохранение совместимости между версиями Windows). Это позволяет отказаться от подключения библиотеки user32.dll (функции работы с окнами), что, в свою очередь, предотвращает загрузку серии зависимых библиотек на этапе подготовки образа к выполнению, тем самым обеспечивается создание более оптимизированных по использованию памяти приложений.

Следует ли из всего вышеперечисленного, что все функциональные методы консольного приложения "заключены" внутри собственного текстового консольного окна и им лишь и ограничиваются? Отнюдь, основная идея заключается в том, что отличие оконного приложения от консольного чисто условное, поскольку консольное приложение сохраняет возможность вызывать GUI-функции (то есть функции, работающие уже с графическими примитивами) программного интерфейса Win32, все зависит лишь от набора подключаемых библиотек. Поэтому можно сделать следующее обобщение:

Ограничение консольных приложений текстовым режимом в терминах операционной системы Windows, достаточно условное.

Иначе говоря, консольные приложения имеют возможность полноценно взаимодействовать с функциями Win32 API наравне с типовым оконным GUI-приложением, ведь в процессе написания исходного кода автор имеет возможность импортировать (подключать) любые функции любых доступных коду библиотек, каковые он сочтет нужными.

Объекты консольного приложения

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

  • [единственный] входной буфер - область данных (события/сигналы/данные) для ввода (передачи на консоль);
  • [несколько] экранный выходной буфер - область данных (символы/атрибуты) для вывода (отображения на экране);
  • Окно консоли - область экрана, отображающая часть выходного буфера;
  • Текущая позиция курсора - маркера вывода, обозначающий текущую позицию вывода;

Стандартные потоки

С консолью в операционной системе Windows закреплено три основных потока ввода-вывода:

Наименование Назначение
Стандартный ввод (stdin) Поток данных, идущих в программу.
Стандартный вывод (stdout) Поток данных, идущих из программы.
Стандартная ошибка (stderr) Поток сообщений об ошибках, идущих из программы.
Стандартный поток ввода-вывода — объект процесса консольного приложения, предназначающийся для организации обмена данными.

Традиционно, со стандартным вводом ассоциирована клавиатура, а со стандартным выводом ассоциирован монитор (экран), хотя потоки могут быть переопределены (перенаправлены) и на другие логические устройства (файл, ввод/вывод другой программы и прочее). Вследствие всего этого возникает резонный вопрос: обязательно ли наличие окна консоли у консольного приложения? Ведь, теоретически, консольные программы могут обходиться и без классического ввода (с клавиатуры) и вывода (в окно, на экран), поскольку объекты stdin и stdout могут быть связаны с файлами, потоками ввода/вывода других программ или иными объектами операционной системы? Тем не менее, стандартный сценарий использования консольного приложения в Windows подразумевает создание отдельного окна консоли.

Окно и буферы

Как уже оговаривалось, одними из основных объектов консольного приложения являются окно консоли, входной буфер консоли и экранный буфер консоли.

Входной буфер консоли (Input buffer) -- очередь данных, каждая запись которой содержит информацию относительно входного события консоли (нажатие/отпускание клавиш, движение/нажатие/отпускание мыши и прочие).
Экранный буфер консоли (Screen buffer) -- двумерный массив для хранения данных консольного приложения (символы+атрибуты), которые могут быть отображены в окне консольного приложения.

И окно и буфер имеют собственные [исходные] размеры, которые могут изменяться при помощи функций Win32 API. По умолчанию размеры, определенные в операционной системе Windows:

console buffer size

  • размер буфера - 80х300 символов (300 строк по 80 символов длиной каждая);
  • размер экрана - 80х25 символов (25 строк по 80 символов длиной каждая);

Получается примерно следующая картина:

буфер и окно

Соответственно, из увиденного мы можем сделать несколько выводов:

  • Размер экранного буфера консоли должен быть больше или равен размера окна консоли;
  • В случае, когда консольный буфер больше (по размеру) чем окно консоли, в окне отрисовываются ползунки горизонтальной и/или вертикальной полосы прокрутки.

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

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

Задать (поменять) буферу консоли требуемый размер можно при помощи функции SetConsoleScreenBufferSize. Имеется так же возможно создать новый консольный экранный буфер, для этого предоставляется функция CreateConsoleScreenBuffer. При этом надо помнить, что каждый из созданных таким образом буферов имеет собственную отображаемую область окна, определяемую координатами верхнего левого и нижнего правого знакомест (символьных ячеек). Для определения отображаемой в консольном окне области экранного буфера (в числе прочих параметров), используется функция GetConsoleScreenBufferInfo.

Основные функции

Общие функции:

  • SetConsoleMode -- задает режим работы консольного буфера ввода и режим консольного экранного буфера вывода;
  • SetConsoleTitle -- меняет название (текст в шапке) текущего консольного окна;
  • GetStdHandle -- возвращает описатель указанного стандартного устройства (стандартный ввод, стандартный вывод, стандартная ошибка);
  • SetConsoleCP -- задает кодовую страницу ввода для консоли, закрепленной за текущим процессом;
  • GetConsoleCP -- возвращает кодовую страницу ввода для консоли, закрепленной за текущим процессом;
  • SetConsoleOutputCP -- задает кодовую страницу вывода для консоли, закрепленной за текущим процессом;
  • GetConsoieOutputCP -- возвращает кодовую страницу вывода для консоли, закрепленной за текущим процессом;
  • SetConsoleTextAttribute -- изменение атрибутов (цвета) текста/фона.
  • FillConsoleOutputAttribute -- изменение атрибутов символов для заданного количества знакомест (начиная с заданных координат экранного буфера).
  • SetConsoleCursorPosition -- задает позицию курсора для указанного консольного экранного буфера.

Функции для работы с буфером:

  • WriteConsole -- выводит символы в выходной экранный буфер консоли (в текущую позицию курсора);
  • ReadConsole -- производит (фильтрованное) чтение символов из входного буфера консоли (удаляя эти данные из буфера);
  • SetConsoleActiveScreenBuffer -- назначение активного (текущего) экранного буфера консоли;
  • SetConsoleScreenBufferSize -- задание (изменение) размера экранного буфера консоли;
  • CreateConsoleScreenBuffer -- создания нового (собственного) консольного буфера;
  • GetConsoleScreenBufferInfo -- запрос параметров экранного буфера;
  • WriteConsoleOutput -- записывает символ и атрибут цвета в заданную прямоугольную область консольного экранного буфера;
  • WriteConsoleOutputCharacter -- копирование заданного числа символов в последовательно располагающиеся ячейки консольного экранного буфера;

Функции для работы с окном:

  • SetConsoleWindowInfo -- перемещение (прокрутка) позиции отображения, изменение размера видимой области в рамках окна консоли.
  • GetLargestConsoleWindowSize -- получение максимально-возможного размера консольного окна.

Пример 1: вывод строки и ожидание нажатия клавиши

Настало время перейти непосредственно к практической части вопроса, и в качестве стартового примера вы приведем простейшее консольное приложение, выводящее в консоль одну-единственную строку "Hello World!" и ожидающую ввода любого символа.

В отличии от других компиляторов исходных кодов языка Ассемблер, для FASM характерно довольно существенное преимущество, а именно включение всех опций компиляции непосредственно в исходный код приложения. Например, в данном примере мы разрабатываем консольное приложение, соответственно, для указания данного факта нам вовсе не обязательно задавать какие-то опции командной строки, достаточно в исходном тексте, в качестве параметра директивы format указать определение console (см. строку 1).
Итак, переходим к секции кода. Как мы помним из теории, приведенной в начале статьи, для выполнения ввода-вывода в окно консольного приложения, нам сперва надо получить дескриптор (описатель) стандартных потоков. Windows API для этого предоставляется функция GetStdHandle, прототип которой выглядит следующим образом:

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

Имя Размер Значение Назначение
STD_INPUT_HANDLE DWORD -10 Стандартный ВВОД. Изначально является консольным буфером ввода (ассоциирован с клавиатурой).
STD_OUTPUT_HANDLE DWORD -11 Стандартный ВЫВОД. Изначально является консольным буфером вывода (ассоциирован с экраном).
STD_ERROR_HANDLE DWORD -12 Стандартная ОШИБКА. Изначально является буфером для вывода ошибок (ассоциирован с экраном).

В строках 10 и 11 нашего исходника происходит запрос дескрипторов стандартного вывода и стандартного ввода соответственно. Полученные описатели, возвращаемые функцией в регистре EAX, сохраняются в локальных переменных (stdout, stdin).
Далее по коду (строка 14) располагается функция вывода текста в окно консоли WriteConsole. Эта функция более сложная, и тут у нас используется уже целых пять входных параметров (слева-направо):

  1. дескриптор стандартного потока вывода
  2. указатель на строку выводимых (печатаемых) символов
  3. число печатаемых символов
  4. указатель на локальную переменную, получающую число фактически выведенных символов
  5. зарезервированный параметр, резерв (зарезервировано для следующих версий);
Поток выводимых символов отображается в окно консоли начиная с текущей позиции курсора. Позиция курсора сдвигается по мере вывода символов.

После вывода строки Hello, world! на в окно консоли, у нас организовано ожидание ввода символа посредством функции ReadConsole (строка 15). Это сделано с одной-единственной целью - воспрепятствовать автоматическому закрытию окна, поскольку консольное окно после выполнения кода приложения обычно закрывается. Функция ReadConsole принимает на вход пять параметров (слева-направо):

  1. дескриптор стандартного потока ввода
  2. указатель на буфер, получающий данные из потока ввода
  3. количество символов для чтения
  4. Указатель на переменную, которая получает количество фактически считанных символов
  5. Указатель на структуру прототипа CONSOLE_READCONSOLE_CONTROL, которая получает контрольный символ для сигнализации об окончании операции чтения. В нашем случае не используется, поэтому NULL.
На низком уровне функции WriteConsole и ReadConsole отфильтровывают данные соответственно выходного и входного буферов так, чтобы удалить из них данные о событиях мыши, клавиатуры, изменений размеров консольного окна. Соответственно, в следствии данного факта эти функции являются как бы "высокоуровневыми", обеспечивая простой вывод символов в окно консоли и ввод символов с клавиатуры.

И под конец секции кода у нас выполняется функция ExitProcess (строка 17) с единственным аргументом, имеющим значение 0. Она производит завершение вызывающего процесса и всех его потоков. При завершении процесса, закрепленная за процессом консоль освобождается автоматически.

Характерным отличием этого примера является автоматическое создание консоли самой операционной системой.

Пример 2: собственная консоль и размеры видимой области

В случае с консольным приложением из предыдущего примера, окно консоли создается и отображается системой в автоматическом режиме. Но что бы получить возможность гибко управлять различными аспектами консоли, нам потребуется создать так называемую собственную консоль, и в данном примере мы будем попытаемся это реализовать.

Алгоритм приведенного выше исходного кода можно описать следующим образом:

  1. Отключим (освободим) консоль по-умолчанию, ассоциированную с процессом;
  2. Создадим (назначим) процессу новую "собственную" консоль;
  3. Получим параметры текущего консольного экранного буфера
  4. На основе размером буфера выставим размеры видимого окна консоли: размер (ширина, высота) видимой области консоли = размер (ширина, высота) буфера, деленный на 4.
  5. Освободим созданную ранее консоль.

Как было уже сказано, операционная система в ходе загрузки консольного приложения на выполнение, автоматически создает для него "консоль по умолчанию", однако за консольным приложением (в один момент времени) может быть закреплена единственная консоль, и если мы хотим создать собственную консоль, то автоматически созданную нам потребуется закрыть. И вот тут возникает ряд дополнительных нюансов.

На просторах Сети можно встретить рекомендацию: в случае необходимости работы с собственной консолью и отсутствия уверенности в том, наследовало ли приложение консоль (передается консоль приложению) или нет, предлагается сначала "отвязывать" процесс от текущей консоли функцией FreeConsole, а затем уже назначать собственную консоль вызывающему процессу при помощи функции AllocConsole.

Функция FreeConsole (строка 10) выполняет отключение вызывающего процесса от связанной с вызывающим процессом консоли. После чего наш код осуществляет создание "собственной" консоли посредством вызова функции AllocConsole (строка 11). Все, собственная консоль создана. Затем, при помощи функции GetConsoleScreenBufferInfo (строка 17), производится запрос параметров активного, ассоциированного с консольным приложением экранного буфера. Полученные параметры (размеры, позиция курсора, атрибуты, позиции углов прямоугольника, образуемого буфером, максимальные размеры консольного окна) сохраняются в структуре под названием lpConsoleScreenBufferInfo, которая описана в нашем приложении и имеет прототип CONSOLE_SCREEN_BUFFER_INFO (строки 55-61).
Далее, мы имеем намерение поменять видимую область, то есть прямоугольник, в котором отображается часть содержимого экранного буфера. Осуществим это при помощи функции SetConsoleWindowInfo (строка 28), которая в качестве параметра использует структуру типа SMALL_RECT (строки 48-53). Но с назначением видимой области надо быть предельно аккуратным, поскольку:

Отображаемая в консольном окне область буфера не может быть больше размеров самого буфера.

Функция SetConsoleWindowInfo не меняет позиций консольного окна на рабочем столе, она предназначается для перемещения (прокручивания) позиции и изменения размеров отображения (отображаемого в консольном окне участка экранного буфера). Таким образом эта функция может быть использована для типовой задачи, связанной с программированием консольных приложений: "прокрутки" содержимого консольного экранного буфера путем смещения позиции отображаемого прямоугольника. А вот для непосредственного перемещения окна по экрану рекомендуется использовать типовую функцию перемещения окна MoveWindow.
В финальной части программы мы видим вызов функции ожидания Sleep (применяется для организации задержки), сразу после которой происходит "освобождение" консоли посредством функции FreeConsole и последующего выхода из программы через ExitProcess.

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

Пример 3: кодировка

А вы когда-нибудь пробовали вывести в окно Windows-консоли текст, состоящий из символов кириллицы? Я вот тут попробовал намедни, просто переписал фразу из первого примера статьи на наш великий и могучий русский язык, а вот что у меня из всего этого получилось вы можете наблюдать на данном снимке экрана:

кодировка консоли

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

Причина этого явления кроется в том, что разработчики, в операционных системах линейки Windows, придумали для кириллицы новую кодировку CP1251, при этом (в целях совместимости) сохранив и старую со времен MSDOS - CP866.

Текстовые редакторы или среды разработки, которыми пользуется немалое количество специалистов (например, notepad++, редактор из состава Far Commander, блокнот (notepad) и многие другие) работают с текстом в кодировке ANSI, что для русской локализации эквивалентно кодовой странице 1251 (Windows-1251, CP1251). Тем не менее, для консоли действует следующее правило:

По умолчанию окно консоли настроено на работу с растровыми (точечными) шрифтами (raster fonts), которые правильно отображают лишь кодовую страницу OEM (она же cp866).

Получается, что стандартные потоки ввода-вывода окна консоли в Windows функционируют в кодировке OEM (DOS-OEM, OEM-866, что соответствует кодовой странице 866 (CP866) для русского языка)? Вероятно это действительно так, поскольку, например, в ветви реестра HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage параметр OEMCP имеет значение по умолчанию 866. При подобного рода различиях кодовых страниц в Win32 API и консоли, то есть фактически кодировок исходных данных и стандартных консольных потоков, результат очевиден.
Имеется два основных метода решения данной проблемы:

  1. Сменить кодировку самого редактора, используемого вами для редактирования исходного кода, на OEM. И, конечно же, перенабить все локализованные строки, содержащие кириллицу. Но тут есть один нюанс: в OEM-кодировке должны быть входные данные (файлы) для нашего приложения, и в этой же OEM-кодировке будут и все выходные данные.
  2. Использовать функций, задающие кодировку стандартных потоков ввода-вывода консоли, такие как SetConsoleOutputCP и SetConsoleCP. Но тут одной сменой кодировки в исходном тексте не обойтись и придется сделать дополнительное телодвижение: в свойствах окна консоли (например cmd) необходимо единожды выбрать любой TrueType-шрифт (доступные в свойствах окна: Consolas и Lucida Console).
  3. Конвертировать выводимые данные "на лету". В этом случае отпадает необходимость смены кодировки основного исходного текста приложения. Реализуется это при помощи функций CharToOem (если текст в 1251) и WideCharToMultiByte (если текст в UNICODE).
  4. (попробовать) Использовать "прямую" запись в буфер вместо работы через функции, использующие стандартные потоки. На практике не проверял!!

Почему присутствуют функции определения кодировки для входных и выходных потоков консоли? Это объясняет тем, что:

С каждой консолью операционная система ассоциирует две кодовые таблицы — для ввода и вывода.

Соответственно, кодовые таблицы используются следующим образом:

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

Ну а вот второй из вышеописанных способов давайте опишем на примере исходного кода:

Тут все достаточно просто, в коде выделены все критические места: вызовы функций SetConsoleOutputCP и SetConsoleCP, а так же набранный в соответствующей кодировке русский текст.

3 комментария:

  1. Victor

    На фасме код не компилиться ворчит

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Помощь сайту