如何将stdoutredirect到Windows应用程序中的某个可见显示?
我可以访问一个“好东西”的第三方库。 它将状态和进度消息发布到标准输出。 在控制台应用程序中,我可以看到这些消息就好了。 在一个Windows应用程序中,他们只是去了一点桶。
有没有一个相当简单的方法来redirect标准输出和标准错误到文本控件或其他可见的地方。 理想情况下,这不需要重新编译第三方代码。 它只是在低级别拦截蒸汽。 我想要一个解决scheme,我只是#包括头,调用初始化函数,并链接库在…
#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; };
我只是在做梦吗? 还是有人知道可以做这样的事情吗?
两个答案后编辑:我认为使用freopenredirect文件是一个很好的第一步。 对于完整的解决scheme,需要创build一个新的线程来读取文件并显示输出。 为了debugging,在cygwin shell窗口中做一个“tail -f”就足够了。 对于一个更加精美的应用程序来说,我想写的就是这样……创build线程会有一些额外的工作。
你需要使用CreatePipe()创buildpipe道,然后使用SetStdHandle()将stdout附加到它的写入端,然后你可以使用ReadFile()从pipe道的读取端读取数据,并把你从那里得到的文本放在任何你喜欢的地方。
你可以使用freopenredirectstdout,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
你可能正在寻找这样的东西:
#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; }
这个class将做到这一点:
#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
当你使用CreateProcess()创build一个进程时,你可以select一个把stdout和stderr写入的HANDLE
。 这个HANDLE
可以是你指向输出的文件。
这将让你使用的代码,而不用重新编译它。 只要执行它,而不是使用system()
或者什么都不使用CreateProcess()
。
您为CreateProcess()
提供的句柄也可以是您创build的pipe道的句柄,然后您可以从pipe道中读取数据并对数据执行其他操作。
你可以用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
派生的其他类来做到这一点。
要redirectstdout,你需要重新打开文件句柄。 这个线程有这种性质的一些想法。
在这里,我们将设置一个覆盖自己的入口点consoleMain
。
- 确定您的应用程序的入口点。 在VisualStudio中,select项目属性/链接器/高级/入口点 。 我们称之为
defaultMain
。 -
在源代码的某个地方声明原始入口点(所以我们可以链接到它)和新的入口点。 两者都必须声明为
extern "C"
以防止名称混乱。extern "C" { int defaultMain (void); int consoleMain (void); }
-
实现入口点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(); }
-
在某个地方添加你的testing代码, 例如在button点击操作中。
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
设置为新的入口点( 项目属性/链接器/高级/入口点 )。
感谢greyfade在答案中的gamedev链接,我能够编写和testing这段简单的代码
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;
它的工作原理和足够的debugging。 把文本变成一个更有吸引力的GUI元素将需要更多的工作。
这是我会做的:
- CreatePipe()。
- CreateProcess()与CreatePipe()的句柄作为新进程的标准输出。
- 创build一个计时器或一个线程,每隔一段时间在该句柄上调用ReadFile(),并将读取的数据放入文本框或什么东西。