У меня есть приложение, позвольте мне его 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) */
}
}