在 WPF 窗口中嵌入控制台窗口

发布于 2024-09-10 12:01:10 字数 2129 浏览 1 评论 0 原文

是否可以在 WPF 窗口中嵌入控制台窗口?

作为一个小背景,首先,我尝试在 WPF 中从头开始实现一个控制台窗口,除了一个巨大的问题之外,它是成功的 - 它非常慢。请参阅此处的问题:
Windows WPF 或 Silverlight 中的 VT100 终端仿真

因为这并不似乎是一个选项,我正在考虑在我的 WPF 应用程序中托管一个实际的控制台窗口,我已经学会了如何按照此处所述进行操作:

WPF 应用程序没有输出到控制台?

这很好,但理想情况下,我希望控制台窗口看起来像一个WPF 应用程序其余部分的一部分。我知道使用 WinForms 应用程序是可能的,正如我所看到的那样,涉及使用 SetParent Win32 API。您可以看到一个示例 .NET 项目,它使用此 CommandBar 项目来实现此目的,该项目将控制台窗口嵌入到 shell 中:

http://www.codeproject.com/KB/cs/commandbar.aspx

所以我希望它也可以用 WPF 来完成,但我不知道你会如何做到这一点。非常感谢您的帮助(另外,如果您对我在 WPF 中从头开始创建终端窗口的最初问题有任何出色的解决方案,因为这也可以解决我的需求)。

更新:

在 Reed Copsey 的帮助下,我能够嵌入控制台窗口。然而,当然它需要设计样式和移动,否则它看起来就像 WPF 窗口中的常规控制台窗口。我需要删除标题栏和大边框。通过研究,我弄清楚了如何使用 Win32 API 来做到这一点,如下所示:

uint style = GetWindowLong(ConsoleManager.ConsoleWindowHandle, GWL_STYLE);
style &= ~(uint)WindowStyles.WS_CAPTION;
style &= ~(uint)WindowStyles.WS_THICKFRAME;
style &= ~(uint)WindowStyles.WS_DLGFRAME;
style &= ~(uint)WindowStyles.WS_POPUP;
SetWindowLong(ConsoleManager.ConsoleWindowHandle, GWL_STYLE, style);
MoveWindow(ConsoleManager.ConsoleWindowHandle, 0, 0, (int)WindowsFormsHost.ActualWidth, (int)WindowsFormsHost.ActualHeight, true);

但是,有一个大问题。由于某种原因,控制台窗口有渲染伪影。就好像它没有在左下角和右上角重新绘制自己。工件的宽度与标题栏和粗边框的宽度类似,事实上,如果我保留粗边框,工件的大小就会下降。但仅仅重新粉刷它并没有什么帮助,因为它会重新出现。例如,我可以将窗口移出屏幕并再次移回以修复它,但它很快就会自行重新出现:

渲染工件 http://img837.imageshack.us/img837/6241/renderissue.png

更新 2: 即使我不为它设置父级,效果也会发生进入 WindowsFormsHost 控件。重现它所需要做的就是启动控制台(使用 AllocConsole()),然后使用 SetWindowLong 删除其标题栏。这是win7的机器。

更新3:似乎不支持像这样与其他窗口“混淆”。控制台窗口在假设有标题的情况下计算其文本区域,因此无法解决此问题。我认为在 WPF 中获得类似控制台的行为的唯一选择是编写自定义 WinForms 控件,然后将其嵌入到 WPF 中。

Is it possible to embed a console window inside a WPF window?

As a little background, at first, I tried to implement a console window from scratch in WPF, which was successful except for one huge problem -- it is extremely slow. See the question here:
VT100 Terminal Emulation in Windows WPF or Silverlight

Since that does not seem to be an option I am instead looking at hosting an actual console window in my WPF application, which I've learned how to do as described here:

No output to console from a WPF application?

And that's great, but ideally, I'd like to have that console window look like it is a part of the rest of the WPF application. I know it is possible with a WinForms app as I've seen it done, involving using the SetParent Win32 API. You can see an example .NET project that does it with this CommandBar project that embeds a console window into the shell:

http://www.codeproject.com/KB/cs/commandbar.aspx

So I am hopeful it can be done with WPF as well, but I have no idea how you'd do that. Help is much appreciated (also, if you have any brilliant solutions to my original problem of creating a terminal window from scratch in WPF since that would solve my needs too).

UPDATE:

With help Reed Copsey's help I was able to get the Console Window embedded. However, of course it needed to be styled and moved or else it just looked like a regular console window inside a WPF window. I need the title bar and large borders removed. Doing research I figured out how to use the Win32 APIs to do that like this:

uint style = GetWindowLong(ConsoleManager.ConsoleWindowHandle, GWL_STYLE);
style &= ~(uint)WindowStyles.WS_CAPTION;
style &= ~(uint)WindowStyles.WS_THICKFRAME;
style &= ~(uint)WindowStyles.WS_DLGFRAME;
style &= ~(uint)WindowStyles.WS_POPUP;
SetWindowLong(ConsoleManager.ConsoleWindowHandle, GWL_STYLE, style);
MoveWindow(ConsoleManager.ConsoleWindowHandle, 0, 0, (int)WindowsFormsHost.ActualWidth, (int)WindowsFormsHost.ActualHeight, true);

However, there's one big problem. For some reason, the console window has a rendering artifact. It's as if it is not repainting itself on the bottom left and top right sides. The width of the artifact is similar to the width of the title bar and the thick border, and in fact, if I leave the thick border in the size of the artifact goes down. But simply repainting it won't help since it reappears. I can, for example, move the window off the screen and back again to fix it, but it soon reappears on its own:

rendering artifact http://img837.imageshack.us/img837/6241/renderissue.png

UPDATE 2: The effect happens even if I don't parent it into the WindowsFormsHost control. All I need to do to reproduce it is launch the console (using AllocConsole()) and then remove its title bar with SetWindowLong. This is a win7 machine.

UPDATE 3: It seems 'messing' with other windows like this isn't supported. The console window calculates its textarea assuming there is a caption, so there's no way around this. I think my only option to get console-like behavior in WPF is going to be to write a custom WinForms control and then embed that into WPF.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神经暖 2024-09-17 12:01:10

除了 Reed Copsey 关于在 WPF 应用程序中嵌入控制台窗口的出色建议之外,还有一种替代策略:实现起来极其简单,只需通过 Process 类发出命令并将两个流重定向到本机 WPF TextBlock 中即可。这是屏幕截图...

在此处输入图像描述

此 WPF 应用程序(连接到“exe”文件的 Windows 资源管理器上下文菜单)执行程序并将结果通过管道传送到适当的窗口。

它旨在帮助您运行控制台实用程序,当您单击它时,该实用程序会在控制台窗口中呼啸而过,而您永远看不到发生了什么。它还连接到“csproj”文件,以便从资源管理器对其运行 MSBuild。

关键是,有时自己做比尝试托管控制台窗口更容易且更具可扩展性...

该应用程序的内部使用此类...

public class ProcessPiper
{
    public string StdOut { get; private set; }
    public string StdErr { get; private set; }
    public string ExMessage { get; set; }
    public void Start(FileInfo exe, string args, Action<ProcessPiper>onComplete)
    {
        ProcessStartInfo psi = new ProcessStartInfo(exe.FullName, args);
        psi.RedirectStandardError = true;
        psi.RedirectStandardOutput = true;
        psi.UseShellExecute = false;
        psi.WorkingDirectory = Path.GetDirectoryName(exe.FullName);
        Task.Factory.StartNew(() =>
            {
                try
                {
                    ExMessage = string.Empty;
                    Process process = new Process();
                    process.StartInfo = psi;
                    process.Start();
                    process.WaitForExit();
                    StdOut = process.StandardOutput.ReadToEnd();
                    StdErr = process.StandardError.ReadToEnd();
                    onComplete(this);
                }
                catch (Exception ex)
                {
                    ExMessage = ex.Message;
                }
            });
    }
}

此类执行名为“exe”的文件并捕获输出和然后调用视图模型。整个编码练习大约需要一个小时左右...

有关 Process 类的文档位于:

In addition to Reed Copsey's excellent advice on embedding a console window in a WPF application, an alternative strategy which is ridiculously easy to implement would be to simply issue the command via the Process class and redirect the two streams into native WPF TextBlocks. Here's a screenshot...

enter image description here

This WPF app (wired to the Windows Explorer context menu for 'exe' files) executes the program and pipes the results into the appropriate window.

It's designed to help when you want to run a console utility and when you click it, the utility goes whizzing by in a console window and you never get to see what happened. It is also wired to 'csproj' files to run MSBuild on them from Explorer.

The point being that sometimes it's easier and more scalable to do it yourself rather than try to host a console window...

The internals of this app use this class...

public class ProcessPiper
{
    public string StdOut { get; private set; }
    public string StdErr { get; private set; }
    public string ExMessage { get; set; }
    public void Start(FileInfo exe, string args, Action<ProcessPiper>onComplete)
    {
        ProcessStartInfo psi = new ProcessStartInfo(exe.FullName, args);
        psi.RedirectStandardError = true;
        psi.RedirectStandardOutput = true;
        psi.UseShellExecute = false;
        psi.WorkingDirectory = Path.GetDirectoryName(exe.FullName);
        Task.Factory.StartNew(() =>
            {
                try
                {
                    ExMessage = string.Empty;
                    Process process = new Process();
                    process.StartInfo = psi;
                    process.Start();
                    process.WaitForExit();
                    StdOut = process.StandardOutput.ReadToEnd();
                    StdErr = process.StandardError.ReadToEnd();
                    onComplete(this);
                }
                catch (Exception ex)
                {
                    ExMessage = ex.Message;
                }
            });
    }
}

This class executes the named 'exe' file and captures the output and then calls the View Model. The whole coding drill should take about an hour or so...

Docs on the Process class are here: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx

情归归情 2024-09-17 12:01:10

您应该能够使用与您展示的 Windows 窗体应用程序相同的技术,方法是将父级重新设置为 HwndHost。您甚至可以调整 Windows 窗体代码,并将其直接放入 WindowsFormsHost 控件。

You should be able to use the same technique as the Windows Forms application you showed by reparenting into an HwndHost. You could even just adapt the Windows Forms code, and put this directly into WindowsFormsHost control.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文