web-dev-qa-db-fra.com

Comment puis-je rediriger STDOUT sur un affichage visible dans une application Windows?

J'ai accès à une bibliothèque tierce qui fait "bonnes choses". Il publie des messages de statut et de progression à STDOUT. Dans une application de console, je peux voir ces messages tout à fait. Dans une application Windows, ils vont juste au godet de bits.

Y a-t-il un moyen assez simple de rediriger le stdout et STDERR à un contrôle de texte ou à un autre endroit visible. Idéalement, cela ne nécessiterait aucun recompéteur du code tiers. Il suffirait d'intercepter les vapeurs à un niveau bas. Je voudrais une solution où je viens de #include l'en-tête, appelez la fonction d'initialisation et reliez 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";
}

Encore mieux serait si elle utilisait une interface que je pouvais remplacer, il n'est pas lié à une bibliothèque d'interface graphique particulière.

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

Est-ce que je rêvais juste? Ou quelqu'un sait-il quelque chose qui peut faire quelque chose comme ça?

Modifier 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, il faudrait créer un nouveau fil créé pour lire le fichier et afficher la sortie. Pour déboguer, faire une "queue -f" dans une fenêtre de coquille Cygwin serait suffisante. Pour une application plus polie ... qui est ce que je veux écrire ... Il y aurait du travail supplémentaire pour créer le fil, etc.

37
JoeBieg

Vous devez créer un tuyau (avec Createaipeaipe () ), puis attachez-la à la fin de l'écriture avec SetStDhandle () , alors vous pouvez lire à partir de la fin de lecture du tuyau avec Readfile () et mettre du texte que vous obtenez de là où vous le souhaitez. .

18
n0rd

Vous pouvez rediriger stdout, stard et stdin en utilisant Freopen .

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 une invite de commande comme si:

a.exe > stdout.txt 2> stderr.txt
16
Brian R. Bondy

Vous recherchez probablement quelque chose sur 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
14
Cedric Perthuis

Lorsque vous créez un processus à l'aide d'un processus CatégorieProcess () Vous pouvez choisir un HANDLE à laquelle STDOUT et STDERR vont être écrits. Ceci HANDLE peut être un fichier auquel vous dirigez la sortie.

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

La poignée que vous donnez à CreateProcess() peut également être celle d'un tuyau que vous avez créé, puis vous pouvez lire dans le tuyau et faire autre chose avec les données.

5
shoosh

Vous pouvez faire quelque chose comme ça avec Cout ou 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, vous pouvez le faire avec un std::stringstream ou une autre classe dérivée de std::ostream.

Pour rediriger le stdout, vous devez rouvrir la poignée du fichier. Ce fil a quelques idées de cette nature.

3
greyfade

Ici, nous donnerons un nouveau point d'entrée consoleMain qui remplace votre propre.

  1. Déterminez le point d'entrée de votre application. Dans VisualStudio, sélectionnez Propriétés du projet/Linker/Avancé/Point d'entrée. Appelons-le defaultMain.
  2. Quelque part dans votre code source déclarer le point d'entrée d'origine (afin que nous puissions la enchaîner) et le nouveau point d'entrée. Les deux doivent être déclarés extern "C" Pour empêcher Nom Mangling.

    extern "C"
    {
      int defaultMain (void);
      int consoleMain (void);
    }
    
  3. Implémenter la fonction du 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, E.G. dans un bouton Cliquez sur Action.

    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. Définir consoleMain comme nouveau point d'entrée (Propriétés du projet/point de liaison/point de participation/avancée/entrée).
2
Patrizio Bertoni

C'est ce que je ferais:

  1. Createaipe ().
  2. CréerProcess () avec la poignée de Creeépipe () utilisé comme stdout pour le nouveau processus.
  3. Créez une minuterie ou un filetage qui appelle réadapsile () sur cette poignée de temps en temps et met les données lues dans une zone de texte ou.
1
Andreas Magnusson

Grâce au lien Gamedev Link dans la réponse de Greyfade, j'ai pu écrire et tester ce code simple

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;

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

1
JoeBieg