是否可以将控制台窗口(由AllocConsole创建)创建为GUI子窗口?如果可以,怎么办?

时间:2019-02-21 15:56:07

标签: c# wpf console

出于各种目的,我希望在WPF应用程序中包括控制台窗口功能,包括:

  • 屏幕记录
  • 显示由应用程序执行的控制台命令的结果
  • 诊断

我使用的两种技术是:

  • 创建一个TextBox并通过分配或数据绑定设置其Text属性。这样可以很好地将视图集成到应用程序中,但是随着文本“ buffer”变大,会出现性能问题。
  • AllocConsole进行互操作以创建控制台窗口。相对较大的文本“缓冲区”没有性能问题,但创建为顶层窗口(从视觉上与应用程序分开,带有自己的标题栏,等等)。

理想情况下,我想创建一个控制台窗口(例如AllocConsole)作为WPF应用程序窗口的子窗口,类似于将Forms窗口托管为WPF应用程序的子窗口的方式,住在标签等中。

据我所知,创建窗口后无法更改其父窗口,创建新控制台窗口的唯一方法是AllocConsole,并且不带任何参数,因此父窗口(例如窗体)主机)无法指定。我想念什么吗?

我的问题的重点不是使用TextBox,ListBox等来近似/模拟类似于控制台的行为,而是将实际的控制台窗口作为WPF应用程序的GUI元素托管。

编辑

我到目前为止的位置:

XAML主窗口的片段:

<TabControl>
    <TabItem Header="Log">
        <Grid>
            <WindowsFormsHost x:Name="FormsHost">

            </WindowsFormsHost>
        </Grid>
    </TabItem>
</TabControl>

主窗口代码背后:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Forms;

public partial class MainWindow : Window
{
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool AllocConsole();

    [DllImport("kernel32.dll")]
    static extern IntPtr GetConsoleWindow();

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

    [DllImport("user32.dll")]
    public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

    [DllImport("user32.dll")]
    public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    public MainWindow()
    {
        InitializeComponent();

        AllocConsole();

        var newOut = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
        Console.SetOut(newOut);
        Console.SetError(newOut);

        var ptr = GetConsoleWindow();

        FormsHost.Child = new Control();

        SetWindowLong(ptr, -16, GetWindowLong(ptr, -16) | 0x40000000);
        SetParent(ptr, FormsHost.Child.Handle);
        SetWindowPos(ptr, IntPtr.Zero, 0, 0, 300, 300, 0);

        Console.WriteLine("Hello, World");
    }
}

以上内容确实将控制台窗口带入WPF选项卡,但是它仍然具有顶级窗口的外观,并且在其宿主中仍可调整大小和移动。另外,虽然我可以在控制台区域中单击鼠标右键以获取用于文本选择的上下文菜单,但不能使用鼠标来选择文本。

更新

我通过更改解决了窗口样式的问题

SetWindowLong(ptr, -16, GetWindowLong(ptr, -16) | 0x40000000)

SetWindowLong(ptr, -16, 0x50000000)

并调整了窗口的大小,但是鼠标交互仍然存在问题-鼠标右键可以激活上下文菜单,而鼠标左键则不能。

1 个答案:

答案 0 :(得分:2)

从我收集到的信息来看,您的意图仅是用于输出(而不是输入),然后像Console.SetOut这样的简单内容就可以了。

这会将控制台输出重定向到您选择的TextWriter。然后可以根据需要显示。

当然,您也可以使用许多日志记录库之一,而不用写入控制台。或使用类似Debug.Write之类的东西...

但这是一种快速简单的方法,可读取输出到控制台的所有内容并根据需要显示。或根据需要重定向到文件。

关于性能,我必须查看您使用的代码。我个人会在大量日志记录数据上使用ListBox而不是TextBox