Win32 GUI, которое записывает текст использования в stdout при вызове как "app.exe --help"

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

  • это обычное графическое приложение при вызове без аргументов командной строки
  • указание необязательного аргумента командной строки "--help" заставляет приложение записывать текст использования в stdout, а затем завершать
  • он должен быть одним исполняемым. Нет обмана, создав консольное приложение для выполнения второго исполняемого файла.
  • Предположим, что основной код приложения написан на C/С++
  • бонусные баллы, если окно GUI не создается при указании "-help". (т.е. не мерцание из недолговечного окна)

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

Ответы

Ответ 1

Microsoft разработала консольные и графические приложения, которые будут взаимоисключающими. Этот бит близорукости означает, что идеального решения нет. Наиболее популярный подход состоит в том, чтобы иметь два исполняемых файла (например, cscript/wscript, java/javaw, devenv.com/devenv.exe и т.д.), однако вы указали, что считаете это "обманом".

У вас есть два варианта: создать "исполняемый файл консоли" или "исполняемый файл gui", а затем используйте код, чтобы попытаться обеспечить другое поведение.

  • Исполняемый файл GUI:

cmd.exe будет считать, что ваша программа не делает консольных операций ввода-вывода, поэтому не будет ждать завершения ее перед продолжением, который в интерактивном режиме (то есть не пакет) означает отображение следующей ( "C:\>" ) подсказки и чтение с клавиатуры. Поэтому, даже если вы используете AttachConsole, ваш результат будет смешанным с выходом cmd, и ситуация ухудшается, если вы пытаетесь сделать ввод. Это в основном не стартер.

  • Исполняемый файл консоли:

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

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

Здесь C-код, иллюстрирующий этот подход:

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv[])
{
    if (GetStdHandle(STD_OUTPUT_HANDLE) == 0) // no console, we must be the child process
    {
        MessageBox(0, "Hello GUI world!", "", 0);
    }
    else if (argc > 1) // we have command line args
    {
        printf("Hello console world!\n");
    }
    else // no command line args but a console - launch child process
    {
        DWORD dwCreationFlags = CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS;
        STARTUPINFO startinfo;
        PROCESS_INFORMATION procinfo;
        ZeroMemory(&startinfo, sizeof(startinfo));
        startinfo.cb = sizeof(startinfo);
        if (!CreateProcess(NULL, argv[0], NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &startinfo, &procinfo))
            MessageBox(0, "CreateProcess() failed :(", "", 0);
    }
    exit(0);
}

Я скомпилировал его с cygwin gcc - YMMV с MSVC.

Вторая проблема заключается в том, что при запуске из проводника ваша программа будет на долю секунды отобразите консольное окно. Там нет программного пути, потому что консоль созданный Windows при запуске приложения до его запуска. Единственное, что вы можете сделать это, в вашем установщике, сделать ярлык для вашей программы с помощью команды show SW_HIDE (т.е. 0). Это повлияет только на консоль, если вы не намеренно почитаете поле wShowWindow для STARTUPINFO в вашей программе, поэтому не делайте этого.

Я протестировал это, взломав cygwin "mkshortcut.exe". Как вы достигаете это в вашей программе установки по вашему выбору.

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

Ответ 2

Вы можете использовать AllocConsole() функцию WinApi для размещения консоли для приложения GUI. Вы также можете попробовать подключиться к консоли родительского процесса с помощью AttachConsole(), это имеет смысл, если он уже есть. Полный код с перенаправлением stdout и stderr на эту консоль будет выглядеть следующим образом:

if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()){
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
}

Я нашел этот подход в источниках Pidgin (см. WinMain() в pidgin/win32/winpidgin.c)

Ответ 3

Я знаю, что мой ответ наступает поздно, но я думаю, что предпочтительным методом для ситуации является метод ".com" и ".exe".

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

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

Затем вы можете использовать трюки для того, чтобы этот ".com" был прокси-сервером для stdin/stdout/stderr и запускал файл с одинаковым именем .exe. Это дает поведение, позволяющее программе преформировать в режиме командной строки при вызове формы из консоли (возможно, только при обнаружении определенных аргументов командной строки), при этом все еще можно запускать в качестве приложения GUI без консоли.

Существуют различные статьи, описывающие это как "Как сделать приложение как графическим интерфейсом, так и консольным приложением?" (см. ссылки в ссылке ниже).

У меня был проект под названием dualsubsystem на google code, который обновляет старое решение для этого метода и предоставляет источник кода и исполняемых файлов.

Я надеюсь, что это будет полезно!