Можно ли перенаправить стандартный вывод в окно вывода из Visual Studio?
Я использую OutputDebugString
в своей программе, но я использую некоторые библиотеки, которые имеют выходные сообщения отладки с printf или cout.
Можно ли перенаправить стандартный вывод в окно вывода из Visual Studio?
Я использую OutputDebugString
в своей программе, но я использую некоторые библиотеки, которые имеют выходные сообщения отладки с printf или cout.
Простое стандартное перенаправление вывода не будет работать, так как нет никакого дескриптора, соответствующего OutputDebugString. Однако должен быть способ:
Это можно сделать, перенаправив стандартный вывод в канал, а затем создав поток, который будет читать канал и печатать что-либо из него, используя OutputDebugString.
Примечание: я давно обдумывал это, так как сталкиваюсь с той же проблемой, что и вы (некоторые библиотеки, использующие printf или fprintf (stderr....). Однако я никогда этого не делал. Я всегда вместо этого закончилась модификация библиотек, и поэтому у меня нет работающей реализации, но я думаю, что это должно быть осуществимо в принципе.
Из перенаправления cerr и clog в OutputDebugString():
#include <ostream>
#include <Windows.h>
/// \brief This class is derives from 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
...
// Will be displayed in the debugger
std::cerr << "Error: something bad happened" << std::endl;
...
}
Возможно, вы захотите использовать его с
IsDebuggerPresent()
так что он все еще выводит на консоль, когда не запускается из отладчика Visual Studio.
Да. Я предполагаю, что вы работаете над Win32 GUI-приложением.
Ваша реализация C определяет три дескриптора для стандартного ввода, стандартного вывода и стандартной ошибки. Win32 определяет эквивалентные дескрипторы, которые определяют, где будет отображаться фактический физический ввод/вывод. Функции C, такие как printf, используют эти дескрипторы Win32 для выполнения операций ввода-вывода. По сути, вы должны создать консоль для вывода, а затем перенаправить туда, куда указывает стандартный вывод Win32. А затем получить дескриптор для стандартного вывода C и связать его со стандартным выводом Win32.
Эта ссылка содержит дополнительную информацию о том, как это сделать:
Вам нужно будет добавить два новых файла в ваше приложение (ссылка содержит списки).
Я использовал Visual Studio 2012, а также хотел перенаправить стандартный вывод и стандартную ошибку при отладке скрипта, программы C++ или MSTest DLL без необходимости изменения самого исходного кода. Мой подход, который я наконец-то придумал, состоял в том, чтобы захватить вывод, используя своего рода промежуточную программу.
Возьмите следующий код С# и создайте/скомпилируйте консольное приложение Windows С#.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 и захватывает вывод неуправляемого кода, вам необходимо выбрать Смешанный в качестве типа отладки/отладчика в настройках проекта. В противном случае (с помощью Авто) вы можете получить необработанное исключение KernelBase.dll.
Теперь вы можете использовать приложение, поместив только что созданный
OutputDebugStringConsole.exe
в свойствах отладки/команд и
"$(TargetPath)" [ARGS...]
или, например, если это файл DLL MSTest:
"$(DevEnvDir)CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Platform:x86 $(TargetPath)
в ваши отладки/аргументы приложения, которое вы хотите отладить. Кавычки в аргументах команды необходимы для обработки пробелов в пути приложения.
Пожалуйста, возьмите это только в качестве примера, для чего может использоваться приложение. Я знаю, что Visual Studio 2012 Test Explorer предлагает очень хороший способ запуска файлов MSTest DLL и получения выходных данных в структурированном виде.