¿Cómo puedo redireccionar stdout a alguna pantalla visible en una aplicación de Windows?

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.

¿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 recomstackción del código de terceros. Simplemente interceptaría los vapores a un nivel bajo. Me gustaría una solución donde simplemente # incluya el encabezado, llame a la función de inicialización y vincule 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"; } 

Sería aún mejor si utilizara alguna interfaz que pudiera anular para que no esté vinculada 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 refinada … que es lo que quiero escribir … habría algo de trabajo extra para crear el hilo, etc.

Necesita crear un conducto (con CreatePipe () ), luego adjuntar stdout a su fin de escritura con SetStdHandle () , luego puede leer desde el final de lectura de pipe con ReadFile () y colocar el texto que obtenga desde allí en cualquier lugar que desee.

Puede redirigir stdout, stderr y stdin usando freopen .

Del enlace de arriba:

 /* freopen example: redirecting stdout */ #include  int main () { freopen ("myfile.txt","w",stdout); printf ("This sentence is redirected to a file."); fclose (stdout); return 0; } 

También puede ejecutar su progtwig a través del símbolo del sistema así:

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

Probablemente estés buscando algo en esa línea:

  #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  #include  #include  #include  #include  #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; } 

Este es el resultado:

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

Cuando crea un proceso usando CreateProcess () puede elegir un HANDLE en el que se escribirán stdout y stderr. Esta HANDLE puede ser un archivo al que dirige la salida.

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

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

Podrías 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 std::stringstream o alguna otra clase derivada de std::ostream .

Para redirigir la salida estándar, deberá volver a abrir el identificador del archivo. Este hilo tiene algunas ideas de esta naturaleza.

Aquí estableceremos un nuevo punto de entrada consoleMain que anula tu propio.

  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. Implementar 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. Agregue su código de prueba en algún lugar, por ejemplo , en una acción de clic de botó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. Establezca consoleMain como el nuevo punto de entrada ( Propiedades del proyecto / Enlazador / Avanzado / Punto de entrada ).

Gracias al enlace gamedev en la respuesta de greyfade, pude escribir y probar esta simple pieza 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 adecuado para la depuración. Convertir el texto en un elemento de GUI más atractivo tomaría un poco más de trabajo.

Esto es lo que haría:

  1. CreatePipe ().
  2. CreateProcess () con el handle 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.