С такой задачей довольно часто приходится иметь дело начинающим программистам. И нередко она вызывает серьёзные затруднения, несмотря на то, что принцип её решения предельно прост.
Основная проблема чаще всего кроется в ещё недостаточном понимании структуры как консольных таки оконных программ.
Консольную программу, в большинстве случаев, можно условно разделить на три части выполняемые последовательно.
- Ввод данных;
- Выполнение тех или иных действий;
- Вывод результатов на экран консоли.
В оконной программе первая и третья части объединены в оконном интерфейсе, а вторая часть выполняется при возникновении определённого события (например, нажатие кнопки).
Для наглядности рассмотрим следующий простой пример.
Есть консольная программ, которая перемножает два числа введённые пользователем и выводит результат умножения. Ниже приведён её код на Pascal и C++.
Delphi/Pascal
a , b , c : real ;
int _tmain ( int argc , _TCHAR * argv [ ] ) <
double a , b , c ;
// 1я часть. Ввод данных
// 2я часть. Собственно алгоритма программы
// 3я часть. Вывод результата
Subnautica КОНСОЛЬНЫЕ КОМАНДЫ
Необходимо переделать эту консольную программу в оконную.
Реализацию оконной версии выполним в Delphi и C++ Builder.
Так как программа принимает на вход два числа и возвращает результат в виде одного числа, в окне должны быть два компонента исходных чисел (допустим, поля для ввода Edit) и один компонент для вывода результата (допустим, метка Label).
Для того чтобы дать программе команду выполнить вычисления можно задействовать обычную кнопку. При нажатии на неё оба исходных числа будут перемножены, а результат выведен в Label.
Внешний вид окна показан на скриншоте ниже.
Для большей наглядности можно добавить два дополнительных компонента Label, которые будут служить подписями для компонентов Edit.Однако в силу того, что при перестановке множителей произведение не изменяется, наличие или отсутствие этих компонентов, а также их взаимное расположение на работу пользователя с программой никак не повлияют.
Теперь необходимо обработать событие нажатия на кнопку.
В обработчике нужно преобразовать текст из полей вода в числа, затем перемножить их и вывести результат.
Источник: streletzcoder.ru
Консольное приложение на ассемблере
В процессе изучения основных правил работы оконных приложений, мы познакомились с некоторыми принципами программирования графических примитивов. Теперь самое время открыть для себя еще одну разновидность приложений под Windows — консольные приложения.
Во времена разработки первых операционных систем, в них не было такого понятия как графический интерфейс пользователя, был доступен только текстовый режим видеоадаптера с интерфейсом командной строки. Однако, в ходе развития ОС был создан графический интерфейс пользователя (GUI), роль которого со временем существенно возросла. Как раз во время создания GUI стало очевидно, что необходимость в консольных приложениях сохраняется, следствием чего было создание консоли Windows . Консоль, в зависимости от ОС, может работать как в привычных по старым операционным системам текстовых режимах (они еще поддерживаются на аппаратном уровне в современных видеоадаптерах), так и в режимах эмуляции текстового режима. Основная причина по которой консоль в Windows была выделена в отдельную разработку и продолжает своё существование, заключается в следующем:
Крутые команды консоли windows которые вы должны знать
Большое количество системных утилит, входящих в состав операционной системы, используют исключительно текстовый интерфейс и рассчитаны на использование аргументов командной строки.
Как вы знаете, большинство упомянутых системных утилит мигрировали к нам из ранних версий операционной системы Windows, куда они, в свою очередь, заимствовались как из той же MSDOS (фактически предка Windows), так и из сторонних операционных систем. Соответственно, в старых ОС, до определенного времени, интерфейс взаимодействия с пользователем (командная строка) ограничивался текстовым режимом, по этой причине и разрабатывались утилиты исключительно под него.
Казалось бы, при переходе операционных систем к графическому режиму, стоило бы переписывать и системные утилиты под новые реалии? Тем не менее это не имело ни малейшего смысла, поскольку системные утилиты представляют собой обособленную группу программ, предназначенных для работы в командных сценариях, своеобразных программах, которые используют простые языки описания действий, в которых вывод одной программы может поступать на вход другой. То есть вся работа зачастую ведется с минимальным выводом статусных сообщений, либо и вовсе без какого-либо визуального оповещения пользователя. Очевидно, что использование графического интерфейса тут явно не к месту (избыточно). Описанная выше так называемая «преемственность» консольных утилит имеет под собой ряд весомых причин:
- возможность (для взаимодействия) использовать стандартные потоки ввода-вывода (будут описаны ниже).
- наличие потоков позволяет использовать консольные утилиты в сценариях (скриптах), где выходные потоки одних утилит могут перенаправляться во входные потоки других.
- оптимизация приложения по скорости выполнения (скорости вывода информации) и потреблению системных ресурсов.
- удобство программирования, обусловленное простотой и небольшим набором функций обслуживания консоли.
Помимо класса консольных утилит, в практике разработчика часто возникают задачи, в которых разворачивать оконный графический интерфейс соразмерно напрасной трате своего и процессорного времени. В подобного рода задачах вполне достаточным условием является использование текстового (для вывода диагностических сообщений) или вовсе неинтерактивного (вывод данных в файл) режимов. Действительно, зачем пытаться изображать графический интерфейс там, где он явно излишен или вовсе не обязателен, не проще ли в таких задачах от него отказаться вовсе? Поэтому, сегодня мы акцентируем внимание на особенностях языка Ассемблера (FASM) при написании консольного приложения на ассемблере под Windows, и темой данной статьи будет создание серии простейших консольных приложений, демонстрирующих основные алгоритмы взаимодействия с консолью. Разработанные шаблоны (в примерах) могут быть в дальнейшем использованы в качестве базовых в различного рода проектах.
Итак, в словосочетании консольное приложение на ассемблере присутствует ключевое слово «консоль», на которое стоит обратить особое внимание, поскольку именно оно даст нам понимание основных принципов работы. Скорее всего, люди уже знакомые с компьютером в общем и операционными системами в частности, знают что это такое, хотя могут понимать это в широком, так сказать, смысле этого слова: механизм для ввода информации с клавиатуры и вывода ее на экран. Поэтому, не лишним будет дать серию расширенных определений:
Текстовый пользовательский интерфейс (Text user interface, TUI / Character-Mode User Interface, CUI) — разновидность интерфейса, использующая для ввода-вывода/представления информации текстовый режим (или его эмуляцию) работы видеоадаптера и набор буквенно-цифровых символов и символов псевдографики.
что является базой для:
Консоль (интерфейс командной строки) — текстовый интерфейс управления консольным приложением , ввод-вывод в рамках которого может осуществляться через стандартные потоки: ввод ( stdin ), вывод ( stdout ), ошибка ( stderr ), а так же прямыми чтением/записью из буферов ввода-вывода. По умолчанию к потоку ввода «подключена» клавиатура (мышь), а к потоку вывода — экран (монитора). В частном случае может быть представлена в виде бесконечной бумажной ленты, прокручивающейся в обе стороны.
что, в свою очередь, формирует понятие:
Консольное приложение (character-mode applications) Windows — класс приложений, использующих для взаимодействия с системой/пользователем объекты консоли: текстовый интерфейс и стандартные потоки ввода-вывода. Считается что консольные приложения управляются консолями .
Консольное приложение операционной системы Windows обеспечивает взаимодействие с пользователем через так называемое окно консоли . Примером подобных консольных приложений могут являться: окно командной строки (cmd), файловые менеджеры (например, Far Commander), и ряд типовых системных консольных утилит:

Различия оконного и консольного приложений
Консольные приложения являются одним из типов исполняемых образов (приложений) Windows, наряду с типовыми оконными (GUI), библиотеками (DLL), драйверами (native) и некоторыми другими. По сути это полноценные приложениями, имеющие ряд специфических отличий:
- Значение поля Subsystem заголовка результирующего PE-файла равно 3 ( IMAGE_SUBSYSTEM_WINDOWS_CUI ). На основании значения данного поля, загрузчик образов при подготовке приложения к выполнению производит свойственную консольным приложениям последовательность загрузки.
- При запуске консольного приложения, загрузчик образов пытается наследовать консоль от процесса-родителя (приложение, из-под которого был произведен запуск):
- Создается новая консоль, если родительский процесс не имеет консоли.
- Консоль наследуется, если процесс-родитель тоже является консольным приложением (окно командной строки и прочее).
Следует ли из всего вышеперечисленного, что все функциональные методы консольного приложения «заключены» внутри набора текстовых функций и им лишь и ограничиваются? Отнюдь, основная идея заключается в том, что отличие оконного приложения от консольного чисто условное, поскольку консольное приложение сохраняет возможность вызывать GUI-функции (то есть функции, работающие уже с графическими примитивами) программного интерфейса Win32, все зависит лишь от набора подключаемых библиотек. Да, в простейшем случае текстовый интерфейс использует интерфейс командной строки, тем не менее многие приложения могут создавать более дружественный пользователю интерфейс при помощи интерактивных элементов, тем самым приближаясь по удобству к полноценному оконному (графическому).
Ограничение консольных приложений «текстовым» режимом в операционной системы Windows чисто условное, поскольку им доступны все базовые элементы управления, используемые и в графическом интерфейсе: кнопки, списки, меню, переключатели, флажки, полосы прокрутки и тому подобное.
Иначе говоря, программы с текстовым интерфейсом могут имитировать оконный интерфейс. Поэтому можно сделать следующее обобщение: консольные приложения имеют возможность полноценно взаимодействовать с функциями Win32 API наравне с типовым оконным GUI-приложением, ведь в процессе написания исходного кода автор имеет возможность импортировать (подключать) любые функции любых доступных коду библиотек, каковые он сочтет нужными.
Начиная с Windows 7, на системном уровне функционал консоли был вынесен из диспетчера csrss.exe и оформлен в качестве самостоятельных исполняемых образов:
- conhost.exe – обработчик консольных окон режима пользователя (обеспечивает весь функционал работы с консолью);
- condrv.sys – драйвер режима ядра, обеспечивающий взаимодействие conhost и консольных приложений;
Объекты консольного приложения
Выводить текст на консоль, осуществлять ввод символов, а так же совершать любые иные действия с консолью можно лишь ассоциировав с ней некие системные сущности (объекты), посредством которых можно обеспечивать обмен данными. Каждая консоль состоит из следующих основных объектов:
- [единственный] входной буфер — область данных (события/сигналы/данные) для ввода (передачи на консоль);
- [несколько] экранный выходной буфер — область данных (символы/атрибуты) для вывода (отображения на экране);
- Окно консоли — область экрана, отображающая часть выходного буфера;
- Текущая позиция курсора — маркера вывода, обозначающий текущую позицию вывода;
Стандартные потоки (дескрипторы консоли)
На программном уровне для ввода/вывода информации консольные приложения используют три основных стандартных устройства ввода-вывода:
| Стандартный ввод ( stdin ) | Поток данных, идущих в программу. |
| Стандартный вывод ( stdout ) | Поток данных, идущих из программы. |
| Стандартная ошибка ( stderr ) | Поток сообщений об ошибках, идущих из программы. |
Для кода любого консольного приложения стандартные потоки доступны через дескрипторы и используют последние для того, чтобы обратиться к стандартному вводу (буферу ввода данных) и стандартному выводу (экранным буферам) собственной консоли. Хотя этими потоками консольное приложение не ограничено, никто не запрещает ему открывать любые файлы, использовать сетевые соединения и совершать иные действия, доступные в выполняющей их среде.
Стандартный поток — объект процесса консольного приложения, предназначающийся для организации обмена данными.
Традиционно, со стандартным вводом ассоциирована клавиатура, а со стандартным выводом/ошибкой ассоциирован монитор (экран), таким образом вывод печатных символов в STDOUT и STDERR приводит к появлению этих символов на устройстве вывода и к получению их пользователем. В дополнение, потоки могут быть переопределены (перенаправлены) и на другие логические устройства (файл, ввод/вывод другой программы и прочее). Поэтому определение консольного приложения может быть расширено:
Любая программа, получающая данные путём чтения STDIN и передающая данные путём записи в STDOUT , является консольной.
Тем не менее возникает резонный вопрос: обязательно ли наличие окна консоли у консольного приложения? Ведь, теоретически, консольные программы могут обходиться и без классического ввода (с клавиатуры) и вывода (в окно, на экран), поскольку объекты stdin и stdout могут быть связаны с файлами, потоками ввода/вывода других программ или иными объектами операционной системы? Тем не менее, стандартный сценарий использования консольного приложения в Windows подразумевает создание отдельного окна консоли.
В ходе запуска консольного приложения, система генерирует вышеперечисленные дескрипторы для вновь создаваемого процесса консоли. Процесс консольного приложения обычно использует функции GetStdHandle , CreateFile , CreateConsoleScreenBuffer для того, чтобы открыть один из вышеописанных дескрипторов. Функция GetStdHandle обеспечивает механизм получения кодом приложения дескрипторов стандартного ввода, стандартного вывода и стандартной ошибки, связываемых с процессом в момент создания. В случае необходимости имеется возможность переназначить стандартные дескрипторы через функцию SetStdHandle , изменяющую дескрипторы, связанные с STDIN , STDOUT или STDERR .
Стандартные дескрипторы родительского процесса всегда наследуются всеми создаваемыми дочерними процессами, поэтому вызовы функции GetStdHandle дочерними процессами возвращают переназначенный дескриптор. По этой причине, в зависимости от действий родительского процесса, дескриптор, возвращенный функцией GetStdHandle , может сослаться на что-либо, отличное от привычного нам консольного ввода-вывода. К примеру, родительский процесс может при помощи SetStdHandle изменить дескриптор какого-либо потока (например STDIN ) перед созданием дочернего процесса. Затем, когда созданный дочерний процесс у себя в коде вызовет функцию GetStdHandle , он получает дескриптор переназначенного канала. Этим обеспечивается механизм управления родительским процессом стандартными дескрипторами дочернего процесса.
К дескрипторам применяются права доступа. По умолчанию дескрипторы, возвращенные функцией GetStdHandle , имеют доступ GENERIC_READ | GENERIC_WRITE .
Буфер ввода
Каждое консольное приложение имеет буфер вводимых данных:
Входной буфер консоли (Input buffer) — очередь данных, каждая запись которой содержит информацию относительно входного события консоли (нажатие/отпускание клавиш, движение/нажатие/отпускание мыши и прочие).
В момент, когда окно консольного приложения имеет фокус клавиатуры (является активным), консоль оформляет каждое событие ввода (типа нажатия/отпускания клавиши, перемещение указателя мыши или щелчка кнопки мыши) в качестве данных, которые помещаются в буфер вводимых данных консоли. Что такое эти самые данные? Запись данных о вводе ― структура Windows, содержащая в себе информацию о деталях события: тип, источник (клавиатура, мышь, размеры окна, фокус, меню) и прочих. Структура имеет внутренний тип INPUT_RECORD и представляет собой следующее:
Источник: datadump.ru
Возможности консольных приложений для Windows
В настоящее время, когда у разработчиков программного обеспечения появляется всё больше возможностей для сознания приложений со сложным графическим интерфейсом, консольные приложения по-прежнему прочно удерживают свои позиции, даже в такой, казалось бы им среде, как современная операционная система Windows. Это объясняется в первую очередь простотой их исполнения и некоторыми специфическими особенностями консольных приложений, которые делают их в ряде случаев более подходящими для решения задачи, чем приложения с графическим интерфейсом. Один минус: выглядят такие приложения очень уныло и однообразно, как безликая серая масса букв и цифр на чёрном фоне окна консоли. Но не всё так безнадёжно, как может показаться на первый взгляд. В этой статье я попытаюсь дать несколько полезных рецептов расширения функциональности консольных приложений и придания их внешнему виду большей выразительности.
И так: начнём с заголовка окна консоли. При запуске программы в заголовке окна отображается полное имя файла, с которого было запущено приложение. Вместо имени файла, в заголовке окна можно указать любой другой текст: название приложения, например. Это делается с помощью функции SetConsoleTitle.
::SetConsoleTitle(_T(«Пример программы на C++»));
Далее рассмотрим проблему с выводом на консоль текста кириллицы. Практически каждому программисту когда-нибудь приходилось с этим сталкиваться.
По сложившейся традиции, для вывода текста на консоль в большинстве примеров на C++ используются функции стандартной библиотеки, такие как printf или puts, которые работают с текстом в кодировке OEM, что соответствует кодовой странице 866 для русского языка. То же самое происходит при использовании потока вывода cout. Но проблема в том, что большинство текстовых редакторов для Windows работают с текстом в кодировке ANSI, что для русского языка соответствует кодовой странице 1251.
Как решить эту проблему? Проще всего использовать функции, которые записывают текст непосредственно в буфер консоли, а не в стандартный поток. Для этого нужно в программный код включить заголовочный файл CONIO.H и, вместо функций printf и puts, вызывать аналогичные функции _cprintf и _cputs.
Так они выглядят в коде на Visual C++:
_cputts(_T(«Всем привет от меня!rn»)); _tcprintf(_T(«%srn»), _T(«Благодарю за внимание.»));
Несложно написать и собственную процедуру вывода текста на консоль с использованием системной функции WriteConsole. Вот пример такой процедуры:
BOOL PrintText(LPCTSTR szText) < static HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); // DWORD dw(0); return ::WriteConsole(hConsole, szText, ::lstrlen(szText), >
А вот так она используется:
PrintText(_T(«Всем пока. rn»));
Как вы уже наверно заметили, для перевода строки необходимо указывать последовательность из двух символов: «rn», в отличие от стандартного потока, где указывается лишь один символ ‘n’. Но бывает и так, что нужно вывести текст именно через стандартный поток, чтобы его можно было перенаправить в текстовый файл или в другое консольное приложение. Для этого текст нужно перевести в другую кодировку с помощью функции CharToOem. Или использовать функцию WideCharToMultiByte с параметром CP_OEMCP, если ваш текст в кодировке UNICODE.
Размер буфера консоли по умолчанию равен 80х300, т.е. 300 строк по 80 символов каждая. Задать буферу консоли другой размер можно при помощи функции SetConsoleScreenBufferSize.
Атрибутами текста консоли являются: цвет символов и цвет заднего фона. Код атрибута соответствует числовому значению в диапазоне от 0x00 до 0x7F. Так выглядит таблица с числовыми значениями атрибутов текста в шестнадцатеричном коде:
| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 1A | 1B | 1C | 1D | 1E | 1F |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 2A | 2B | 2C | 2D | 2E | 2F |
| 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 3A | 3B | 3C | 3D | 3E | 3F |
| 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4A | 4B | 4C | 4D | 4E | 4F |
| 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 5A | 5B | 5C | 5D | 5E | 5F |
| 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 6A | 6B | 6C | 6D | 6E | 6F |
| 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 7A | 7B | 7C | 7D | 7E | 7F |
Функция SetConsoleTextAttribute задаёт выводимому тексту указанный атрибут. Для задания всему окну консоли указанных атрибутов применяется функция FillConsoleOutputAttribute.
Позиционирование текста (т.е. установка курсора на позицию с заданными координатами) производится с помощью функции SetConsoleCursorPosition.
Полный код моего примера в Visual C++ 2005 выглядит так:
#include «stdafx.h» #include BOOL PrintText(LPCTSTR szText); BOOL SetConsoleAttrib(WORD wAttrib); BOOL SetCurrentPos(SHORT x, SHORT y); BOOL SetConsoleSize(SHORT x, SHORT y); int _tmain(int argc, _TCHAR* argv[]) < ::SetConsoleTitle(_T(«Пример программы на C++»)); // SetConsoleSize(160, 80); SetConsoleAttrib(0x6F); // _cputts(_T(«Всем привет от меня!rn»)); _tcprintf(_T(«%srn»), _T(«Благодарю за внимание.»)); // SetCurrentPos(25, 10); PrintText(_T(«Всем пока. rn»)); // _gettch(); // ожидание нажатия клавиши. return 0; >static HANDLE _ConsoleOut = ::GetStdHandle(STD_OUTPUT_HANDLE); BOOL SetConsoleAttrib(WORD wAttrib) < ::SetConsoleTextAttribute(_ConsoleOut, wAttrib); CONSOLE_SCREEN_BUFFER_INFO csbi = ; ::GetConsoleScreenBufferInfo(_ConsoleOut, DWORD dw(0); COORD cr = ; return ::FillConsoleOutputAttribute(_ConsoleOut, wAttrib, csbi.dwSize.X * csbi.dwSize.Y, cr, > BOOL PrintText(LPCTSTR szText) < DWORD dw(0); return ::WriteConsole(_ConsoleOut, szText, ::lstrlen(szText), >BOOL SetCurrentPos(SHORT x, SHORT y) < COORD pos = ; return ::SetConsoleCursorPosition(_ConsoleOut, pos); > BOOL SetConsoleSize(SHORT x, SHORT y) < COORD size = ; return ::SetConsoleScreenBufferSize(_ConsoleOut, size); >
Приведённый пример был создан в среде Visual C++ 2005 как проект Win32 Console Application.
В заключение хочу отметить, что возможности консольного интерфейса Windows отнюдь не исчерпываются теми функциями, которые были упомянуты в этой статье. Не было рассказано про получение и обработку сообщений от мыши, об использовании в консольных приложениях возможностей графического интерфейса Windows. Но об этом речь пойдёт в дальнейших публикациях.
Источник: codenet.ru