C++ 如何将标准输出重定向到 Windows 应用程序中的某些可见显示?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/573724/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I redirect stdout to some visible display in a Windows Application?
提问by JoeBieg
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.
我可以访问做“好东西”的第三方库。它向标准输出发出状态和进度消息。在控制台应用程序中,我可以很好地看到这些消息。在 Windows 应用程序中,它们只是进入比特桶。
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...
是否有一种相当简单的方法将 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";
}
Even better would be if it used some interface that I could override so it is not tied to any particular GUI library.
更好的是,如果它使用一些我可以覆盖的接口,这样它就不会绑定到任何特定的 GUI 库。
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.
两个答案后编辑:我认为使用 freopen 重定向文件是一个很好的第一步。对于完整的解决方案,需要创建一个新线程来读取文件并显示输出。对于调试,在 cygwin shell 窗口中执行“tail -f”就足够了。对于更精致的应用程序......这就是我想要写的......创建线程等会有一些额外的工作。
回答by n0rd
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.
您需要创建管(带CreatePipe() ),然后将其附加标准输出到它与写结束SetStdHandle() ,那么你可以从管道与读端读的ReadFile() ,并把文本你从那里您想要的位置。
回答by Brian R. Bondy
You can redirect stdout, stderr and stdin using freopen.
您可以使用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
回答by Brian R. Bondy
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] = '1: stdout
3: stdout
5: stdout
Redirected outputs:
2: redirected stdout
4: redirected stdout
';
return nOutRead;
}
Here's the result:
结果如下:
// 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);
回答by shoosh
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.
当您使用CreateProcess()创建进程时,您可以选择HANDLE
将要写入的 stdout 和 stderr。这HANDLE
可以是您将输出定向到的文件。
This will let you use the code without recompiling it. Just execute it and instead of using system()
or whatnot, use CreateProcess()
.
这将使您无需重新编译即可使用代码。只需执行它,而不是使用system()
或诸如此类,使用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.
您提供给的句柄CreateProcess()
也可以是您创建的管道的句柄,然后您可以从管道中读取数据并对数据执行其他操作。
回答by greyfade
You could do something like this with cout or cerr:
你可以用 cout 或 cerr 做这样的事情:
extern "C"
{
int defaultMain (void);
int consoleMain (void);
}
Or, you can do that with a std::stringstream
or some other class derived from std::ostream
.
或者,您可以使用std::stringstream
从std::ostream
.
To redirect stdout, you'd need to reopen the file handle. This threadhas some ideas of this nature.
要重定向标准输出,您需要重新打开文件句柄。这个线程有一些这种性质的想法。
回答by Patrizio Bertoni
Here we'll set a new entry point consoleMain
that overrides your own one.
在这里,我们将设置一个新的入口点consoleMain
来覆盖您自己的入口点。
- Determine the entry point of your application. In VisualStudio, select Project Properties/Linker/Advanced/Entry Point. Let us call it
defaultMain
. 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.__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(); }
Implement the entry point function.
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);
Add your test code somewhere, e.g.in a button click action.
extern "C" { int defaultMain (void); int consoleMain (void); }
- Set
consoleMain
as the new entry point (Project Properties/Linker/Advanced/Entry Point).
- 确定应用程序的入口点。在 VisualStudio 中,选择Project Properties/Linker/Advanced/Entry Point。让我们称之为
defaultMain
。 在源代码的某处声明原始入口点(以便我们可以链接到它)和新入口点。两者都必须声明
extern "C"
以防止名称修改。__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);
在某处添加您的测试代码,例如在按钮单击操作中。
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;
- 设置
consoleMain
为新的入口点(Project Properties/Linker/Advanced/Entry Point)。
回答by Andreas Magnusson
This is what I'd do:
这就是我要做的:
- CreatePipe().
- CreateProcess() with the handle from CreatePipe() used as stdout for the new process.
- 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.
- 创建管道()。
- CreateProcess() 和来自 CreatePipe() 的句柄用作新进程的标准输出。
- 创建一个计时器或一个线程,时不时在该句柄上调用 ReadFile() 并将读取的数据放入文本框或其他内容中。
回答by JoeBieg
Thanks to the gamedevlink in the answer by greyfade, I was able to write and test this simple piece of code
多亏了gamedev由greyfade答案的链接,我能够编写和测试这个简单的代码
##代码##It works and is adequate for debugging. Getting the text into a more attractive GUI element would take a bit more work.
它可以工作并且足以进行调试。将文本变成更具吸引力的 GUI 元素需要做更多的工作。