如何将 stdout 重定向到 Windows 应用程序中的某些可见显示?

发布于 2024-07-14 09:13:13 字数 1040 浏览 5 评论 0原文

我可以访问一个做“好东西”的第三方库。 它向标准输出发出状态和进度消息。 在控制台应用程序中,我可以很好地看到这些消息。 在 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”就足够了。 对于更精致的应用程序...这就是我想写的...需要一些额外的工作来创建线程等。

I have access to a third party library that does "good stuff." It issues status and progress messages to stdout. In a Console application I can see these messages just fine. In a Windows application they just go to the bit bucket.

Is there a fairly simple way to redirect stdout and stderr to a text control or other visible place. Ideally, this would not require any recompiles of the third party code. It would just intercept the steams at a low level. I'd like a solution where I just #include the header, call the initialization function and link the library as in...

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

Even better would be if it used some interface that I could override so it is not tied to any particular GUI library.

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

Am I just dreaming? Or does anyone know of something that can do something like this?

Edit after two answers: I think using freopen to redirect the files is a good first step. For a complete solution there would need to be a new thread created to read the file and display the output. For debugging, doing a 'tail -f' in a cygwin shell window would be enough. For a more polished application... Which is what I want to write... there would be some extra work to create the thread, etc.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

儭儭莪哋寶赑 2024-07-21 09:13:13

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

You need to create pipe (with CreatePipe()), then attach stdout to it's write end with SetStdHandle(), then you can read from pipe's read end with ReadFile() and put text you get from there anywhere you like.

梦幻的心爱 2024-07-21 09:13:13

您可以使用 freopen 重定向 stdout、stderr 和 stdin。

从上面的链接:

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

You can redirect stdout, stderr and stdin using freopen.

From the above link:

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

You can also run your program via command prompt like so:

a.exe > stdout.txt 2> stderr.txt
独自唱情﹋歌 2024-07-21 09:13:13

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

    #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

You're probably looking for something along those lines:

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

This class will do it:

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

Here's the result:

1: stdout
3: stdout
5: stdout
Redirected outputs:
2: redirected stdout
4: redirected stdout
盛装女皇 2024-07-21 09:13:13

当您使用 CreateProcess() 创建进程时,您可以选择 < code>HANDLE 将写入 stdout 和 stderr。 此HANDLE 可以是您将输出定向到的文件。

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

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

When you create a process using CreateProcess() you can choose a HANDLE to which stdout and stderr are going to be written. This HANDLE can be a file to which you direct the output.

This will let you use the code without recompiling it. Just execute it and instead of using system() or whatnot, use CreateProcess().

The HANDLE you give to CreateProcess() can also be that of a pipe you created, and then you can read from the pipe and do something else with the data.

关于从前 2024-07-21 09:13:13

您可以使用 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 派生的其他类来执行此操作。

要重定向标准输出,您需要重新打开文件句柄。 此帖子有一些这种性质的想法。

You could do something like this with cout or 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);

Or, you can do that with a std::stringstream or some other class derived from std::ostream.

To redirect stdout, you'd need to reopen the file handle. This thread has some ideas of this nature.

心病无药医 2024-07-21 09:13:13

这就是我要做的:

  1. CreatePipe()。
  2. CreateProcess() 与 CreatePipe() 的句柄用作新进程的标准输出。
  3. 创建一个计时器或一个线程,时不时地在该句柄上调用 ReadFile() 并将读取的数据放入文本框或其他内容中。

This is what I'd do:

  1. CreatePipe().
  2. CreateProcess() with the handle from CreatePipe() used as stdout for the new process.
  3. Create a timer or a thread that calls ReadFile() on that handle every now and then and puts the data read into a text-box or whatnot.
魂牵梦绕锁你心扉 2024-07-21 09:13:13

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

  1. 确定应用程序的入口点。 在 VisualStudio 中,选择“项目属性/链接器/高级/入口点”。 我们将其命名为 defaultMain
  2. 在源代码中的某个位置声明原始入口点(以便我们可以链接到它)和新入口点。 两者都必须声明 extern "C" 以防止名称损坏。

    <前><代码>外部“C”
    {
    int defaultMain (void);
    int consoleMain(无效);
    }

  3. 实现入口点功能。

    __declspec(noinline) int consoleMain (void) 
      { 
        // __debugbreak();   // 在入口点闯入程序! 
        AllocConsole();   // 创建一个新的控制台 
        freopen("CON", "w", 标准输出); 
        freopen("CON", "w", stderr); 
        freopen("CON", "r", 标准输入);   // 注意:“r”,而不是“w”。 
        返回defaultMain(); 
      } 
      
  4. 将测试代码添加到某处,例如在按钮单击操作中。

    fwprintf(stdout, L"这是对 stdout\n 的测试"); 
      fwprintf(stderr, L"这是对 stderr\n 的测试"); 
      cout<<“输入一个整数,然后按 ENTER 继续”<<   结束; 
      _flushall(); 
      整数 i = 0; 
      int 结果 = wscanf( L"%d", &i); 
      printf ("从控制台读取 %d。结果 = %d\n", i, 结果); 
      
  5. consoleMain 设置为新入口点(项目属性/链接器/高级/入口点)。

Here we'll set a new entry point consoleMain that overrides your own one.

  1. Determine the entry point of your application. In VisualStudio, select Project Properties/Linker/Advanced/Entry Point. Let us call it defaultMain.
  2. Somewhere in your source code declare the original entry point (so we can chain to it) and the new entry point. Both must be declared extern "C" to prevent name mangling.

    extern "C"
    {
      int defaultMain (void);
      int consoleMain (void);
    }
    
  3. Implement the entry point function.

    __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. Add your test code somewhere, e.g. in a button click 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. Set consoleMain as the new entry point (Project Properties/Linker/Advanced/Entry Point).
提笔落墨 2024-07-21 09:13:13

感谢 greyfade 答案中的 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 元素需要更多的工作。

Thanks to the gamedev link in the answer by greyfade, I was able to write and test this simple piece of 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;

It works and is adequate for debugging. Getting the text into a more attractive GUI element would take a bit more work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文