我有一个相对较旧的应用程序.通过一些小的改动,它几乎完全用Visual C++ 2008构建.我注意到的一件事是我的"调试控制台"工作不正常.基本上在过去,我AllocConsole()
用来为我的调试输出创建一个控制台.然后我会用它freopen
来重定向stdout
到它.这与C和C++风格的IO完美配合.
现在,它似乎只适用于C风格的IO.将内容重定向cout
到分配的控制台的正确方法是什么AllocConsole()
?
这是以前的代码:
if(AllocConsole()) { freopen("CONOUT$", "wt", stdout); SetConsoleTitle("Debug Console"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); }
编辑:我发生的一件事是我可以创建一个自定义streambuf,其溢出方法使用C样式IO写入并std::cout
用它替换默认的流缓冲区.但这似乎是一个警察.有没有正确的方法在2008年这样做?或者这可能是MS忽略的东西?
编辑2:好的,所以我已经实现了上面拼写的想法.基本上它看起来像这样:
class outbuf : public std::streambuf { public: outbuf() { setp(0, 0); } virtual int_type overflow(int_type c = traits_type::eof()) { return fputc(c, stdout) == EOF ? traits_type::eof() : c; } }; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // create the console if(AllocConsole()) { freopen("CONOUT$", "w", stdout); SetConsoleTitle("Debug Console"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); } // set std::cout to use my custom streambuf outbuf ob; std::streambuf *sb = std::cout.rdbuf(&ob); // do some work here // make sure to restore the original so we don't get a crash on close! std::cout.rdbuf(sb); return 0; }
任何人都有更好/更清洁的解决方案,而不仅仅是强迫std::cout
自己获得荣耀fputc
?
这是修复此问题的函数的最新版本:
void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr) { // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been // observed that the file number of our standard handle file objects can be assigned internally to a value of -2 // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target // using the "_dup2" function. if (bindStdIn) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "r", stdin); } if (bindStdOut) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "w", stdout); } if (bindStdErr) { FILE* dummyFile; freopen_s(&dummyFile, "nul", "w", stderr); } // Redirect unbuffered stdin from the current standard input handle if (bindStdIn) { HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE); if(stdHandle != INVALID_HANDLE_VALUE) { int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); if(fileDescriptor != -1) { FILE* file = _fdopen(fileDescriptor, "r"); if(file != NULL) { int dup2Result = _dup2(_fileno(file), _fileno(stdin)); if (dup2Result == 0) { setvbuf(stdin, NULL, _IONBF, 0); } } } } } // Redirect unbuffered stdout to the current standard output handle if (bindStdOut) { HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE); if(stdHandle != INVALID_HANDLE_VALUE) { int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); if(fileDescriptor != -1) { FILE* file = _fdopen(fileDescriptor, "w"); if(file != NULL) { int dup2Result = _dup2(_fileno(file), _fileno(stdout)); if (dup2Result == 0) { setvbuf(stdout, NULL, _IONBF, 0); } } } } } // Redirect unbuffered stderr to the current standard error handle if (bindStdErr) { HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE); if(stdHandle != INVALID_HANDLE_VALUE) { int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT); if(fileDescriptor != -1) { FILE* file = _fdopen(fileDescriptor, "w"); if(file != NULL) { int dup2Result = _dup2(_fileno(file), _fileno(stderr)); if (dup2Result == 0) { setvbuf(stderr, NULL, _IONBF, 0); } } } } } // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything // has been read from or written to the targets or not. if (bindStdIn) { std::wcin.clear(); std::cin.clear(); } if (bindStdOut) { std::wcout.clear(); std::cout.clear(); } if (bindStdErr) { std::wcerr.clear(); std::cerr.clear(); } }
要定义此功能,您需要以下一组包含:
#include#include #include #include
简而言之,此函数将C/C++运行时标准输入/输出/错误句柄与与Win32进程关联的当前标准句柄同步.正如文档中所提到的,AllocConsole为我们更改了这些进程句柄,因此所需要的只是在AllocConsole之后调用此函数来更新运行时句柄,否则我们将留下初始化运行时时锁定的句柄.基本用法如下:
// Allocate a console window for this process AllocConsole(); // Update the C/C++ runtime standard input, output, and error targets to use the console window BindCrtHandlesToStdHandles(true, true, true);
此功能已经过多次修改,因此如果您对历史信息或替代方案感兴趣,请检查对此答案的编辑.当前的答案是解决这个问题的最佳方案,它提供了最大的灵活性,可以处理任何Visual Studio版本.
我正在以答案形式发布便携式解决方案,因此可以接受.基本上我取代cout
的streambuf
与一个被用c文件I/O其中并最终被重定向实现.感谢大家的投入.
class outbuf : public std::streambuf { public: outbuf() { setp(0, 0); } virtual int_type overflow(int_type c = traits_type::eof()) { return fputc(c, stdout) == EOF ? traits_type::eof() : c; } }; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // create the console if(AllocConsole()) { freopen("CONOUT$", "w", stdout); SetConsoleTitle("Debug Console"); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED); } // set std::cout to use my custom streambuf outbuf ob; std::streambuf *sb = std::cout.rdbuf(&ob); // do some work here // make sure to restore the original so we don't get a crash on close! std::cout.rdbuf(sb); return 0; }
如果控制台仅用于调试,则可以使用OutputDebugStringA
/ OutputDebugStringW
functions.如果您处于调试模式,它们的输出将定向到VS中的Output窗口,否则您可以使用DebugView来查看它.