2009-02-21 15 views
36

Ho accesso a una libreria di terze parti che fa "cose ​​buone". Invia messaggi di stato e di avanzamento allo stdout. In un'applicazione Console posso vedere questi messaggi bene. In un'applicazione Windows vanno semplicemente al pozzetto.Come posso reindirizzare lo stdout su un display visibile in un'applicazione Windows?

C'è un modo abbastanza semplice per reindirizzare lo stdout e lo stderr in un controllo di testo o in un'altra posizione visibile. Idealmente, questo non richiederebbe alcuna ricompilazione del codice di terze parti. Sarebbe solo intercettare i vapori a un livello basso. Vorrei una soluzione in cui ho appena # include l'intestazione, chiamare la funzione di inizializzazione e collegare la libreria come in ...

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

Ancora meglio sarebbe se utilizzato qualche interfaccia che ho potuto ignorare così è non legato a nessuna libreria della GUI particolare.

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

Sto solo sognando? O qualcuno sa qualcosa che può fare qualcosa di simile?

Modifica dopo due risposte: Penso che l'utilizzo di freopen per reindirizzare i file sia un buon primo passo. Per una soluzione completa ci dovrebbe essere un nuovo thread creato per leggere il file e visualizzare l'output. Per il debug, fare un 'tail -f' in una finestra shell cygwin sarebbe sufficiente. Per un'applicazione più lucida ... Qual è quello che voglio scrivere ... Ci sarebbe qualche lavoro in più per creare il thread, ecc.

+0

'AllocConsole' è il pezzo mancante. Produce la consueta finestra della console. Le funzioni di lettura e scrittura sono scomode, ma puoi facilmente reindirizzare stdout/stdin in/da esso con questo metodo: https://stackoverflow.com/a/15547699/133836 –

risposta

16

È possibile reindirizzare stdout, stderr e stdin utilizzando freopen.

dal link qui sopra:

/* 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; 
} 

È inoltre possibile eseguire il programma tramite prompt dei comandi in questo modo:

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

Quando si crea un processo utilizzando CreateProcess() è possibile scegliere un HANDLE a cui stdout e stderr saranno scritti. Questo HANDLE può essere un file a cui indirizzare l'output.

Questo consentirà di utilizzare il codice senza ricompilarlo. Basta eseguirlo e invece di usare system() o quant'altro, utilizzare CreateProcess().

La MANIGLIA che si fornisce a CreateProcess() può anche essere quella di una pipe creata dall'utente, quindi è possibile leggere dalla pipe e fare qualcos'altro con i dati.

3

Si potrebbe fare qualcosa di simile 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); 

In alternativa, è possibile farlo con un std::stringstream o qualche altra classe derivata da std::ostream.

Per reindirizzare lo stdout, è necessario riaprire l'handle del file. This thread ha alcune idee di questa natura.

+0

Questo non ti aiuterà a reindirizzare un altro (generato) lo stdout del programma su qualsiasi vostro gradimento. –

+0

La domanda si riferisce a una libreria di terze parti. Non ha menzionato la necessità di reindirizzare l'output di un altro processo. – greyfade

18

È necessario creare tubo (con CreatePipe()), quindi allegare stdout al suo scrivere terminare con SetStdHandle(), allora si può leggere dalla fine lettura di tubo con ReadFile() e inserire il testo che si ottiene da lì ovunque lo desideri.

0

Questo è quello che farei:

  1. CreatePipe().
  2. CreateProcess() con l'handle di CreatePipe() utilizzato come stdout per il nuovo processo.
  3. Creare un timer o una discussione che chiama ReadFile() su quell'handle ogni tanto e mette i dati letti in una casella di testo o whatnot.
1

Grazie al collegamento gamedev nella risposta da greyfade, sono stato in grado di scrivere e testare questo semplice pezzo di codice

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; 

funziona ed è adeguata per il debug. Ottenere il testo in un elemento GUI più attraente richiederebbe un po 'più di lavoro.

13

Probabilmente siete alla ricerca di qualcosa del genere:

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

Questa classe lo farà:

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

Ecco il risultato:

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

Molto utile. Proprio quello di cui avevo bisogno. –

+0

Molto utile per me, grazie – alexpov

2

Qui faremo impostare un nuovo punto di ingresso consoleMain che sostituisca il proprio.

  1. Determinare il punto di ingresso dell'applicazione. In VisualStudio, selezionare Proprietà progetto/linker/Avanzate/Punto di ingresso. Chiamiamolo defaultMain.
  2. Da qualche parte nel codice sorgente dichiara il punto di ingresso originale (quindi possiamo concatenarlo) e il nuovo punto di ingresso. Entrambe devono essere dichiarate extern "C" per impedire la manipolazione del nome.

    extern "C" 
    { 
        int defaultMain (void); 
        int consoleMain (void); 
    } 
    
  3. Implementare la funzione del punto di ingresso.

    __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. Aggiungi il tuo codice di prova da qualche parte, per esempio in un'azione di clic del pulsante.

    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. Imposta consoleMain come il nuovo punto di ingresso (Proprietà progetto/Linker/Avanzate/Punto di ingresso).