2009-02-21 6 views
36

J'ai accès à une bibliothèque tierce qui fait de "bonnes choses". Il envoie des messages d'état et de progression à stdout. Dans une application de console, je peux voir ces messages très bien. Dans une application Windows, ils vont simplement au bit bit.Comment rediriger stdout vers un affichage visible dans une application Windows?

Existe-t-il un moyen assez simple de rediriger stdout et stderr vers un contrôle de texte ou un autre endroit visible. Idéalement, cela ne nécessiterait aucune recompilation du code de tiers. Il aurait juste intercepter les vapeurs à un niveau bas. Je voudrais une solution où je viens #Include l'en-tête, appelez la fonction d'initialisation et de relier la bibliothèque comme dans ...

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

serait encore mieux si elle utilisait une interface qui je pouvais passer outre il est pas lié à une bibliothèque GUI particulière.

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

Je rêve juste? Ou est-ce que quelqu'un sait quelque chose qui peut faire quelque chose comme ça? Éditer après deux réponses: Je pense qu'utiliser freopen pour rediriger les fichiers est une bonne première étape. Pour une solution complète, un nouveau thread doit être créé pour lire le fichier et afficher la sortie. Pour le débogage, faire un 'tail -f' dans une fenêtre shell cygwin serait suffisant. Pour une application plus polie ... Ce qui est ce que je veux écrire ... il y aurait du travail supplémentaire pour créer le thread, etc.

+0

'AllocConsole' est la pièce manquante. Il produit la fenêtre de la console familière. Les fonctions de lecture et d'écriture sont gênantes, mais vous pouvez facilement rediriger stdout/stdin vers/depuis cette méthode en utilisant cette méthode: https://stackoverflow.com/a/15547699/133836 –

Répondre

16

Vous pouvez rediriger stdout, stderr et stdin en utilisant freopen.

À partir du lien ci-dessus:

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

Vous pouvez également exécuter votre programme via l'invite de commande comme ceci:

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

Lorsque vous créez un processus utilisant CreateProcess() vous pouvez choisir un HANDLE auquel stdout et stderr vont être écrits. Ce HANDLE peut être un fichier vers lequel vous dirigez la sortie.

Cela vous permettra d'utiliser le code sans le recompiler. Il suffit de l'exécuter et au lieu d'utiliser system() ou autre, utilisez CreateProcess().

Le POIGNEE que vous donnez à CreateProcess() peut aussi être celui d'un tuyau que vous avez créé, et vous pouvez ensuite lire depuis le tuyau et faire autre chose avec les données.

3

Vous pouvez faire quelque chose comme ça avec ou Cout 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); 

Ou bien, vous pouvez le faire avec un std::stringstream ou d'une autre classe dérivée de std::ostream.

Pour rediriger stdout, vous devez rouvrir le handle de fichier. This thread a quelques idées de cette nature.

+0

Cela ne vous aidera pas à rediriger un autre (engendré) programme stdout à n'importe où de votre goût. –

+0

La question se réfère à une bibliothèque tierce. Il n'a pas mentionné la nécessité de réorienter les résultats d'un autre processus. – greyfade

18

Vous devez créer tuyau (avec CreatePipe()), puis attacher stdout à sa fin avec écrire SetStdHandle(), vous pouvez lire la fin de lecture de tuyau avec ReadFile() et mettre le texte que vous obtenez à partir de là où vous voulez.

0

C'est ce que je ferais:

  1. CreatePipe().
  2. CreateProcess() avec le handle de CreatePipe() utilisé comme stdout pour le nouveau processus.
  3. Créez un minuteur ou un thread qui appelle ReadFile() sur ce handle de temps en temps et met les données lues dans une zone de texte ou autre.
1

Merci au lien gamedev dans la réponse par greyfade, j'ai pu écrire et tester ce simple morceau de 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; 

Il fonctionne et est adéquat pour le débogage. Obtenir le texte dans un élément graphique plus attrayant prendrait un peu plus de travail.

13

Vous êtes probablement quelque chose le long de ces lignes:

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

Cette classe le fera:

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

Voici le résultat:

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

Très utile. Juste ce dont j'avais besoin. –

+0

Très utile pour moi, merci – alexpov

2

Ici, nous allons définissez un nouveau point d'entrée consoleMain qui remplace le vôtre.

  1. Déterminez le point d'entrée de votre application. Dans VisualStudio, sélectionnez Propriétés du projet/Lieur/Avancé/Point d'entrée. Appelons-le defaultMain.
  2. Quelque part dans votre code source, déclarez le point d'entrée d'origine (nous pouvons donc le chaîner) et le nouveau point d'entrée. Les deux doivent être déclarés extern "C" afin d'éviter le détournement de nom.

    extern "C" 
    { 
        int defaultMain (void); 
        int consoleMain (void); 
    } 
    
  3. Implémentez la fonction de point d'entrée.

    __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. Ajoutez votre code de test quelque part, par exemple dans une action de clic sur un bouton.

    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 comme nouveau point (Propriétés du projet/Linker/Avancé/Point d'entrée) d'entrée.
Questions connexes