是否可以将stdout重定向到Visual Studio的输出窗口?我在我的程序OutputDebugString中使用但是我使用了一些带有printf或cout的输出调试消息的库。
答案 0 :(得分:10)
#include <ostream>
#include <Windows.h>
/// \brief This class is a derivate of basic_stringbuf which will output all the written data using the OutputDebugString function
template<typename TChar, typename TTraits = std::char_traits<TChar>>
class OutputDebugStringBuf : public std::basic_stringbuf<TChar,TTraits> {
public:
explicit OutputDebugStringBuf() : _buffer(256) {
setg(nullptr, nullptr, nullptr);
setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
}
~OutputDebugStringBuf() {
}
static_assert(std::is_same<TChar,char>::value || std::is_same<TChar,wchar_t>::value, "OutputDebugStringBuf only supports char and wchar_t types");
int sync() try {
MessageOutputer<TChar,TTraits>()(pbase(), pptr());
setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
return 0;
} catch(...) {
return -1;
}
int_type overflow(int_type c = TTraits::eof()) {
auto syncRet = sync();
if (c != TTraits::eof()) {
_buffer[0] = c;
setp(_buffer.data(), _buffer.data() + 1, _buffer.data() + _buffer.size());
}
return syncRet == -1 ? TTraits::eof() : 0;
}
private:
std::vector<TChar> _buffer;
template<typename TChar, typename TTraits>
struct MessageOutputer;
template<>
struct MessageOutputer<char,std::char_traits<char>> {
template<typename TIterator>
void operator()(TIterator begin, TIterator end) const {
std::string s(begin, end);
OutputDebugStringA(s.c_str());
}
};
template<>
struct MessageOutputer<wchar_t,std::char_traits<wchar_t>> {
template<typename TIterator>
void operator()(TIterator begin, TIterator end) const {
std::wstring s(begin, end);
OutputDebugStringW(s.c_str());
}
};
};
然后:
int main() {
#ifndef NDEBUG
#ifdef _WIN32
static OutputDebugStringBuf<char> charDebugOutput;
std::cerr.rdbuf(&charDebugOutput);
std::clog.rdbuf(&charDebugOutput);
static OutputDebugStringBuf<wchar_t> wcharDebugOutput;
std::wcerr.rdbuf(&wcharDebugOutput);
std::wclog.rdbuf(&wcharDebugOutput);
#endif
#endif
...
std::cerr << "Error: something bad happened" << std::endl; // will be displayed in the debugger
...
}
您可能希望将其与
一起使用的IsDebuggerPresent()
以便在不从Visual Studio调试器运行时仍然输出到控制台。
答案 1 :(得分:4)
直接的stdout重定向不起作用,因为没有与OutputDebugString对应的句柄。但是,应该有办法:
可以通过将stdout重定向到管道,然后创建一个线程来完成,该线程将读取管道并使用OutputDebugString打印从中读取的任何内容。
注意:很久以前我一直在考虑实现这个问题,因为我遇到的问题与你做的完全相同(有些库使用printf或fprintf(stderr ....),但是我从来没有真正这样做过,我总是结束修改库,因此我没有工作实现,但我认为它原则上应该是可行的。
答案 2 :(得分:3)
是。我假设您正在使用Win32 GUI应用程序。
您的c实现为stdin,stdout和stderr定义了3个句柄。 Win32定义了等效句柄,它定义了实际物理输入/输出的显示位置。 c函数如'printf',使用这些Win32句柄来执行i / o。基本上你要做的就是为输出创建一个控制台,然后重定向Win32标准输出指向的位置。然后获取c stdout的句柄并将其与Win32标准输出关联。
This link包含有关如何执行此操作的详细信息:
您需要向应用程序添加两个新文件(该链接包含列表)。
答案 3 :(得分:0)
我正在使用Visual Studio 2012,并且还希望在调试脚本,C ++程序或MSTest DLL时重定向 stdout 和 stderr ,而无需更改源代码代码本身。我最终想出的方法是使用一种中间程序来捕获输出。
获取以下C#代码并创建/编译Windows C#.NET控制台应用程序:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
namespace OutputDebugStringConsole
{
class OutputDebugStringConsole
{
private static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (null != outLine.Data)
{
Trace.WriteLine(outLine.Data);
Trace.Flush();
Console.WriteLine(outLine.Data);
}
}
static void Main(string[] args)
{
if (args.Length == 0)
{
return;
}
try
{
Process p = new Process();
p.StartInfo.FileName = args[0];
p.StartInfo.Arguments = String.Join(" ", args, 1, args.Length - 1);
Trace.WriteLine("Calling " + p.StartInfo.FileName + " " + p.StartInfo.Arguments);
p.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory();
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
p.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
p.WaitForExit();
// Call WaitForExit() AFTER I know the process has already exited by a successful return from WaitForExit(timeout).
// This causes the code that reads all remaining pending async data to be executed.
// see https://groups.google.com/d/msg/microsoft.public.dotnet.framework.sdk/jEen9Hin9hY/FQtEhjdKLmoJ
Thread.Sleep(100);
p.WaitForExit();
p.Close();
}
catch (Exception e)
{
Trace.WriteLine(e.ToString());
Console.WriteLine("{0} Exception caught.", e);
}
}
}
}
我使用了Trace.WriteLine()而不是Debug.WriteLine(),因为它也适用于上述代码的发行版本。
注意:如果您选择了.NET 4 / 4.5并且正在捕获非托管代码的输出,则需要选择混合作为调试/调试器类型你的项目设置。否则(使用自动),您可能会获得unhandled KernelBase.dll exception。
现在您可以通过放置新创建的
来使用该应用程序OutputDebugStringConsole.exe
进入调试/命令属性和
“$(TargetPath)”[ARGS ...]
或者例如如果是MSTest DLL
“$(DevEnvDir)CommonExtensions \微软\ TestWindow \ vstest.console.exe” /平台:x86 $(TargetPath)
进入要调试的应用程序的Debugging / Arguments。命令参数中的引号是处理应用程序路径中的空格所必需的。
请仅以此为例来说明应用程序的用途。我知道VS 2012 Test Explorer确实提供了一种运行MSTest DLL并以结构化方式获取输出的非常好的方法。