Существует несколько подходов для приложений, которым необходимо выбрать, будут ли они работать в качестве консольных или графических приложений в зависимости от контекста в Windows:
- Имеет два отдельных приложения и одно условно запускает другое.
- Вариант вышеупомянутой стратегии имеет два приложения, одно из которых называется «app.com» (т.е. просто переименовывает консольный EXE-файл с расширением COM), а другое - «app.exe», так что вызовы командной строки найдут app.com первым. Из-за древней совместимости с DOS исполняемые файлы .COM были найдены раньше .EXE. (Это настраивается в Windows; см. Переменную среды PATHEXT.)
- Техника rxvt / Cygwin, которую я не видел нигде в документации.
Позвольте мне немного подробнее рассказать о том, как работает rxvt на Cygwin. Rxvt - это эмулятор терминала, который обычно работает в системе X Window. Из-за ограничений консоли Win32 Cygwin упаковывает ее как более полнофункциональную консоль с поддержкой таких вещей, как множество строк истории, динамическое изменение размера, настраиваемые шрифты для каждого экземпляра и цветовые темы, выбор мыши без блокировки приложения и копирование, и т. д. Для того, чтобы работать в Windows изначально, rxvt, поставляемый с Cygwin, включает в себя крошечную библиотеку оболочки X11 для Win32. Rxvt в Windows на самом деле является консольным приложением по причинам совместимости с существующими собственными исполняемыми файлами Win32, но в большинстве случаев вы никогда не увидите консоль; вы просто видите само окно эмулятора терминала rxvt.
Способ его работы специально реализован в rxvt/W11/wrap/wrap.c
в дереве исходных текстов rxvt, в функции под названием hideConsole()
. По сути, он открывает консоль (с CreateFile("CONOUT$" ...)
) и проверяет, находится ли позиция курсора в (0,0) (используя GetConsoleScreenBufferInfo()
на дескрипторе консоли).
Если это так, то это означает, что оно было запущено как отдельное приложение, а не из родительского приложения консоли, и, таким образом, оно знает, что ОС создала выделенную консоль Win32 для процесса. Он продолжает скрывать это консольное окно, но сначала его нужно найти. Он использует SetConsoleTitle
, чтобы установить для заголовка окна консоли уникальное значение на основе имени приложения и текущего идентификатора потока. Затем он использует FindWindow
для поиска дескриптора этого окна (периодически Sleep
в течение нескольких мс, если необходимо изменить заголовок, поскольку консольные окна фактически полностью управляются другим процессом в Windows). Когда он в конце концов находит дескриптор окна, он скрывает его с ShowWindowAsync
, передавая SW_HIDE
.
Используя этот подход, вы можете написать приложение, которое:
- если запущен из родительской консоли, он может продолжать использовать эту консоль
- если запущено как приложение, оно может по выбору выбрать, скрывать ли консоль
Единственным недостатком является очень короткая вспышка окна консоли при запуске приложения.