2009-02-21 4 views
36

Ich habe Zugriff auf eine Drittanbieter-Bibliothek, die "gute Sachen" macht. Es gibt Status- und Fortschrittsmeldungen an stdout aus. In einer Konsolenanwendung kann ich diese Nachrichten gut sehen. In einer Windows-Anwendung gehen sie einfach in den Bit-Bucket.Wie kann ich stdout auf eine sichtbare Anzeige in einer Windows-Anwendung umleiten?

Gibt es eine ziemlich einfache Möglichkeit, stdout und stderr zu einem Textsteuerelement oder einem anderen sichtbaren Ort umzuleiten. Idealerweise würde dies keine Neukompilierung des Drittanbietercodes erfordern. Es würde nur die Dämpfe auf niedrigem Niveau abfangen. Ich würde eine Lösung wie, wo ich den Header nur # include, rufen Sie die Funktion zur Initialisierung und verknüpfen Sie die Bibliothek wie 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"; 
} 

Noch besser wäre es, wenn es einige Schnittstelle verwendet, die ich überschreiben könnte, so ist es nicht an eine bestimmte GUI-Bibliothek gebunden.

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

Träume ich gerade? Oder weiß jemand etwas, das so etwas kann?

Bearbeiten nach zwei Antworten: Ich denke, mit Freopen zum Umleiten der Dateien ist ein guter erster Schritt. Für eine vollständige Lösung müsste ein neuer Thread erstellt werden, um die Datei zu lesen und die Ausgabe anzuzeigen. Für das Debugging wäre es ausreichend, ein 'tail -f' in einem Cygwin-Shell-Fenster zu machen. Für eine ausgefeiltere Anwendung ... Was ich schreiben möchte ... es würde etwas zusätzliche Arbeit geben, um den Thread usw. zu erstellen.

+0

'AllocConsole' ist das fehlende Stück. Es erzeugt das vertraute Konsolenfenster. Die Lese- und Schreibfunktionen sind umständlich, aber du kannst stdout/stdin ganz einfach auf diese Weise umleiten: https://stackoverflow.com/a/15547699/133836 –

Antwort

16

Sie können stdout, stderr und stdin unter Verwendung von freopen umleiten.

Aus dem obigen Link:

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

Sie können auch wie so Ihr Programm über Eingabeaufforderung:

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

Wenn Sie einen Prozess erstellen CreateProcess() verwenden, können Sie eine HANDLE zu dem stdout wählen und stderr werden geschrieben. Diese HANDLE kann eine Datei sein, an die Sie die Ausgabe richten.

Damit können Sie den Code ohne Neukompilierung verwenden. Führen Sie es einfach aus und verwenden Sie system() oder whatnot CreateProcess().

Das HANDLE, das Sie an CreateProcess() geben, kann auch das einer Pipe sein, die Sie erstellt haben, und dann können Sie aus der Pipe lesen und etwas anderes mit den Daten tun.

3

Sie könnten mit cout oder cerr etwas tun:

// 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); 

Oder Sie tun können, dass mit einer std::stringstream oder einer anderen von std::ostream abgeleiteten Klasse.

Um stdout umzuleiten, müssen Sie das Dateihandle erneut öffnen. This thread hat einige Ideen dieser Art.

+0

Das wird dir nicht helfen, einen anderen (erzeugten) umzuleiten. Programm stdout zu irgendeinem von Ihnen wünschen. –

+0

Die Frage bezieht sich auf eine Drittanbieter-Bibliothek. Er erwähnte nicht die Notwendigkeit einer Umleitung der Ausgabe eines anderen Prozesses. – greyfade

18

Sie müssen Rohr erstellen (mit CreatePipe()), dann stdout befestigen, um es mit SetStdHandle() End schreiben, dann können Sie aus dem Rohr des Leseende mit ReadFile() lesen und Text Sie dort setzen überall Sie mögen.Diese

0

ist das, was ich tun würde:

  1. CreatePipe().
  2. CreateProcess() mit dem Handle von CreatePipe() als Stdout für den neuen Prozess verwendet.
  3. Erstellen Sie einen Zeitgeber oder einen Thread, der ReadFile() auf diesem Handle hin und wieder aufruft und die gelesenen Daten in ein Textfeld oder whatnot legt.
1

Dank des gamedev Link in der Antwort von greyfade, konnte ich dieses einfache Stück Code

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; 

Es funktioniert schreiben und testen und ist für das Debuggen ausreichend. Den Text in ein attraktiveres GUI-Element zu bringen, würde ein bisschen mehr Arbeit erfordern.

13

Du bist wahrscheinlich für etwas in diese Richtung suchen:

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

Diese Klasse wird es tun:

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

Hier ist das Ergebnis:

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

Sehr hilfreich. Genau das, was ich brauchte. –

+0

Sehr hilfreich für mich, danke – alexpov

2

Hier werden wir Setzen Sie einen neuen Einstiegspunkt consoleMain, der Ihren eigenen überschreibt.

  1. Bestimmen Sie den Einstiegspunkt Ihrer Anwendung. Wählen Sie in VisualStudio Projekteigenschaften/Linker/Erweitert/Einstiegspunkt. Nennen wir es defaultMain.
  2. Irgendwo in Ihrem Quellcode deklarieren Sie den ursprünglichen Einstiegspunkt (damit wir es verketten können) und den neuen Einstiegspunkt. Beide müssen extern "C" deklariert werden, um die Namensverfälschung zu verhindern.

    extern "C" 
    { 
        int defaultMain (void); 
        int consoleMain (void); 
    } 
    
  3. Implementieren Sie die Einstiegspunktfunktion.

    __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. Fügen Sie Ihre Test-Code irgendwo, z.B. in einer Schaltfläche klicken Aktion.

    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. Set consoleMain als neuer Einstiegspunkt (Projekteigenschaften/Linker/Erweitert/Entry Point).