如何将标准输出重定向到 Windows 应用程序中的某个可见显示?

Posted

技术标签:

【中文标题】如何将标准输出重定向到 Windows 应用程序中的某个可见显示?【英文标题】:How can I redirect stdout to some visible display in a Windows Application? 【发布时间】:2010-10-09 02:13:20 【问题描述】:

我可以访问做“好东西”的第三方库。它向标准输出发出状态和进度消息。在控制台应用程序中,我可以很好地看到这些消息。在 Windows 应用程序中,它们只是进入比特桶。

是否有一种相当简单的方法可以将 stdout 和 stderr 重定向到文本控件或其他可见位置。理想情况下,这不需要重新编译第三方代码。它只会拦截低水平的蒸汽。我想要一个解决方案,我只需#include 标头,调用初始化函数并链接库,如...

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

如果它使用一些我可以覆盖的界面,这样它就不会绑定到任何特定的 GUI 库,那就更好了。

class StdFilesRedirector

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

我只是在做梦吗?或者有人知道可以做这样的事情吗?

在两个答案后编辑:我认为使用 freopen 重定向文件是一个很好的第一步。对于完整的解决方案,需要创建一个新线程来读取文件并显示输出。对于调试,在 cygwin shell 窗口中执行 'tail -f' 就足够了。对于更精致的应用程序......这就是我想要写的......创建线程等需要一些额外的工作。

【问题讨论】:

AllocConsole 是缺失的部分。它产生熟悉的控制台窗口。读取和写入功能很笨拙,但是您可以使用以下方法轻松地将 stdout/stdin 重定向到/从它:***.com/a/15547699/133836 【参考方案1】:

您需要创建管道(使用CreatePipe()),然后使用SetStdHandle() 将stdout 附加到它的写入端,然后您可以使用ReadFile() 从管道的读取端读取并将您从那里获得的文本放在您喜欢的任何地方。

【讨论】:

【参考方案2】:

您可以使用freopen 重定向标准输出、标准错误和标准输入。

来自以上链接:

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

你也可以像这样通过命令提示符运行你的程序:

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

【讨论】:

【参考方案3】:

您可能正在寻找类似的东西:

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

这个类会做:

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

结果如下:

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

【讨论】:

非常有帮助。正是我需要的。 为什么每次都用Start()Stop() 调用ios::sync_with_stdio(true)?是否有其他代码将其设置为false【参考方案4】:

当你使用CreateProcess() 创建一个进程时,你可以选择一个HANDLE 来写入stdout 和stderr。这个HANDLE 可以是您将输出定向到的文件。

这将使您无需重新编译即可使用代码。只需执行它,而不是使用system() 或诸如此类的东西,而是使用CreateProcess()

您提供给CreateProcess() 的句柄也可以是您创建的管道的句柄,然后您可以从管道中读取数据并对数据执行其他操作。

【讨论】:

【参考方案5】:

你可以用 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);

或者,您可以使用std::stringstream 或从std::ostream 派生的其他类来做到这一点。

要重定向标准输出,您需要重新打开文件句柄。 This thread 有一些类似的想法。

【讨论】:

这不会帮助您将另一个(衍生)程序的标准输出重定向到您喜欢的任何地方。 问题涉及第三方库。他没有提到需要重定向另一个进程的输出。【参考方案6】:

这就是我要做的:

    CreatePipe(). CreateProcess() 使用来自 CreatePipe() 的句柄作为新进程的标准输出。 创建一个计时器或线程,不时在该句柄上调用 ReadFile(),并将读取的数据放入文本框等。

【讨论】:

【参考方案7】:

在这里,我们将设置一个新的入口点 consoleMain,它会覆盖您自己的入口点。

    确定应用程序的入口点。在 VisualStudio 中,选择 Project Properties/Linker/Advanced/Entry Point。让我们称之为defaultMain

    在您的源代码中的某处声明原始入口点(以便我们可以链接到它)和新入口点。两者都必须声明为 extern "C" 以防止名称混淆。

    extern "C"
    
      int defaultMain (void);
      int consoleMain (void);
    
    

    实现入口函数。

    __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();
    
    

    在某处添加您的测试代码,例如在按钮单击操作中。

    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);
    
    consoleMain 设置为新的入口点(Project Properties/Linker/Advanced/Entry Point)。

【讨论】:

【参考方案8】:

感谢 grayfade 答案中的 gamedev 链接,我能够编写和测试这段简单的代码

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;

它可以工作并且足以进行调试。将文本变成更具吸引力的 GUI 元素需要更多的工作。

【讨论】:

以上是关于如何将标准输出重定向到 Windows 应用程序中的某个可见显示?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 A 的标准输出重定向到 B 的标准输入,将 B 的标准输出重定向到 A 的标准输入?

如何将标准输出重定向到 CMD 中的程序?

如何将标准输出重定向到 Tkinter Text 小部件

如何将 gzip 输出重定向到 Popen 标准输入

如何将标准输出和标准错误重定向到 Python 中的记录器

如何将标准输出和标准错误重定向到 Python 中的记录器