【问题标题】:Is it possible to create a console window (as created by AllocConsole) as a GUI child window? If so, how?是否可以将控制台窗口(由 AllocConsole 创建)创建为 GUI 子窗口?如果是这样,怎么做?
【发布时间】:2019-02-21 15:56:07
【问题描述】:

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

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

我使用的两种技术是:

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

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

据我所知,一旦创建窗口,就无法更改其父窗口,创建新控制台窗口的唯一方法是 AllocConsole,它不带任何参数,因此父窗口(例如 Forms主机)无法指定。我错过了什么吗?

我的问题不是使用 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)

并解决了窗口大小调整问题,但鼠标交互仍然存在问题 - 鼠标右键可以激活上下文菜单,但鼠标左键无法进行选择。

【问题讨论】:

  • @eocron 我一直在使用 TextBox 和/或其他 GUI 控件来模拟控制台窗口;他们似乎都有这样或那样的问题。这个问题的目的是探索在 WPF 父级中托管实际控制台窗口的可能性。
  • 我见过很多这样使用的控件,我自己也有一个。将控制台附加到您的 WPF 应用程序在许多方面都是不明智的。
  • @eocron 我想更好地理解这些问题。请看我的相关问题stackoverflow.com/questions/54830047/…

标签: c# wpf console


【解决方案1】:

根据我收集到的信息,您的意图仅用于输出(而非输入),那么像 Console.SetOut 这样简单的东西就可以满足要求。

这会将控制台输出重定向到您选择的TextWriter。然后,您可以随意显示。

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

但这是一种快速而简单的方法,可以将任何输出读取到控制台并根据需要进行显示。如果您愿意,也可以重定向到文件。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 2011-07-08
    • 2011-02-22
    • 2020-05-05
    • 1970-01-01
    • 2018-04-14
    • 2010-09-08
    相关资源
    最近更新 更多