2009-02-21 9 views
36

Tengo acceso a una biblioteca de terceros que hace "cosas buenas". Emite mensajes de estado y progreso a stdout. En una aplicación de consola puedo ver estos mensajes muy bien. En una aplicación de Windows simplemente van al cubo de bits.¿Cómo puedo redireccionar stdout a alguna pantalla visible en una aplicación de Windows?

Hay una manera bastante simple de redirigir stdout y stderr a un control de texto u otro lugar visible. Idealmente, esto no requeriría ninguna recompilación del código de terceros. Simplemente interceptaría los vapores a un nivel bajo. Me gustaría una solución donde acabo #Include la cabecera, llame a la función de inicialización y vincular la biblioteca como en ...

#include "redirectStdFiles.h" 

void function(args...) 
{ 
    TextControl* text = new TextControl(args...); 
    initializeRedirectLibrary(text, ...); 

    printf("Message that will show up in the TextControl\n"); 
    std::cout << "Another message that also shows up in TextControl\n"; 
} 

Aún mejor sería si se utiliza alguna de las interfaces que podría anular por lo que es no vinculado a ninguna biblioteca de GUI en particular.

class StdFilesRedirector 
{ 
    public: 
    writeStdout(std::string const& message) = 0; 
    writeStderr(std::string const& errorMessage) = 0; 
    readStdin(std::string &putReadStringHere) = 0; 
}; 

¿Estoy soñando? ¿O alguien sabe de algo que puede hacer algo como esto?

Editar después de dos respuestas: Creo que usar freopen para redirigir los archivos es un buen primer paso. Para una solución completa, debería haber un nuevo hilo creado para leer el archivo y mostrar el resultado. Para la depuración, hacer un 'tail -f' en una ventana de cgwin shell sería suficiente. Para una aplicación más pulida ... Que es lo que quiero escribir ... habría un trabajo extra para crear el hilo, etc.

+0

'AllocConsole' es la pieza que falta. Produce la ventana de consola familiar. Las funciones de lectura y escritura son incómodas, pero puede redirigir stdout/stdin a/desde él fácilmente usando este método: https://stackoverflow.com/a/15547699/133836 –

Respuesta

16

Puede redirigir stdout, stderr y stdin usando freopen.

Desde el enlace de arriba:

/* freopen example: redirecting stdout */ 
#include <stdio.h> 

int main() 
{ 
    freopen ("myfile.txt","w",stdout); 
    printf ("This sentence is redirected to a file."); 
    fclose (stdout); 
    return 0; 
} 

También puede ejecutar su programa a través de línea de comandos, así:

a.exe > stdout.txt 2> stderr.txt 
5

Cuando se crea un proceso que utiliza CreateProcess() se puede elegir un HANDLE a la que stdout y stderr van a ser escritos. Este HANDLE puede ser un archivo al que dirige la salida.

Esto le permitirá usar el código sin volver a compilarlo. Simplemente ejecútelo y en lugar de usar system() o lo que sea, use CreateProcess().

La MANIJA que le da a CreateProcess() también puede ser la de una tubería que ha creado, y luego puede leer desde la tubería y hacer algo más con los datos.

3

Se podría hacer algo como esto con cout o cerr:

// open a file stream 
ofstream out("filename"); 
// save cout's stream buffer 
streambuf *sb = cout.rdbuf(); 
// point cout's stream buffer to that of the open file 
cout.rdbuf(out.rdbuf()); 
// now you can print to file by writing to cout 
cout << "Hello, world!"; 
// restore cout's buffer back 
cout.rdbuf(sb); 

O bien, puede hacer eso con un std::stringstream o alguna otra clase derivada de std::ostream.

Para redirigir la salida estándar, debe volver a abrir el identificador del archivo. This thread tiene algunas ideas de esta naturaleza.

+0

Esto no lo ayudará a redireccionar a otro (engendrado) programa de stdout a cualquier lugar de su agrado. –

+0

La pregunta se refiere a una biblioteca de terceros. No mencionó la necesidad de redirigir la salida de otro proceso. – greyfade

18

Es necesario crear tubería (con CreatePipe()), a continuación, conecte la salida estándar a escribir que es terminar con SetStdHandle(), a continuación, se puede leer desde el extremo del tubo de leer con ReadFile() y poner el texto se obtiene a partir de ahí cualquier lugar que desee.

0

Esto es lo que haría:

  1. CreatePipe().
  2. CreateProcess() con el identificador de CreatePipe() utilizado como stdout para el nuevo proceso.
  3. Cree un temporizador o un hilo que llame a ReadFile() en ese identificador de vez en cuando y coloque los datos leídos en un cuadro de texto o lo que sea.
1

Gracias al enlace de gamedev en la respuesta por greyfade, yo era capaz de escribir y probar este simple trozo de código

AllocConsole(); 

*stdout = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), _T("a")); 
*stderr = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), _T("a")); 
*stdin = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_INPUT_HANDLE), _O_WRONLY), _T("r")); 


printf("A printf to stdout\n"); 
std::cout << "A << to std::cout\n"; 
std::cerr << "A << to std::cerr\n"; 
std::string input; 
std::cin >> input; 

std::cout << "value read from std::cin is " << input << std::endl; 

Funciona y es adecuada para la depuración. Convertir el texto en un elemento de GUI más atractivo tomaría un poco más de trabajo.

13

probablemente usted está buscando algo por el estilo:

#define OUT_BUFF_SIZE 512 

    int main(int argc, char* argv[]) 
    { 
     printf("1: stdout\n"); 

     StdOutRedirect stdoutRedirect(512); 
     stdoutRedirect.Start(); 
     printf("2: redirected stdout\n"); 
     stdoutRedirect.Stop(); 

     printf("3: stdout\n"); 

     stdoutRedirect.Start(); 
     printf("4: redirected stdout\n"); 
     stdoutRedirect.Stop(); 

     printf("5: stdout\n"); 

     char szBuffer[OUT_BUFF_SIZE]; 
     int nOutRead = stdoutRedirect.GetBuffer(szBuffer,OUT_BUFF_SIZE); 
     if(nOutRead) 
      printf("Redirected outputs: \n%s\n",szBuffer); 

     return 0; 
    } 

Esta clase lo hará:

#include <windows.h> 

#include <stdio.h> 
#include <fcntl.h> 
#include <io.h> 
#include <iostream> 

#ifndef _USE_OLD_IOSTREAMS 
using namespace std; 
#endif 

#define READ_FD 0 
#define WRITE_FD 1 

#define CHECK(a) if ((a)!= 0) return -1; 

class StdOutRedirect 
{ 
    public: 
     StdOutRedirect(int bufferSize); 
     ~StdOutRedirect(); 

     int Start(); 
     int Stop(); 
     int GetBuffer(char *buffer, int size); 

    private: 
     int fdStdOutPipe[2]; 
     int fdStdOut; 
}; 

StdOutRedirect::~StdOutRedirect() 
{ 
    _close(fdStdOut); 
    _close(fdStdOutPipe[WRITE_FD]); 
    _close(fdStdOutPipe[READ_FD]); 
} 
StdOutRedirect::StdOutRedirect(int bufferSize) 
{ 
    if (_pipe(fdStdOutPipe, bufferSize, O_TEXT)!=0) 
    { 
     //treat error eventually 
    } 
    fdStdOut = _dup(_fileno(stdout)); 
} 

int StdOutRedirect::Start() 
{ 
    fflush(stdout); 
    CHECK(_dup2(fdStdOutPipe[WRITE_FD], _fileno(stdout))); 
    ios::sync_with_stdio(); 
    setvbuf(stdout, NULL, _IONBF, 0); // absolutely needed 
    return 0; 
} 

int StdOutRedirect::Stop() 
{ 
    CHECK(_dup2(fdStdOut, _fileno(stdout))); 
    ios::sync_with_stdio(); 
    return 0; 
} 

int StdOutRedirect::GetBuffer(char *buffer, int size) 
{ 
    int nOutRead = _read(fdStdOutPipe[READ_FD], buffer, size); 
    buffer[nOutRead] = '\0'; 
    return nOutRead; 
} 

Aquí está el resultado:

1: stdout 
3: stdout 
5: stdout 
Redirected outputs: 
2: redirected stdout 
4: redirected stdout 
+0

Muy útil. Justo lo que necesitaba. –

+0

Muy útil para mí, gracias – alexpov

2

Aquí vamos a establece un nuevo punto de entrada consoleMain que anula el tuyo.

  1. Determine el punto de entrada de su aplicación. En VisualStudio, seleccione Propiedades del proyecto/Enlazador/Avanzado/Punto de entrada. Vamos a llamarlo defaultMain.
  2. En algún lugar de su código fuente, declare el punto de entrada original (para que podamos encadenarlo) y el nuevo punto de entrada. Ambos deben declararse extern "C" para evitar el cambio de nombre.

    extern "C" 
    { 
        int defaultMain (void); 
        int consoleMain (void); 
    } 
    
  3. Implemente la función de punto de entrada.

    __declspec(noinline) int consoleMain (void) 
    { 
        // __debugbreak(); // Break into the program right at the entry point! 
        AllocConsole(); // Create a new console 
        freopen("CON", "w", stdout); 
        freopen("CON", "w", stderr); 
        freopen("CON", "r", stdin); // Note: "r", not "w". 
        return defaultMain(); 
    } 
    
  4. Añadir su código de prueba en algún lugar, por ejemplo, en un botón haga clic en acción.

    fwprintf(stdout, L"This is a test to stdout\n"); 
    fwprintf(stderr, L"This is a test to stderr\n"); 
    cout<<"Enter an Integer Number Followed by ENTER to Continue" << endl; 
    _flushall(); 
    int i = 0; 
    int Result = wscanf(L"%d", &i); 
    printf ("Read %d from console. Result = %d\n", i, Result); 
    
  5. Conjunto consoleMain como el nuevo punto de entrada (Propiedades del proyecto/enlazador/Avanzado Punto de Entrada/).
Cuestiones relacionadas