如何从视觉工作室将stdout重定向到输出窗口

时间:2011-03-09 16:18:45

标签: c++ visual-studio visual-studio-2010

是否可以将stdout重定向到Visual Studio的输出窗口?我在我的程序OutputDebugString中使用但是我使用了一些带有printf或cout的输出调试消息的库。

4 个答案:

答案 0 :(得分:10)

来自https://web.archive.org/web/20140825203329/http://blog.tomaka17.com/2011/07/redirecting-cerr-and-clog-to-outputdebugstring/

#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#代码并创建/编译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(),因为它也适用于上述代码的发行版本。

在VS Project调试中使用应用程序

注意:如果您选择了.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并以结构化方式获取输出的非常好的方法。