c# - 将 cout 从 C++ dll 重定向到 C# 中的文本框

标签 c# c++ dll console

我正在尝试将 dll 中方法的控制台输出 (cout) 显示到 C# 程序中的文本框。每次调用该方法时,控制台输出都会显示在 Visual Studio 的输出 Pane 中。有没有办法将输出 Pane 的内容重定向到文本框?

dll 是由其他人用 C++ 编写的,我无法更改它。 该 dll 使用 SWIG 包装,以便我的 C# 代码可以调用它。

最佳答案

在点击 David 建议的链接后,我决定针对您的问题编写一个更具体的解决方案。此版本允许您通过 BackgroundWorker PropertyChangedEventHandler 回调在 GUI 中接收标准输出。

这是 ConsoleRedirector 的代码:

public class ConsoleRedirector : IDisposable
{
    private static ConsoleRedirector _instance;

    public static void attach(ProgressChangedEventHandler handler, bool forceConsoleRedirection)
    {
        Debug.Assert(null == _instance);
        _instance = new ConsoleRedirector(handler, forceConsoleRedirection);

    }

    public static void detatch()
    {
        _instance.Dispose();
        _instance = null;
    }

    public static bool isAttached
    {
        get
        {
            return null != _instance;
        }
    }

    private static void ResetConsoleOutStream()
    {
        //Force console to recreate its output stream the next time Write/WriteLine is called
        typeof(Console).GetField("_out", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null);
    }

    private const int PERIOD = 500;
    private const int BUFFER_SIZE = 4096;
    private volatile bool _isDisposed;
    private BackgroundWorker _worker;
    private readonly IntPtr _stdout;
    private readonly Mutex _sync;
    private readonly System.Threading.Timer _timer;
    private readonly char[] _buffer;
    private readonly AnonymousPipeServerStream _outServer;
    private readonly TextReader _outClient;
    private readonly bool _forceConsoleRedirection;

    private StreamWriter _consoleStandardOut;

    private ConsoleRedirector(ProgressChangedEventHandler handler, bool forceConsoleRedirection)
    {
        bool ret;
        _forceConsoleRedirection = forceConsoleRedirection;

        if (!_forceConsoleRedirection)
        {
            //Make sure Console._out is initialized before we redirect stdout, so the redirection won't affect it
            TextWriter temp = Console.Out;
        }

        AnonymousPipeClientStream client;

        _worker = new BackgroundWorker();
        _worker.ProgressChanged += handler;
        _worker.DoWork += _worker_DoWork;
        _worker.WorkerReportsProgress = true;

        _stdout = GetStdHandle(STD_OUTPUT_HANDLE);

        _sync = new Mutex();
        _buffer = new char[BUFFER_SIZE];

        _outServer = new AnonymousPipeServerStream(PipeDirection.Out);
        client = new AnonymousPipeClientStream(PipeDirection.In, _outServer.ClientSafePipeHandle);
        Debug.Assert(_outServer.IsConnected);
        _outClient = new StreamReader(client, Encoding.Default);
        ret = SetStdHandle(STD_OUTPUT_HANDLE, _outServer.SafePipeHandle.DangerousGetHandle());
        Debug.Assert(ret);

        if (_forceConsoleRedirection)
        {
            ResetConsoleOutStream(); //calls to Console.Write/WriteLine will now get made against the redirected stream
        }

        _worker.RunWorkerAsync(_outClient);

        _timer = new System.Threading.Timer(flush, null, PERIOD, PERIOD);

    }

    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        TextReader client = (TextReader)e.Argument;
        try
        {
            while (true)
            {
                int read = client.Read(_buffer, 0, BUFFER_SIZE);
                if (read > 0)
                    worker.ReportProgress(0, new string(_buffer, 0, read));
            }
        }
        catch (ObjectDisposedException)
        {
            // Pipe was closed... terminate

        }
        catch (Exception ex)
        {

        }
    }

    private void flush(object state)
    {
        _outServer.Flush();
    }

    public void Dispose()
    {
        Dispose(true);
    }

    ~ConsoleRedirector()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            lock (_sync)
            {
                if (!_isDisposed)
                {
                    _isDisposed = true;
                    _timer.Change(Timeout.Infinite, Timeout.Infinite);
                    _timer.Dispose();
                    flush(null);

                    try { SetStdHandle(STD_OUTPUT_HANDLE, _stdout);}
                    catch (Exception) { }
                    _outClient.Dispose();
                    _outServer.Dispose();

                    if (_forceConsoleRedirection)
                    {
                        ResetConsoleOutStream(); //Calls to Console.Write/WriteLine will now get redirected to the original stdout stream
                    }

                }
            }
        }
    }

    private const int STD_OUTPUT_HANDLE = -11;

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetStdHandle(int nStdHandle);
}

这是一个完整示例表单的链接,演示了如何使用它:ConsoleRedirector Example Form

关于c# - 将 cout 从 C++ dll 重定向到 C# 中的文本框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9061655/

相关文章:

c# - 将方法转换为 Linq 表达式以供查询

c++ - cudaMalloc 问题和 "out of memory"错误

c++ - 数组作为参数

c# - 是什么导致对象浏览器中 DLL 中的属性命名空间?

c - 使用 C DLL 的 VB.Net

C# 错误 CS0117 : 'Array' does not contain a definition for

c# - SignalR - 仅适用于本地主机

c# - 我可以使用 hwnd/NativeWindow 获得设置我的 WinForms 窗体所有者的行为吗?

c++ - 为什么在元组初始化中复制构造的对象?

c++ - 将函数名称从 C++ Dll 导出到 Delphi