Куда идут записи в стандартный вывод при запуске из оболочки cygwin, перенаправление отсутствует - PullRequest
14 голосов
/ 27 октября 2010

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

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

  • myapp -> cmd.exe запускает myapp.com, который запускает myapp.exe. Первоначально стандартный вывод - это отдельная консоль, при использовании 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 к GUI. OK

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

  • system('myapp') или system('myapp.com') или system('myapp.exe') -> Для всех трех вариантов стандартный вывод передается в MatLab. OK

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

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

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

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

РЕДАКТИРОВАТЬ: запрашивается дополнительная информация.

Переменная окружения CYGWIN пуста (или не существует).

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

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

РЕДАКТИРОВАТЬ: окончательный код:

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 Ответ

3 голосов
/ 30 октября 2010

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

...