2010-10-26 9 views
14

Tengo una aplicación, llamémosla myapp.exe, que es consola/GUI de modo dual, construida como/SUBSYSTEM: WINDOWS (Hay un pequeño 3KB myapp.com cuña para causar cmd.exe a esperar para mostrar la nueva petición)Donde se escribe en stdout cuando se lanza desde un shell cygwin, sin redirección

Si pongo en marcha de una línea de comandos:.

  • myapp -> cmd.exe corre myapp.com que corre miaplicacion. exe. stdout es inicialmente una consola separada, usando AttachConsole y freopen("CONOUT$", "w", stdout) mi salida aparece en el cuadro de comando. OK
  • myapp.exe -> cmd.exe muestra el mensaje demasiado pronto (problema conocido), de lo contrario igual que el anterior. No es un escenario de uso normal.
  • myapp > log -> stdout es un archivo, el uso normal de std::cout termina en el archivo. Aceptar

Si lanzo desde Windows Explorer:

  • myapp.com - se crea> consola, la salida estándar es la consola, la salida va en la consola. El mismo resultado que usar/SUBSYSTEM: CONSOLE para todo el programa, excepto que he agregado una pausa cuando myapp.com es el único proceso en la consola. No es un escenario de uso normal.
  • myapp.exe -> stdout es un controlador NULL, lo detecto y engancho std::cout en una GUI. Aceptar

Si pongo en marcha a partir de cáscara de Matlab:

  • system('myapp') o system('myapp.com') o system('myapp.exe') -> Para las tres variantes, la salida estándar se canaliza a Matlab. Aceptar

Si pongo en marcha de un shell bash Cygwin:

  • ./myapp.com -> Al igual que el lanzamiento de cmd.exe, la salida aparece en el cuadro de mando. OK
  • ./myapp -> (bash encuentra ./myapp.exe). Este es el caso roto. stdout es un controlador no NULL pero la salida no va a ninguna parte. ¡Esta es la situación normal para ejecutar el programa desde bash y necesita ser reparado!
  • ./myapp > log -> Al igual que el inicio desde cmd.exe con la redirección de archivos. OK
  • ./myapp | cat -> Similar a la redirección de archivos, excepto que la salida termina en la ventana de la consola. Aceptar

¿Alguien sabe lo que cygwin conjuntos como la salida estándar en el lanzamiento de un/SUBSISTEMA: proceso y procedimientos de Windows que se puede unir std::cout a ella? ¿O al menos dime cómo averiguar qué tipo de identificador recibo de GetStdHandle(STD_OUTPUT_HANDLE)?

Mi programa está escrito con Visual C++ 2010, sin /clr, en caso de que importe de alguna manera. El sistema operativo es Windows 7 de 64 bits.

EDITAR: Se solicitó información adicional.

La variable de entorno CYGWIN está vacía (o no existe).

GetFileType() devuelve FILE_TYPE_UNKNOWN. GetLastError() devuelve 6 (ERROR_INVALID_HANDLE). No importa si lo compruebo antes o después de llamar al AttachConsole().

Sin embargo, si simplemente ignoro el identificador no válido y freopen("CONOUT$", "w", stdout) todo funciona bien. Me faltaba una manera de distinguir entre la salida de la consola (reventada) y la redirección de archivos, y GetFileType() siempre que.

EDIT: código final:

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) */ 
    } 
} 
+0

¿Eso es con la consola Cygwin predeterminada (iniciada a través del acceso directo "Cygwin Bash Shell")? ¿Tiene algo en la variable de entorno CYGWIN, en particular 'tty'? 'GetFileType()' puede decirle qué tipo de manejador está tratando. – ak2

+0

@ ak2: 'GetFileType' era solo el ticket. Si haces una respuesta, la aceptaré. –

+0

Tuve un comportamiento similar usando cygwin en Console2, pero estaba bien usando el acceso directo "Cygwin64 Terminal". Console2 tenía su shell configurado en 'C: \ cygwin64 \ bin \ zsh.exe --login -i -c" cd ~; exec/bin/zsh "' –

Respuesta

3

La función GetFileType() permite distinguir entre algunos tipos de mangos, en particular, consolas, tuberías, archivos y mangos rotos.

Cuestiones relacionadas