我有一个相对较旧的应用程序.通过一些小的改动,它几乎完全用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);
}
Run Code Online (Sandbox Code Playgroud)
编辑:我发生的一件事是我可以创建一个自定义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;
}
Run Code Online (Sandbox Code Playgroud)
任何人都有更好/更清洁的解决方案,而不仅仅是强迫std::cout自己获得荣耀fputc?
Rog*_*ers 31
这是修复此问题的函数的最新版本:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
要定义此功能,您需要以下一组包含:
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <iostream>
Run Code Online (Sandbox Code Playgroud)
简而言之,此函数将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);
Run Code Online (Sandbox Code Playgroud)
此功能已经过多次修改,因此如果您对历史信息或替代方案感兴趣,请检查对此答案的编辑.当前的答案是解决这个问题的最佳方案,它提供了最大的灵活性,可以处理任何Visual Studio版本.
Eva*_*ran 19
我正在以答案形式发布便携式解决方案,因此可以接受.基本上我取代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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
36037 次 |
| 最近记录: |