C++ 如何将标准输出从 Visual Studio 重定向到输出窗口

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5248704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:45:26  来源:igfitidea点击:

How to redirect standard output to output window from Visual Studio

c++visual-studiovisual-studio-2010

提问by Mircea Ispas

Is it possible to redirect standard output to the output window from Visual Studio?

是否可以将标准输出从 Visual Studio 重定向到输出窗口?

I use OutputDebugStringin my program, but I use some libraries that have output debug messages with printf's or cout's.

OutputDebugString在我的程序中使用,但我使用了一些带有 printf 或 cout 输出调试消息的库。

采纳答案by Suma

Straightforward standard output redirection will not work, as there is no handle corresponding to OutputDebugString. However, there should be a way:

直接的标准输出重定向将不起作用,因为没有对应于 OutputDebugString 的句柄。不过,应该有办法:

It could be done by redirecting standard output to a pipe, and then creating a thread which would read the pipe and print anything read from it using OutputDebugString.

这可以通过将标准输出重定向到管道,然后创建一个线程来完成,该线程将读取管道并使用 OutputDebugString 打印从中读取的任何内容。

Note: I was contemplating for a long ago to implement this, as I am facing exactly the same problem as you do (some libraries using printf or fprintf(stderr....). However, I never really did this. I have always ended modifying the libraries instead, and therefore I do not have a working implementation, but I think it should be feasible in principle.

注意:我很久以前就在考虑实现这一点,因为我面临着与您完全相同的问题(一些库使用 printf 或 fprintf(stderr ....)。但是,我从未真正做到过这一点。我一直结束修改库,因此我没有一个有效的实现,但我认为原则上应该是可行的。

回答by Ozirus

From Redirecting cerr and clog to OutputDebugString():

将 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());
        }
    };
};

Then:

然后:

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;

    ...
}

You might want to use it with

你可能想用它

IsDebuggerPresent()

IsDebuggerPresent()

so that it still outputs to console when not run from the Visual Studio debugger.

以便在不从 Visual Studio 调试器运行时仍输出到控制台。

回答by BeeBand

Yes. I'm assuming that you're working on a Win32 GUI application.

是的。我假设您正在开发 Win32 GUI 应用程序。

Your C implementation defines three handles for standard input, standard output, and standard error. Win32 defines equivalent handles, which define where the actual physical input/output will appear. C functions, such as 'printf', use these Win32 handles to perform I/O. Basically, you have to create a console for output, and then redirect where the Win32 standard output points to. And then getting the handle to the C standard output and associating this with the Win32 standard output.

您的 C 实现为标准输入、标准输出和标准错误定义了三个句柄。Win32 定义了等效句柄,它定义了实际物理输入/输出将出现的位置。C 函数,例如“printf”,使用这些 Win32 句柄来执行 I/O。基本上,您必须为输出创建一个控制台,然后重定向 Win32 标准输出指向的位置。然后获取 C 标准输出的句柄并将其与 Win32 标准输出相关联。

This linkcontains more information on how to do this:

此链接包含有关如何执行此操作的更多信息:

You'll need to add two new files to your application (the link contains the listings).

您需要向应用程序添加两个新文件(该链接包含列表)。

回答by Florian

I was using Visual Studio 2012 and also wanted to redirect standard outputand standard errorwhile debugging a script, a C++ program or a MSTest DLL without having to change the source code itself. My approach, that I finally came up with, was to capture the output using sort of an intermediate program.

我使用的是 Visual Studio 2012 并且还想在调试脚本、C++ 程序或 MSTest DLL 时重定向标准输出标准错误,而无需更改源代码本身。我最终想出的方法是使用某种中间程序来捕获输出。

Create C# Windows Console Application

创建 C# Windows 控制台应用程序

Take the following C# code and create/compile a Windows C# .NET Console Application:

使用以下 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);
            }
        }
    }
}

I used Trace.WriteLine() instead of Debug.WriteLine(), because it then works also in the release version of the above code.

我使用了 Trace.WriteLine() 而不是 Debug.WriteLine(),因为它也适用于上述代码的发行版。

Use Application in Visual Studio Project Debugging

在 Visual Studio 项目调试中使用应用程序

NOTE:If you have chosen .NET 4/4.5 and you are capturing the output of unmanaged code, you need to select Mixedas your Debugging/Debugger Type in your Project Settings. Otherwise (with Auto) you may get an unhandled KernelBase.dll exception.

注意:如果您选择了 .NET 4/4.5 并且您正在捕获非托管代码的输出,则需要在项目设置中选择混合作为您的调试/调试器类型。否则(使用Auto)您可能会遇到未处理的 KernelBase.dll 异常

Now you can use the application by putting the newly created

现在您可以通过放置新创建的应用程序来使用该应用程序

OutputDebugStringConsole.exe

输出调试字符串控制台.exe

into Debugging/Command properties and

进入调试/命令属性和

"$(TargetPath)" [ARGS ...]

"$(TargetPath)" [ARGS ...]

or e.g. if it is the MSTest DLL file:

或者例如,如果它是 MSTest DLL 文件:

"$(DevEnvDir)CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Platform:x86 $(TargetPath)

"$(DevEnvDir)CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Platform:x86 $(TargetPath)

into your Debugging/Argumentsof the application you want to debug. The quotation marks in the command arguments are necessary to handle spaces in your application's path.

进入您要调试的应用程序的调试/参数。命令参数中的引号对于处理应用程序路径中的空格是必需的。

Please take this only as an example what the application can be used for. I'm aware that the Visual Studio 2012 Test Explorer does offer a very nice way to run MSTest DLL files and get the output in a structured way.

请仅以该应用程序的用途为例。我知道 Visual Studio 2012 测试资源管理器确实提供了一种非常好的方式来运行 MSTest DLL 文件并以结构化方式获取输出。