Dolya_po_C++

Доля П. Г. Харьковский Национальный Университет механико – математический факультет

Введение в C/C++ программирование консоли

На начальном этапе обучения C/C++ обычно создаются консольные приложения. Затем, познакомившись с основными возможностями языка, но еще не имея достаточного опыта, учащимся хочется создавать сколько-нибудь привлекательные приложения. В этом пособии мы предлагаем познакомиться с продвинутыми возможностями программирования в консольном окне Windows. В первом разделе пособия мы покажем, как можно улучшить внешний вид программ, используя только «текстовый» режим консоли. Затем вы познакомитесь с графическими возможностями этого окна. Вероятно многие программисты даже не подозревают, что в этом окне можно рисовать. Однако консольное окно – это окно Windows и в нем, как и в любом другом окне, можно использовать графические функции. Во втором разделе пособия читатели познакомятся с основными графическими функциями, которые программисты C/C++ могут использовать в простейшем режиме консольного окна.

1. Элементы C/C++ программирования в консольном окне.

или стандартные потоки и ввода/вывода C++. Однако библиотека функций не ограничивается только ими. Имеется возможность использовать функции Windows API для управления консольным вводом – выводом. В этом параграфе мы познакомим читателя с возможностями этих функций. Чтобы использовать такие функции вы должны включить в программу заголовочный файл windows.h, благодаря которому можно получить доступ к большому количеству функций, составляющих библиотеку Windows API. Здесь мы кратко опишем некоторые из них.

1.1 Управление консольным окном в «текстовом» режиме

Функция устанавливает атрибуты цвета текста (цвет букв и цвет фона ячеек с буквами). Она использует следующий синтаксис

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

Для получения идентификатора/дескриптора консольного окна (можно использовать функцию :

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

Здесь константа говорит, что вы хотите получить дескриптор стандартного устройства ввода/вывода

Вторым аргументов в функцию передается число, биты которого управляют цветами фона и текста. Например, команды

SetConsoleTextAttribute(hStdOut, 2); cout << “Green
“;

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

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

b b b b f f f f

Интенсивность фона и текста (яркость) задается битами и . Наличие красной, зеленой и синей составляющей цвета фона определяется битами

, , , а биты , , определяю цвет текста. Т.о. цвета фона и текста

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

примере ранее, передав вторым аргументом десятичное число 2, которое

двоичной записи имеет вид 00000010, мы задали только бит 1, который и

определил зеленый цвет текста на черном фоне. Однако второй аргумент функции удобнее конструировать из набора флагов. Интересующие нас флаги включают и , которые обозначают цвет фона и текста соответственно. Например, команды

cout << “Yellow
“;

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

FOREGROUND_RED 1, а флаг FOREGROUND_GREEN

соответствует биту 1. Оставшиеся биты равны нулю. Аналогично задаются цвета фона. Например, команды

cout << “White on blue
“;

выводят белый текст на ярко синем фоне.

Приведенные выше команды, объединены в единый код в следующем примере.

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); //Выводим текст разными цветами

cout << “Green
“; //зеленый текст на черном фоне

cout << “Yellow
“; //желтый текст на черном фоне

cout << “White on blue
“; //белый текст на синем фоне

// установка цветов для последующей печати

качестве аргумента этой функции строки эквивалентно команде консоли , которая очищает экран.

В данной программе для простоты не был реализован контроль ошибок. О нем мы поговорим позже.

Таким образом, цветом текста управляет всего 4 бита, из которых мы можем получить 16 разных комбинаций. Раскраска фона выполняется аналогично. В частности команда

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

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

string intto4chars(int val1, int val2);

int k,IR,GB,rez; string strrez;

cout << ”

“; for(IR=0; IR<=3; IR++)

for(k =0; k<3; k++)

for(GB=0; GB<=3; GB++)

strrez = intto4chars(IR, GB); rez = (IR<<6) + (GB<<4);

SetConsoleTextAttribute(hStdOut,rez); if (k==1)

cout<<” “<<strrez<<” “; else

cout << ”
“; SetConsoleTextAttribute(hStdOut, 0x70); system(“pause”);

преобразование пары чисел 0<=val1<=3 и 0<=val2<=3 в

четырехсимвольную «двоичную» строку

int val = (val1 << 2) + val2; string sbfull(len, ‘0’);

string sbrez(_itoa(val, chrez, 2));

sbfull.replace(len – sbrez.length(), sbrez.length(), sbrez); return sbfull;

На предыдущем рисунке показано консольное окно после выполнения

Соответственно этим значениям в программе введены переменные и , которые принимают значения от 0 до 3. Вспомогательная функция переводит пару чисел и в строковое представление

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

Функция устанавливает заголовок консольного окна. Например,

SetConsoleTitle(L”Заголовок консольного окна”);

Обратите внимание на префикс , который стоит перед строкой заголовка. О нем мы поговорим позже.

То же можно сделать командой

system(“title Заголовок консольного окна”);

Действительно,если бы вы захотели изменить заголовок консоли не из программы, а из самой консоли, то должныбылибы подать команду

Функция устанавливает положение текстового курсора в консольном окне. Например,

COORD cursorPos; cursorPos. X = 5; cursorPos. Y = 2;

Первым аргументом она принимает дескриптор консольного окна, а вторым – переменную типа (это структура с двумя полями X и Y), в котрой должны находиться координаты текстового курсора.

Функция извлекает информацию о буфере консольного окна. Например,

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(hStdOut, &csbiInfo);

Первый аргумент является дескриптором консольного окна (, второй

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

содержат значения текстовых координат левого верхнего и правого нижнего углов консольного окна.

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

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

CONSOLE_SCREEN_BUFFER_INFO csbiInfo; void drawBox(HANDLE hStdOut, COORD Pos);

system(“CLS”); // заливка консольного окна серым цветом

// Возвращаем исходные цвета

SetConsoleCursorPosition(hStdOut, cursorPos); system(“pause”);

for (int j = 0; j < 6; j++) cout << char(177); Pos. Y += 1;


Dolya_po_C++

Первое состояние консольного окна показано на следующем рисунке слева. Справа показано второе состояние окна.

Функция рисует прямоугольник из 6 x 6 символов с кодом 177, используя второй аргумент для позиционирования его левого верхнего угла.

Когда открывается консольное окно, то для хранения символов, напечатанных в этом окне, заранее выделяется некоторая область памяти, которая называется буфером окна консоли (console screen buffer). Фактически буфер, это одномерный массив символов и их атрибутов. Каждый элемент этого массива имеет тип , являющийся структурой, хранящей код символа (ASCII или Unicode) и его атрибуты. Нулевой элемент этого массива соответствует символьной ячейке (0,0) в окне консоли, следующий – ячейке (0,1), и далее последовательно до конца текстовой строки окна. Последующий элемент массива соответствует точке/ячейке (1,0). Например, если в строке консоли 80 текстовых ячеек, то 80 – й элемент массива будет хранить код символа, отображаемого в этой ячейке (а также его атрибут). В 160 – ом элементе будет содержаться код символа (и атрибут), напечатанный в ячейке (2,0) и т.д. Имеются функции, которые печатают символы или их атрибуты в этот буфер.

Функция многократно печатает заданный символ в буфер консоли, начиная с указанной позиции

BOOL WINAPI FillConsoleOutputCharacter(

// задает координаты первой ячейки

LPDWORD lpNumberOfCharsWritten // Указатель на

переменную, которая получит количество

символов, напечатанных в действительности

Например, если переменная является идентификатором окна консоли, то следующий фрагмент кода выводит подряд 200 символов , начиная с 10

– й ячейки 5 – й строки.

DWORD cWrittenChars; FillConsoleOutputCharacter(wHnd,(TCHAR)’X’,


Dolya_po_C++

У функции есть функция – двойник , которая устанавливает атрибуты цвета для заданного количества символьных ячеек, начиная с указанного места в буфере консоли.

BOOL WINAPI FillConsoleOutputAttribute(

// переменную, которая получит количество // символьных ячеек, чьи атрибуты // установлены в действительности

Например, следующий фрагмент кода устанавливает бирюзовый цвет ячеек, начиная с 5 – й по 7 – ю строки и еще 20 ячеек 8 – й строки.

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

void cls(HANDLE hConsole)

DWORD dwConSize; DWORD cCharsWritten;

dwConSize = csbi.dwSize. X * csbi.dwSize. Y; // количество

// символьных ячеек в буфере консоли

FillConsoleOutputCharacter(hConsole,(TCHAR)’ ‘, dwConSize, coordScreen, &cCharsWritten);

FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);

//получение дескриптора стандартного устройства вывода

HANDLE wHnd = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTitle(L”Demo Console Cls”); // Заголовок консоли cout << “Hello
“;

system(“pause”); // Наблюдаем текст в окне

cls(wHnd); // очищаем окно int iKey = 67;

// Цикл до тех пор пока не нажата клавиша ESC

Последняя строка приведенного кода выполняет задержку до тех пор, пока не будет нажата клавиша ESC, имеющая код 27. Функция _возвращает истину, если нажата какая – либо клавиша на клавиатуре. В противном случае возвращается 0. При этом код нажатой клавиши не удаляется из входного буфера. Если клавиша была нажата, то функция читает ее код в переменную без отображения в консоли.

:/>  How to Log off Computer or Sign out from Windows 11/10

В следующем примере мы управляем положением прямоугольника с помощью клавиатуры. Точнее, мы рисуем прямоугольник такой, как в примере 3, и сдвигаем его с помощью клавиш управления курсором.


Dolya_po_C++

Пришло время засучить рукава и сесть за компьютер, по которому вы так истосковались! Хотите ли взглянуть одним глазком в столь желанное завтра? — я покажу вам ваши будущие программы. Не удивляйтесь, мы познакомимся с программами, которых ещё нет. Вернее, ознакомимся не с программами, а с их . Что такое интерфейс? Знакомое слово, не так ли?

Что такое интерфейс?

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

Кому не знаком удобный и красивый оконный интерфейс? Здесь к услугам пользователя даны меню, кнопочки и прочие удобные штучки. А чем отвечает компьютер? Да чем угодно! Ответом могут быть и текст, и картинки, и даже подвижные изображения: фильмы, мультики.

Но сейчас, до сотворения наших первых программ, я познакомлю вас с другим интерфейсом — . Что это за интерфейс, откуда он взялся и чем хорош?

Консольный интерфейс

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

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

Впрочем, консольные операционные системы не исключают развитых оконных интерфейсов. Напомню, что оконные «коммандеры» и «навигаторы»


Dolya_po_C++

появились в консольной . Со временем и вы научитесь создавать оконные программы.

Прикосновение к консольному интерфейсу

Теперь испытаем консольный интерфейс «на ощупь», обратившись к консольному интерфейсу вашей операционной системы. Однако ж, где найти его среди многочисленных окон? Воспользуйтесь пунктом главного меню, который в ранних версиях назывался «Сеанс MS-DOS», а в более поздних — «Командная строка». Итак, для вызова окна консоли обратитесь в главное меню

Пуск Программы Стандартные Командная строка

Пуск Программы Стандартные Сеанс

Щелчок на этом пункте вызовет окно, похожее на это (рис. 3).

Рис. 3 – Окно командной строки (консольное окно)

Здесь выведено название текущей папки с угловой скобкой в конце. Эта строка с «уголком» называется строкой приглашения. Курсор, мигающий после угловой скобки, предлагает вам ввести какую либо из команд операционной системы. Таких команд насчитывается несколько десятков, их полное описание можно найти в справке по . Сейчас испытаем три из них: — распечатка каталога, — очистка экрана, и — выход из окна консоли.

Напечатайте с позиции курсора слово (большими или маленькими буквами — не важно) и нажмите клавишу . Эта команда заставит систему распечатать информацию о файлах текущей папки. На моем компьютере я увидел вот что (рис. 4).


Dolya_po_C++

Рис. 4 – Распечатка содержимого текущей папки командой DIR

Выполнив команду, операционная система снова выводит «уголок», приглашая напечатать следующую команду. При желании повторите команду ещё пару раз. А теперь введите команду (очистка экрана), — в результате окно консоли очистится, и будет видна лишь строка приглашения. Наконец подача команды (выход) закроет консольное окно, и на этом сеанс завершится.

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

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

— вывод версии операционной системы;

— распечатка характеристик памяти;

— распечатка дерева каталогов;

— вывод списка всех команд операционной системы.

Здесь на время прервём знакомство с консольным интерфейсом, и вернемся к нему в главе 5, где напишем свою первую программу.


Dolya_po_C++

А почему не «окна»?

Читатели, наслышанные о таких мощных визуальных средах программирования как и , обязательно спросят: почему бы нам не воспользоваться этими инструментами? Ведь создавать красивые оконные приложения в том же очень интересно и не так уж сложно!

Да, творить окошки с кнопочками в IDE на первый взгляд просто. Но эта простота скрывает непостижимые для новичка механизмы событийного и объектного программирования. А мы ведь договорились не прыгать по верхушкам,

— оставим это развлечение «чайникам». Это первое.

Открою и второй умысел. Рассмотренный нами консольный интерфейс применяется для ввода и вывода данных не только на экран, но и в текстовые файлы. Профессионал обязательно соприкоснётся с такими файлами, а вы ведь будущий профессионал, не так ли? К тому же, с текстовыми файлами имеют дело и участники школьных олимпиад, в которых вы наверняка пожелаете сразиться.

Итоги

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

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


Dolya_po_C++

Г Л АВ А

Консольное приложение

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

В операционной системе консольное приложение работает в окне командной строки (рис. 6.1), которое часто называют окном консоли.

Консольные приложения удобны для решения задач, в которых не предъявляется особых требований к интерфейсу. Они широко используются для решения системных задач. Следует обратить внимание, что многие утилиты Microsoft . NET Framework реализованы как консольные приложения.

Консольное приложение может вывести информацию на экран и получить данные с клавиатуры одним из трех способов:

при помощи функций (вывод) и (ввод);

вывести информацию в поток вывода (), прочитать данные из потока ввода ();

при помощи методов и объекта .

Основным способом взаимодействия с пользователем в консольных . NETприложениях, созданных Microsoft Visual Studio, является использование объекта . Методы, обеспечивающие отображение и ввод данных, перечислены в табл. 6.1.

Метод выводит на экран (в окно консоли) строку, указанную в качестве параметра метода.

Если надо вывести значение числовой переменной, то для преобразования числа в строку следует использовать метод . Вид (формат) строки, возвращаемой методом , определяет параметр, указанный в инструкции вызова метода (табл. 6.2).

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

После выполнения инструкции курсор остается в той позиции экрана, в которую он переместился после вывода последнего символа строки. Следующая инструкция начинает вывод с той позиции экрана, в которой находится курсор.

Метод отличается от метода тем, что после вывода строки курсор автоматически переходит в начало следующей строки.

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

Метод объекта обеспечивает ввод с клавиатуры строки символов. Для преобразования введенной строки в данные необходимо использовать соответствующие методы преобразования: ,

System::ToInt32()System::ToSingle()System::ToDouble() и т. д.

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

символом, при стандартной для России настройке операционной системы, является запятая).

В качестве примера использования методов объекта в листинге 6.1 приведена программа пересчета цены из долларов в рубли.

Листинг 6.1. Консольное приложение

// ввод данных

usd = Convert::ToSingle(st);

rub = usd * k;

// вывод результата

Console::WriteLine(); // пустая строка

st = usd. ToString(“f”) + “USD = ” + rub. ToString(“c”); Console::WriteLine(st);

// чтобы окно не исчезло с экрана

Командная строка (окно консоли)

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

Во многих системах Windows, включая и Windows 7, имеются два командных процессора: консоль команд (процесс cmd.exe) и окно сессии MS-DOS (процесс ntvdm.exe), которое можно открыть, введя в окне Выполнить (Run) строку . У этих окон разные заголовки и строки подсказки (prompt) и разные возможности. Чаще всего используется командная строка, ее окно мы и рассмотрим далее.

:/>  Everything About DiskPart Commands in Windows 10

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

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

Окно консоли и окно сессии MS-DOS настраиваются совершенно одинаково. Более того, аналогичные параметры имеет и окно оболочки Windows PowerShell.

Флажок Выделение мышью (Quick Edit Mode) на вкладке Общие (Options) разрешает быстрое копирование и вставку символов в командной строке при помощи мыши. Удерживая нажатой левую кнопку мыши, можно выделить часть окна, а при нажатии правой кнопки выполнить запись в буфер выбранных символов или части экрана или, наоборот, вставить символы (строки) из буфера. Во всех последних версиях Windows эта удобная опция по умолчанию выключена.

У окна консоли имеется память команд. Если нажать клавишу F7, то появится окно, где будут перечислены все ранее введенные команды. Можно стрелками клавиатуры выбрать любую команду и выполнить, нажав клавишу Entег; если нажать стрелку Влево или Вправо, то выбранная строка копируется в окно консоли; клавиша Esc закрывает окно без всяких действий. Эта функция очень удобна, особенно когда часто используются «длинные» команды с большим количеством параметров. Предыдущую и последующую команды легко выбрать, просто нажимая клавиши со стрелками Вверх и Вниз, однако когда повторяются несколько команд, буфер команд значительно удобнее и нагляднее. Опции буфера также можно изме­нить на вкладке Общие (Options).

Обратите внимание на вкладку Расположение (Layout). Здесь задаются следующие важные параметры: Размер буфера экрана (Screen Buffer Size), Размер окна (Window Size) и Положение окна (Window Position).

По умолчанию количество строк буфера выставлено как 300. Это нормально при работе с системными командами и утилитами, однако при запуске некоторых устаревших полноэкранных приложений с DOS-окном окно команд­уй строки может растянуться на все разрешенные 300 строк. Поэтому перед запуском программы лучше выбрать приемлемый размер окна консоли. В обычных условиях удобно иметь достаточно большой буфер экрана и увеличить стандартные значения — как по высоте, так и по ширине (некоторые команды или выводимые строки бывают весьма длинными, и неудобно, ко­гда их окончания часто переносятся на другую строку).

При использовании русского языка или при работе в локализованной версии системы для корректного отображения символов в окне командной строки большое значение могут иметь шрифт консоли и кодировка символов (ис­пользуемая кодовая страница (code page)). Эту информацию можно получить с помощью команды . По умолчанию в русских версиях использу­ется кодовая страница 866. Как можно видеть, одна строка (Unknown: . . .), выводимая утилитой , отображается неверно (нечи­таема). Однако если поменять кодовую страницу на 1251, то та же строка вы­водится нормально. Но такое становится возможным, если только стандартный точечный шрифт, используемый по умолчанию (см. вкладку Шрифт (Font) в окне свойств командной строки), поменять на любой TrueType-шрифт (Consolas или Lucida Console).

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

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

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

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

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

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

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

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

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

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

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


Dolya_po_C++

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

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

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

:/>  Скачать Windows 10 для людей с ограниченными возможностями 2019 Home 1709 x64 JAWS 18 торрент

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

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

Начиная с Windows 7, на системном уровне функционал консоли был вынесен из диспетчера и оформлен в качестве самостоятельных исполняемых образов:

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

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

Стандартные потоки (дескрипторы консоли)

На программном уровне для ввода/вывода информации консольные приложения используют три основных стандартных устройства ввода-вывода:

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

Стандартный поток — объект процесса консольного приложения, предназначающийся для организации обмена данными.

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

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

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

Буфер ввода

Каждое консольное приложение имеет буфер вводимых данных:

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

Буфер вывода (экранный буфер) и окно

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


Dolya_po_C++

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


Dolya_po_C++

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

Активный экранный буфер (active screen buffer) ― тот, который отображается на экране.

Вновь созданный экранный буфер не активен до тех пор, пока его дескриптор не будет определен через вызов функции .

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

Основные свойства экранного буфера:

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

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

При создании экранного буфера он заполнен символами пробела, курсор буфера видим на консоли и находится в начале координат буфера (позиция X,Y: 0,0), отображаемое окно консольного приложения устанавливается в верхний левый угол буфера (в начало его координат). Для получения текущих значений разнообразных свойств экранного буфера консоли, используйте функции , и .

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

Курсор

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

Атрибуты символов

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

Как мы видим, одна часть структуры является кодом символа (Unicode или ASCII, в зависимости от настроек кодовой страницы), другая же часть определяет атрибуты. Под атрибуты знакоместа выделяется слово (два байта). Младший байт атрибутов, в свою очередь, описывает цвет текста и фона ячейки (знакоместа): 4 старших бита – фон, 4 младших бита – символ. Приложение может комбинировать константы цвета текста и фона, которые я привел в таблице, чтобы получать разные цвета:

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

Если фон символов экранного буфера не задан, то фон по умолчанию черный, если константа цвета текста не определена, то текст по умолчанию белый.

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