Cout auf eine Konsole in Windows umleiten

Lesezeit: 9 Minuten

Cout auf eine Konsole in Windows umleiten
Evan Teran

Ich habe eine Anwendung, die relativ alt ist. Durch einige geringfügige Änderungen lässt es sich nahezu perfekt mit Visual C++ 2008 erstellen. Eine Sache, die mir aufgefallen ist, ist, dass meine “Debug-Konsole” nicht ganz richtig funktioniert. Grundsätzlich habe ich in der Vergangenheit verwendet AllocConsole() um eine Konsole für meine Debug-Ausgabe zu erstellen. Dann würde ich verwenden freopen umzuleiten stdout dazu. Dies funktionierte sowohl mit C- als auch mit C++-Stil-IO perfekt.

Jetzt scheint es, dass es nur mit C-Stil-IO funktioniert. Was ist der richtige Weg, Dinge wie umzuleiten cout zu einer mit zugewiesenen Konsole AllocConsole()?

Hier ist der Code, der früher funktioniert hat:

if(AllocConsole()) {
    freopen("CONOUT$", "wt", stdout);
    SetConsoleTitle("Debug Console");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
}

BEARBEITEN: Eine Sache, die mir eingefallen ist, ist, dass ich einen benutzerdefinierten Streambuf erstellen könnte, dessen Überlaufmethode mit C-Stil IO schreibt und ersetzt std::cout‘s Standard-Stream-Puffer damit. Aber das scheint eine Ausrede zu sein. Gibt es einen richtigen Weg, dies im Jahr 2008 zu tun? Oder hat MS das vielleicht übersehen?

EDIT2: OK, also habe ich die Idee, die ich oben formuliert habe, umgesetzt. Im Grunde sieht es so aus:

class outbuf : public std::streambuf {
public:
    outbuf() {
        setp(0, 0);
    }

    virtual int_type overflow(int_type c = traits_type::eof()) {
        return fputc(c, stdout) == EOF ? traits_type::eof() : c;
    }
};

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    // create the console
    if(AllocConsole()) {
        freopen("CONOUT$", "w", stdout);
        SetConsoleTitle("Debug Console");
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);  
    }

    // set std::cout to use my custom streambuf
    outbuf ob;
    std::streambuf *sb = std::cout.rdbuf(&ob);

    // do some work here

    // make sure to restore the original so we don't get a crash on close!
    std::cout.rdbuf(sb);
    return 0;
}

Jeder hat eine bessere/sauberere Lösung als nur zu zwingen std::cout verherrlicht sein fputc?

  • Roger: Sehr, sehr geschätzt für die aktualisierte Version. Es funktioniert sehr reibungslos. Ich habe einfach damit experimentiert. Ich verwende in in der Architektur [C# + CPP-managed + Native-CPP]. Ich habe versucht, a in meinem WPF-C#-Hauptprogramm zu öffnen, bevor ich Ihre Funktion aufrufe. Es scheint, dass beide Konsolen am Ende vermischt zu sein scheinen, was ich sowieso bevorzuge. Vielen Dank für die großzügige Mühe und den Beitrag. Prost Ikonuk

    – I. Konuk

    3. November 18 um 2:29 Uhr

1644312728 19 Cout auf eine Konsole in Windows umleiten
Roger Sander

Aktualisiert Februar 2018:

Hier ist die neueste Version einer Funktion, die dieses Problem behebt:

void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)
{
    // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been
    // observed that the file number of our standard handle file objects can be assigned internally to a value of -2
    // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our
    // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value
    // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to
    // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target
    // using the "_dup2" function.
    if (bindStdIn)
    {
        FILE* dummyFile;
        freopen_s(&dummyFile, "nul", "r", stdin);
    }
    if (bindStdOut)
    {
        FILE* dummyFile;
        freopen_s(&dummyFile, "nul", "w", stdout);
    }
    if (bindStdErr)
    {
        FILE* dummyFile;
        freopen_s(&dummyFile, "nul", "w", stderr);
    }

    // Redirect unbuffered stdin from the current standard input handle
    if (bindStdIn)
    {
        HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE);
        if(stdHandle != INVALID_HANDLE_VALUE)
        {
            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
            if(fileDescriptor != -1)
            {
                FILE* file = _fdopen(fileDescriptor, "r");
                if(file != NULL)
                {
                    int dup2Result = _dup2(_fileno(file), _fileno(stdin));
                    if (dup2Result == 0)
                    {
                        setvbuf(stdin, NULL, _IONBF, 0);
                    }
                }
            }
        }
    }

    // Redirect unbuffered stdout to the current standard output handle
    if (bindStdOut)
    {
        HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        if(stdHandle != INVALID_HANDLE_VALUE)
        {
            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
            if(fileDescriptor != -1)
            {
                FILE* file = _fdopen(fileDescriptor, "w");
                if(file != NULL)
                {
                    int dup2Result = _dup2(_fileno(file), _fileno(stdout));
                    if (dup2Result == 0)
                    {
                        setvbuf(stdout, NULL, _IONBF, 0);
                    }
                }
            }
        }
    }

    // Redirect unbuffered stderr to the current standard error handle
    if (bindStdErr)
    {
        HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE);
        if(stdHandle != INVALID_HANDLE_VALUE)
        {
            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);
            if(fileDescriptor != -1)
            {
                FILE* file = _fdopen(fileDescriptor, "w");
                if(file != NULL)
                {
                    int dup2Result = _dup2(_fileno(file), _fileno(stderr));
                    if (dup2Result == 0)
                    {
                        setvbuf(stderr, NULL, _IONBF, 0);
                    }
                }
            }
        }
    }

    // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the
    // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In
    // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything
    // has been read from or written to the targets or not.
    if (bindStdIn)
    {
        std::wcin.clear();
        std::cin.clear();
    }
    if (bindStdOut)
    {
        std::wcout.clear();
        std::cout.clear();
    }
    if (bindStdErr)
    {
        std::wcerr.clear();
        std::cerr.clear();
    }
}

Um diese Funktion zu definieren, benötigen Sie den folgenden Satz von Includes:

#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>

Kurz gesagt, diese Funktion synchronisiert die standardmäßigen C/C++-Laufzeit-Eingabe-/Ausgabe-/Fehler-Handles mit den aktuellen Standard-Handles, die dem Win32-Prozess zugeordnet sind. Wie erwähnt in die Dokumentation, AllocConsole ändert diese Prozess-Handles für uns, also ist es nur erforderlich, diese Funktion nach AllocConsole aufzurufen, um die Runtime-Handles zu aktualisieren, andernfalls bleiben die Handles übrig, die bei der Initialisierung der Laufzeit zwischengespeichert wurden. Die grundlegende Verwendung ist wie folgt:

// Allocate a console window for this process
AllocConsole();

// Update the C/C++ runtime standard input, output, and error targets to use the console window
BindCrtHandlesToStdHandles(true, true, true);

Diese Funktion wurde mehrfach überarbeitet. Überprüfen Sie daher die Änderungen an dieser Antwort, wenn Sie an historischen Informationen oder Alternativen interessiert sind. Die aktuelle Antwort ist jedoch die beste Lösung für dieses Problem, da sie die größte Flexibilität bietet und mit jeder Visual Studio-Version funktioniert.

  • @Zingam: Probieren Sie das aus freopen Code, der in der Frage zusammen mit dem angezeigt wird clear() Anrufe von dieser Antwort.

    – Ben Voigt

    28. September 15 um 20:46 Uhr

  • Vielen Dank für das cout.clear()-Bit. Ich wurde ein bisschen verzweifelt, als ich alles andere an Ort und Stelle hatte und immer noch nichts gedruckt bekam. Der Aufruf von clear() hat es behoben.

    – opoch

    31. August 17 um 17:50 Uhr

  • Ihre Bearbeitung 2 ist meiner Meinung nach falsch. freopen_s verstopft die Datei-Handles, bei deren Einrichtung Sie große Sorgfalt walten ließen. Die “richtige” Lösung ist Ihre Bearbeitung 1. Ich sage “richtig”, weil sie stderr effektiv an stdout sendet, da kein CONERR $ vorhanden ist. Wenn also STD_ERROR_HANDLE irgendwie an eine andere Stelle umgeleitet wurde (2> irgendwo), geht diese Umleitung verloren.

    – Austin Frankreich

    6. Dezember 17 um 10:48 Uhr

  • @AustinFrance Danke, diese Bearbeitung wurde nicht von mir vorgenommen, jemand anderes hat meine Antwort bearbeitet, um eine andere eigene Antwort zu geben. Ich habe die Bearbeitung rückgängig gemacht. An den Benutzer, der die Änderung vorgenommen hat: Sie können gerne eine separate Antwort posten oder einen Kommentar dazu abgeben, wenn Sie ein Problem sehen oder einen Vorschlag haben, aber bitte ändern Sie die gepostete Lösung unter meinem Namen nicht grundlegend.

    – Roger Sander

    12. Dezember 17 um 0:38 Uhr


Cout auf eine Konsole in Windows umleiten
Evan Teran

Ich poste eine portable Lösung in Antwortform, damit sie akzeptiert werden kann. Im Grunde habe ich ersetzt cout‘S streambuf mit einer, die mit c-Datei-E/A implementiert ist und am Ende umgeleitet wird. Vielen Dank an alle für Ihren Beitrag.

class outbuf : public std::streambuf {
public:
    outbuf() {
        setp(0, 0);
    }

    virtual int_type overflow(int_type c = traits_type::eof()) {
        return fputc(c, stdout) == EOF ? traits_type::eof() : c;
    }
};

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    // create the console
    if(AllocConsole()) {
        freopen("CONOUT$", "w", stdout);
        SetConsoleTitle("Debug Console");
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);  
    }

    // set std::cout to use my custom streambuf
    outbuf ob;
    std::streambuf *sb = std::cout.rdbuf(&ob);

    // do some work here

    // make sure to restore the original so we don't get a crash on close!
    std::cout.rdbuf(sb);
    return 0;
}

1644312729 110 Cout auf eine Konsole in Windows umleiten
Dmitri Chalatow

Wenn die Konsole nur zum Debuggen dient, können Sie einfach verwenden OutputDebugStringA/OutputDebugStringW Funktionen. Ihre Ausgabe wird an das Ausgabefenster in VS geleitet, wenn Sie sich im Debug-Modus befinden, andernfalls können Sie verwenden DebugView Es zu sehen.

Dies funktioniert mit VC++ 2017 für E/A im C++-Stil

AllocConsole();

// use static for scope
static ofstream conout("CONOUT$", ios::out); 
// Set std::cout stream buffer to conout's buffer (aka redirect/fdreopen)
cout.rdbuf(conout.rdbuf());

cout << "Hello World" << endl;

Für das Original könnten Sie einfach das Beispiel sync_with_stdio(1) verwenden:

if(AllocConsole())
{
    freopen("CONOUT$", "wt", stdout);
    freopen("CONIN$", "rt", stdin);
    SetConsoleTitle(L"Debug Console");
    std::ios::sync_with_stdio(1);
}

  • Ich habe das versucht, siehe den Hinweis am Ende dieser Antwort: stackoverflow.com/questions/311955/…

    – Evan Teran

    18. September 16 um 4:25 Uhr

Cout auf eine Konsole in Windows umleiten
DarthPingu

Die ios-Bibliothek verfügt über eine Funktion, mit der Sie C++ IO mit dem, was das Standard-C-IO verwendet, neu synchronisieren können: ios::sync_with_stdio().

Hier gibt es eine schöne Erklärung: http://dslweb.nwnexus.com/~ast/dload/guicon.htm.

  • Ich habe das versucht, siehe den Hinweis am Ende dieser Antwort: stackoverflow.com/questions/311955/…

    – Evan Teran

    18. September 16 um 4:25 Uhr

1644312731 149 Cout auf eine Konsole in Windows umleiten
Raymond Martineau

Soweit ich das beurteilen kann, sollte Ihr Code mit VC 2005 funktionieren, wenn es Ihre erste Aktivität mit der Konsole ist.

Nachdem Sie einige Möglichkeiten geprüft haben, versuchen Sie möglicherweise, etwas zu schreiben, bevor Sie die Konsole zuweisen. Das Schreiben auf std::cout oder std::wcout an diesem Punkt schlägt fehl und Sie müssen die Fehler-Flags löschen, bevor Sie weitere Ausgaben machen.

  • Ich werde es prüfen. Nur eine Anmerkung, ich verwende 2008, nicht 2005.

    – Evan Teran

    23. November 08 um 1:49 Uhr

  • Für die Windows-API oder Standardbibliothek unterscheidet sich 2005 nicht wesentlich von 2008. Es gibt jedoch einen Unterschied zwischen VC6 und 2003 (aber das reicht wahrscheinlich nicht aus, um dies zu einem Problem zu machen.)

    – Raymond Martineau

    23. November 08 um 2:17 Uhr

  • Leider haben sie zwischen 2003 und 2008 etwas geändert, sodass std::cout angesichts des von mir verwendeten Codes nicht mehr umgeleitet wird :(.

    – Evan Teran

    23. November 08 um 3:22 Uhr

.

820950cookie-checkCout auf eine Konsole in Windows umleiten

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy