Bill Ticehurst
Bill Ticehurst

Reputation: 1868

WriteFile on redirected stdout in Windows GUI app returns ERROR_NO_DATA

EDIT: Just found out this is only an issue from a PowerShell console. It seems to work from cmd.exe. Investigating that more...

In a Windows GUI (/SUBSYSTEM:WINDOWS) app, it is relatively easy to attach or create a console, and just WriteFile to stdout. (This can get involved if wanting to use the standard C/C++ APIs, e.g. How do I get console output in C++ with a Windows program?, but if using the Win32 API WriteFile it seems simple enough).

However I notice when stdout is redirected, the code works fine in a console app, blows up if it's a GUI app.

Basic code is below, (or see full sample at https://github.com/billti/WinCons). Just include the below as a header file, and shortly after WinMain instantiate the class, call CreateConsole, then any call to Write will fail with the error marked with *** – but ONLY if launched with output redirected (e.g. myapp.exe > .\log.txt). It works fine if not redirected, and either way works fine in a console app.

Note: Attaching to the parent console can result in confusing interleaved output, but it does work.

Any idea how to resolve this? How can I get redirected stdout from a non-console Windows app to work?

#include <string>
 
class Log
{
public:
    void CreateConsole() {
        // In a console app, redirected or not, this returns a valid handle.
        // In a non-redirected GUI app, this returns NULL.
        // If redirected GUI (e.g. "winapp.exe > .\log.txt") this returns a valid handle.
        hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
        if (hStdOut == NULL || hStdOut == INVALID_HANDLE_VALUE) {
            // If launched from a console, then Attach work, else must Alloc.
            if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
                if (!AllocConsole()) {
                    throw std::exception("Failed to allocate a console");
                }
            }
            hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
            if (hStdOut == INVALID_HANDLE_VALUE || hStdOut == NULL) {
                throw std::exception("Invalid stdout");
            }
        }
        hasConsole = true;
    }
 
    void Write(const std::string& msg) {
        DWORD written = 0;
        DWORD err = 0;
        if (hasConsole) {
            if (!WriteFile(hStdOut, msg.c_str(), msg.length(), &written, nullptr)) {
                // *** If output is being redirected in a GUI app, this always fails with ***
                //   0xE8 ERROR_NO_DATA "The pipe is being closed."
                err = GetLastError();
                throw std::exception("Failed to write to console");
            }
        }
    }
 
private:
    bool hasConsole = false;
    HANDLE hStdOut = INVALID_HANDLE_VALUE;
};

Upvotes: 0

Views: 379

Answers (1)

EDITBIN solves this PS issue

editbin /SUBSYTEM:CONSOLE myguiapp.exe

A bit weird solution but still working...

editbin is a part of VS Tools afaik

> where editbin
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.41.34120\bin\Hostx86\x86\editbin.exe

Upvotes: -1

Related Questions