Где пишет stdout при запуске из оболочки cygwin, перенаправление

У меня есть приложение, позвольте мне его myapp.exe, который представляет собой двухрежимную консоль/графический интерфейс, построенный как /SUBSYSTEM: WINDOWS (там крошечный 3KB shim myapp.com, чтобы заставить cmd.exe ждать, чтобы отобразить новое строка.)

Если я запускаю из командной строки:

  • myapp → cmd.exe запускает myapp.com, который запускает myapp.exe. Сначала stdout представляет собой отдельную консоль, используя AttachConsole и freopen("CONOUT$", "w", stdout) мой вывод появляется в поле команды. OK
  • myapp.exe → cmd.exe отображает подсказку слишком рано (известная проблема), в противном случае она аналогична предыдущей. Не обычный сценарий использования.
  • myapp > log → stdout - это файл, нормальное использование std::cout заканчивается в файле. OK

Если я запускаю из проводника Windows:

  • myapp.com → консоль создана, stdout - консоль, вывод идет в консоль. Тот же результат, что и использование /SUBSYSTEM: CONSOLE для всей программы, за исключением того, что я добавил паузу, когда myapp.com - единственный процесс в консоли. Не обычный сценарий использования.
  • myapp.exe → stdout является дескриптором NULL, я обнаруживаю это и привязываю std::cout к графическому интерфейсу. OK

Если я запускаю из оболочки Matlab:

  • system('myapp') или system('myapp.com') или system('myapp.exe') → Для всех трех вариантов stdout передается по каналу в MatLab. OK

Если я запускаю из оболочки cygwin bash:

  • ./myapp.com → Как и запуск из cmd.exe, вывод появляется в поле команды. OK
  • ./myapp → (bash находит ./myapp.exe). Это сломанный случай. stdout - это не-NULL-дескриптор, но выход не проходит. Это нормальная ситуация для запуска программы из bash и ее необходимо исправлять!
  • ./myapp > log → Также как запуск из cmd.exe с перенаправлением файлов. OK
  • ./myapp | cat → Аналогично перенаправлению файлов, кроме вывода в окне консоли. OK

Кто-нибудь знает, что cygwin устанавливает как stdout при запуске процесса /SUBSYSTEM: WINDOWS и как я могу привязать к нему std::cout? Или, по крайней мере, скажите мне, как узнать, какую ручку я получаю от GetStdHandle(STD_OUTPUT_HANDLE)?

Моя программа написана с Visual С++ 2010 без /clr, в случае, если это имеет значение. ОС - 64-разрядная версия Windows 7.

EDIT: запрошена дополнительная информация.

Переменная среды CYGWIN пуста (или не существует).

GetFileType() возвращает FILE_TYPE_UNKNOWN. GetLastError() возвращает 6 (ERROR_INVALID_HANDLE). Неважно, проверяю ли я до или после вызова AttachConsole().

Однако, если я просто игнорирую недействительный дескриптор и freopen("CONOUT$", "w", stdout), тогда все отлично работает. Мне просто не хватало возможности различать (выгруженный) вывод консоли и перенаправление файлов, а GetFileType() при условии, что.

EDIT: Конечный код:

bool is_console(HANDLE h)
{
    if (!h) return false;

    ::AttachConsole(ATTACH_PARENT_PROCESS);

    if (FILE_TYPE_UNKNOWN == ::GetFileType(h) && ERROR_INVALID_HANDLE == GetLastError()) {
        /* workaround cygwin brokenness */
        h = ::CreateFile(_T("CONOUT$"), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if (h) {
            ::CloseHandle(h);
            return true;
        }
    }

    CONSOLE_FONT_INFO cfi;
    return ::GetCurrentConsoleFont(h, FALSE, &cfi) != 0;
}


bool init( void )
{
    HANDLE out = ::GetStdHandle(STD_OUTPUT_HANDLE);

    if (out) {
        /* stdout exists, might be console, file, or pipe */
        if (is_console(out)) {
#pragma warning(push)
#pragma warning(disable: 4996)
            freopen("CONOUT$", "w", stdout);
#pragma warning(pop)
        }
        //std::stringstream msg;
        //DWORD result = ::GetFileType(out);
        //DWORD lasterror = ::GetLastError();
        //msg << result << std::ends;
        //::MessageBoxA(NULL, msg.str().c_str(), "GetFileType", MB_OK);
        //if (result == FILE_TYPE_UNKNOWN) {
        //  msg.str(std::string());
        //  msg << lasterror << std::ends;
        //  ::MessageBoxA(NULL, msg.str().c_str(), "GetLastError", MB_OK);
        //}
        return true;
    }
    else {
        /* no text-mode stdout, launch GUI (actual code removed) */
    }
}

Ответы

Ответ 1

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