Чем отличается консольное приложение от приложения windows

Обновлено: 01.07.2024

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

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

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

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

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

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

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

консоль cmd

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

Консольные приложения являются одним из типов исполняемых образов (приложений) 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 и консольных приложений;

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

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

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

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

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

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

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

    Традиционно, со стандартным вводом ассоциирована клавиатура, а со стандартным выводом/ошибкой ассоциирован монитор (экран), таким образом вывод печатных символов в 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 .

    Буфер ввода

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

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

    какие существуют различия между Windows и консольными приложениями ?

    при создании нового проекта в Visual C++ он просит выбрать любой из вышеперечисленных .

    единственное отличие заключается в том, что консольное приложение всегда порождает консоль, если она не запущена с одного (или консоль активно подавляется при запуске). С другой стороны, приложение windows не порождает консоль. Это can еще прикрепить к нет консоли или создать новый, используя AllocConsole .

    это делает приложения Windows лучше подходят для приложений GUI или фоновых приложений, потому что вы обычно не хотите иметь окно терминала созданный для тех.

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

    определить и изменить тип подсистемы применение, вам нужно прочитать части заголовка ПЭ. Однако адрес данных подсистемы не является фиксированным, поскольку он является частью необязательного заголовка файла, позиция которого определяется адресом, хранящимся в заголовке файла DOS (в элементе e_lfanew ). Этот адрес фактически указывает на _IMAGE_NT_HEADERS запись, которая, в свою очередь, включает в себя IMAGE_OPTIONAL_HEADER32 структура. Это int16 1) член Subsystem . Значение элемента равно 2 для приложения Windows и 3 для консоли приложение. Существуют и другие подсистемы (в частности, POSIX и kernel). Я написал небольшое приложение VB6 для изменения подсистемы приложения, которое можно загрузить из ActiveVB в исходном коде.

    формат PE не очень хорошо документирован, но этот документ может служить введением:Peering Inside the PE: a Tour of the Win32 Portable исполняемый файл формата.

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

    помимо разницы, упомянутой Конрадом, консольные и Windows-приложения ведут себя по-разному при интерактивном вызове из командной строки:

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

    Это неверно для пакетных файлов; они всегда будут ждать выхода приложения. (Вы всегда можете использовать чтобы запустить приложение без ожидания.)

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

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

    насколько кишки приложение идет, они по существу то же самое. Основное различие добавляется на этапе компиляции.


    В чем разница между Windows и консольными приложениями?

    При создании нового проекта в Visual C ++ предлагается выбрать одно из вышеперечисленных.

    Единственное отличие состоит в том, что консольное приложение всегда порождает консоль, если оно не запускается с нее (или консоль активно подавляется при запуске). С другой стороны, приложение Windows не порождает консоль. Это Можно по-прежнему подключитесь к существующей консоли или создайте новую, используя .

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

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

    Чтобы определить и изменить тип подсистемы приложения, вам необходимо прочитать части заголовка PE. Однако адрес данных подсистемы не фиксирован, поскольку он является частью необязательного заголовка файла, положение которого определяется адресом, хранящимся в заголовке файла DOS (в элементе ). Этот адрес фактически указывает на запись , которая, в свою очередь, включает структуру . Это 1) элемент с именем . Значение элемента - 2 для приложения Windows и 3 для консольного приложения. Существуют и другие подсистемы (в частности, POSIX и ядро). Я написал небольшое приложение VB6 для изменения подсистемы приложения, которое можно загрузить с ActiveVB в качестве исходного кода.

    Формат PE не очень хорошо документирован, но этот документ может служить введением: Пиринг внутри PE: Обзор переносимого формата исполняемых файлов Win32.

    1) На самом деле это не противоречит моему утверждению, что отличается только один байт: старший байт этого члена всегда равен 0. Меняется только младший байт.

    • 1 Нет ли различий и в ссылках DLL?
    • @SoapBox: см. Обновленный ответ. Адрес не фиксированный, его нужно вычислить. Я разместил ссылку на код VB6, чтобы прочитать эти данные. В статье MSDN также показано, как это сделать.
    • Мой отец просто заменял компоновщик специальной программой, которая изменяла командную строку компоновщика в соответствующем файле, а затем запускала на нем фактический компоновщик. Конечный результат, конечно, такой же, как и переключение байта в исполняемом файле :)

    Помимо различия, упомянутого Конрадом, консольные и Windows-приложения ведут себя по-разному при интерактивном вызове из командной строки:

    Когда вы запускаете консольное приложение, командная строка не возвращается, пока консольное приложение не завершится. Когда вы запускаете приложение Windows, команда немедленно возвращается.

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

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

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

    Что касается внутренней части приложения, по сути, они одинаковы. Основное отличие добавляется на этапе компиляции.

    Вы можете изменить подсистему с помощью EDITBIN.exe (запись MSDN в EDITBIN.exe)

    • Было бы полезно процитировать важные части ссылки, если она больше не работает.

    Консольное приложение запускается из командной строки Windows (start / run / cmd)

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

    Точка входа для консольных приложений - (начиная с Visual Studio 2008 IIRC), , если вы отключили Unicode по умолчанию. Для настольных - /.

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

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

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

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

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

    Примеры работы с командной строкой см. в статье Кое что о ДОС.

    Оконные приложения

    Оконное приложение - это привычная всем программа Windows. То есть это окошко с разными кнопочками и полями для ввода-вывода данных. На сегодняшний день это, пожалуй, самый распространённый вид программ. Именно оконные приложения создают большинство программистов.

    Драйверы

    Драйвер - это программа, которая обычно служит для “стыковки” компьютерного железа (например, видеокарты) с операционной системой или другой программой. Иногда драйвером называют программу, которая “стыкует” две других программы между собой. Хотя сейчас такие программы принято называть интерфейсами (например, COM-интерфейс или DDE-интерфейс).

    Когда я говорю “стыкует”, то я имею ввиду, что драйвер позволяет организовать правильный обмен данными между компьютерным железом и ОС. То есть операционная система обращается не напрямую к железу, а через драйвер.

    Зачем так сделано? Дело в том, что производителей, например, видеокарт, существует огромное количество. И все они делают их по своим внутренним стандартам. И операционная система не может знать всё обо всех видеокартах. Поэтому есть определённые общепринятые стандарты, которые поддерживаются операционной системой. И производитель “железа” делает какую угодно “железяку”, а затем просто пишет программу-драйвер, которая соответствует общепринятым стандартам и “стыкует” эту “железяку” с операционной системой.

    Это позволяет извращённым умам делать разные смешные штуки. Например, можно написать “кривой” драйвер, который будет определять USB-мышку как флэшку. Конечно, работать такая “флэшка” не будет, но зато будет прикольно)))

    Интерфейсы

    Интерфейс - это программа, которая обычно служит для “стыковки” одной программы с другой. Например, вы хотите получить данные из чужой программы. Как это сделать? Если чужая программа поддерживает какой-нибудь стандартный интерфейс (например, DDE), то вы можете использовать этот интерфейс для получения данных из чужой программы.

    Библиотеки

    Библиотека - это двоичный файл, который хранит разные методы и объекты. Пока это вам ни о чём не говорит. Но просто знайте, что свои процедуры вы можете сохранить в библиотеку, а затем использовать её в других своих программах. Также вы можете использовать в своих программах чужие библиотеки и наоборот - распространять свои библиотеки, чтобы другие программисты могли их использовать в своих программах.

    Удобство использования библиотек заключается в том, что они не привязаны к языку программирования. Например, вы можете написать библиотеку на языке С++, а использовать её потом в программах, которые пишите на Паскале.

    Резидентные программы

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

    Системные программы

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

    Пока на этом всё. Домашнего задания не будет. Просто подумайте о том, как огромен мир программирования, и сколько всего вам надо будет ещё изучить)))

    Читайте также: