2011-07-13 18 views
15

Tengo una aplicación de GUI, que estoy desarrollando multiplataforma para Linux y Windows. En Linux, todo funciona sin problemas. Sin embargo, me he encontrado con un enganche en Windows. Me gustaría poder registrar ciertos mensajes en la consola con una aplicación GUI en Windows, estilo Linux.Windows GUI + Salida de consola, estilo Linux

Lo que quiero decir con Linux-style es que si el programa se abre desde una consola, la salida irá a la consola, pero si el programa se abre, por ejemplo, a través del menú de inicio, el usuario nunca verá salida de consola Aparentemente, esto es más difícil de lo que parece en Windows.

la actualidad, utilizo el siguiente truco en main():

#if _WINDOWS /* Fix console output on Windows */ 
if (AttachConsole(ATTACH_PARENT_PROCESS)) { 
    freopen("CONOUT$","wb",stdout); 
    freopen("CONOUT$","wb",stderr); 
} 
#endif 

Esto me permite crear de salida antes de una ventana se abrió realmente por el programa, tales como responder a "help" de la línea de comando. Sin embargo, una vez que mi programa inicializa y abre una ventana, se devuelve la consola. Necesito una solución que me permita continuar el acceso a la consola durante toda la vida de mi programa, sin abrir una nueva consola si no se usó originalmente.

+1

relacionados: [INFORMACIÓN: Llamar a rutinas CRT de salida desde una aplicación GUI] (especialmente la sección "más información") (http://support.microsoft.com/kb/105305) – jfs

+0

relacionados: [¿Cómo obtener la salida de la consola en C++ con un programa de Windows?] (http://stackoverflow.com/q/191842/95735) –

Respuesta

0

Recuerdo haber leído algo sobre esto y si mal no recuerdo la solución fue agregar una interfaz gráfica a un proyecto de consola en lugar de agregar una consola a un proyecto de interfaz gráfica porque este último solo se puede hacer abriendo una nueva consola.

0

Creo que debe crear una aplicación de consola y luego verificar quién inició el proceso (probablemente cmd.exe) y dependiendo de eso puede ocultar la ventana de la consola. Luego creas una ventana en ella ... el truco de esto es que la ventana de la consola puede estar abierta por un momento hasta que la escondas y se verá muy fea, supongo. A partir más tarde, la consola no tiene ese problema, pero no sé si el stdout lo redirecciona como lo hace en las aplicaciones de consola o si tiene que configurarlo de alguna manera, o tal vez tiene que redirigir en cada llamada ... no , ¡Debe haber una mejor manera!

2

Utilizamos :: AllocConsole() en lugar de :: AttachConsole y permanece abierto en toda la aplicación. ¿Trata eso?

0

La mejor solución que he encontrado hasta ahora es tener dos ejecutables.

  • program.exe es la aplicación GUI.
  • program.com es una aplicación de línea de comandos auxiliar que genera program.exe y le transfiere E/S estándar. (No es un ejecutable de COM desde DOS, que es sólo un ejecutable PE estándar renombrado; ya .com es antes .exe en el orden de preferencia por defecto de cmd.exe, puede escribir program y se llamará automáticamente program.com en lugar de program.exe si ambos están en el camino.)

con esta configuración, puede escribir program en el símbolo del sistema de Windows, escriba a la salida estándar en program.exe, y que se mostrará correctamente en la consola; no se generarán ventanas de consola cuando abra program.exe desde la GUI.

Aquí es un ejemplo de implementación del programa de ayuda, tomada de Inkscape: http://bazaar.launchpad.net/~inkscape.dev/inkscape/trunk/view/head:/src/winconsole.cpp

El ayudante crea tres tubos, y desova el programa de interfaz gráfica de usuario con CreateProcess, dándole los extremos correspondientes de los tubos. Luego crea tres hilos que copian datos entre los tubos y las E/S estándar del programa auxiliar en un bucle infinito.El asistente se compila como una aplicación de consola (importante): el interruptor -mconsole en MinGW.

/** 
* \file 
* Command-line wrapper for Windows. 
* 
* Windows has two types of executables: GUI and console. 
* The GUI executables detach immediately when run from the command 
* prompt (cmd.exe), and whatever you write to standard output 
* disappears into a black hole. Console executables 
* do display standard output and take standard input from the console, 
* but when you run them from the GUI, an extra console window appears. 
* It's possible to hide it, but it still flashes for a fraction 
* of a second. 
* 
* To provide an Unix-like experience, where the application will behave 
* correctly in command line mode and at the same time won't create 
* the ugly console window when run from the GUI, we have to have two 
* executables. The first one, inkscape.exe, is the GUI application. 
* Its entry points are in main.cpp and winmain.cpp. The second one, 
* called inkscape.com, is a small helper application contained in 
* this file. It spawns the GUI application and redirects its output 
* to the console. 
* 
* Note that inkscape.com has nothing to do with "compact executables" 
* from DOS. It's a normal PE executable renamed to .com. The trick 
* is that cmd.exe picks .com over .exe when both are present in PATH, 
* so when you type "inkscape" into the command prompt, inkscape.com 
* gets run. The Windows program loader does not inspect the extension, 
* just like an Unix program loader; it determines the binary format 
* based on the contents of the file. 
* 
*//* 
* Authors: 
* Jos Hirth <[email protected]> 
* Krzysztof Kosinski <[email protected]> 
* 
* Copyright (C) 2008-2010 Authors 
* 
* Released under GNU GPL, read the file 'COPYING' for more information 
*/ 

#ifdef WIN32 
#undef DATADIR 
#include <windows.h> 

struct echo_thread_info { 
    HANDLE echo_read; 
    HANDLE echo_write; 
    unsigned buffer_size; 
}; 

// thread function for echoing from one file handle to another 
DWORD WINAPI echo_thread(void *info_void) 
{ 
    echo_thread_info *info = static_cast<echo_thread_info*>(info_void); 
    char *buffer = reinterpret_cast<char *>(LocalAlloc(LMEM_FIXED, info->buffer_size)); 
    DWORD bytes_read, bytes_written; 

    while(true){ 
     if (!ReadFile(info->echo_read, buffer, info->buffer_size, &bytes_read, NULL) || bytes_read == 0) 
      if (GetLastError() == ERROR_BROKEN_PIPE) 
       break; 

     if (!WriteFile(info->echo_write, buffer, bytes_read, &bytes_written, NULL)) { 
      if (GetLastError() == ERROR_NO_DATA) 
       break; 
     } 
    } 

    LocalFree(reinterpret_cast<HLOCAL>(buffer)); 
    CloseHandle(info->echo_read); 
    CloseHandle(info->echo_write); 

    return 1; 
} 

int main() 
{ 
    // structs that will store information for our I/O threads 
    echo_thread_info stdin = {NULL, NULL, 4096}; 
    echo_thread_info stdout = {NULL, NULL, 4096}; 
    echo_thread_info stderr = {NULL, NULL, 4096}; 
    // handles we'll pass to inkscape.exe 
    HANDLE inkscape_stdin, inkscape_stdout, inkscape_stderr; 
    HANDLE stdin_thread, stdout_thread, stderr_thread; 

    SECURITY_ATTRIBUTES sa; 
    sa.nLength=sizeof(SECURITY_ATTRIBUTES); 
    sa.lpSecurityDescriptor=NULL; 
    sa.bInheritHandle=TRUE; 

    // Determine the path to the Inkscape executable. 
    // Do this by looking up the name of this one and redacting the extension to ".exe" 
    const int pathbuf = 2048; 
    WCHAR *inkscape = reinterpret_cast<WCHAR*>(LocalAlloc(LMEM_FIXED, pathbuf * sizeof(WCHAR))); 
    GetModuleFileNameW(NULL, inkscape, pathbuf); 
    WCHAR *dot_index = wcsrchr(inkscape, L'.'); 
    wcsncpy(dot_index, L".exe", 4); 

    // we simply reuse our own command line for inkscape.exe 
    // it guarantees perfect behavior w.r.t. quoting 
    WCHAR *cmd = GetCommandLineW(); 

    // set up the pipes and handles 
    stdin.echo_read = GetStdHandle(STD_INPUT_HANDLE); 
    stdout.echo_write = GetStdHandle(STD_OUTPUT_HANDLE); 
    stderr.echo_write = GetStdHandle(STD_ERROR_HANDLE); 
    CreatePipe(&inkscape_stdin, &stdin.echo_write, &sa, 0); 
    CreatePipe(&stdout.echo_read, &inkscape_stdout, &sa, 0); 
    CreatePipe(&stderr.echo_read, &inkscape_stderr, &sa, 0); 

    // fill in standard IO handles to be used by the process 
    PROCESS_INFORMATION pi; 
    STARTUPINFOW si; 

    ZeroMemory(&si,sizeof(STARTUPINFO)); 
    si.cb = sizeof(STARTUPINFO); 
    si.dwFlags = STARTF_USESTDHANDLES; 
    si.hStdInput = inkscape_stdin; 
    si.hStdOutput = inkscape_stdout; 
    si.hStdError = inkscape_stderr; 

    // spawn inkscape.exe 
    CreateProcessW(inkscape, // path to inkscape.exe 
        cmd, // command line as a single string 
        NULL, // process security attributes - unused 
        NULL, // thread security attributes - unused 
        TRUE, // inherit handles 
        0, // flags 
        NULL, // environment - NULL = inherit from us 
        NULL, // working directory - NULL = inherit ours 
        &si, // startup info - see above 
        &pi); // information about the created process - unused 

    // clean up a bit 
    LocalFree(reinterpret_cast<HLOCAL>(inkscape)); 
    CloseHandle(pi.hThread); 
    CloseHandle(pi.hProcess); 
    CloseHandle(inkscape_stdin); 
    CloseHandle(inkscape_stdout); 
    CloseHandle(inkscape_stderr); 

    // create IO echo threads 
    DWORD unused; 
    stdin_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdin, 0, &unused); 
    stdout_thread = CreateThread(NULL, 0, echo_thread, (void*) &stdout, 0, &unused); 
    stderr_thread = CreateThread(NULL, 0, echo_thread, (void*) &stderr, 0, &unused); 

    // wait until the standard output thread terminates 
    WaitForSingleObject(stdout_thread, INFINITE); 

    return 0; 
} 

#endif