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

时间:2022-12-30 20:43:32

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

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

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 -- its extremely slow. See the question here:
VT100 Terminal Emulation in Windows WPF or Silverlight

作为一个小背景,最初我尝试从头开始在WPF中实现一个控制台窗口,这是成功的,除了一个巨大的问题——它非常慢。请参见这里的问题:Windows WPF或Silverlight中的VT100终端仿真

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:

由于这似乎不是一个选项,我正在考虑在我的WPF应用程序中托管一个实际的控制台窗口。

No output to console from a WPF application?

没有来自WPF应用程序的控制台输出?

And that's great, by 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:

这很好,理想情况下,我希望控制台窗口看起来像是WPF应用程序的一部分。我知道使用WinForms应用是有可能的,就像我看到的那样,包括使用SetParent Win32 API。您可以看到一个示例。net项目,它使用命令栏项目将控制台窗口嵌入shell:

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

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).

所以我希望WPF也能做到,但我不知道你会怎么做。非常感谢您的帮助(另外,如果您对我最初在WPF中从零开始创建终端窗口的问题有任何出色的解决方案,那么这也将解决我的需求)。

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:

在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);

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

使工件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 it's title bar with SetWindowLong. This is a win7 machine.

更新2:即使我不在WindowsFormsHost控件中添加它,也会产生这种效果。复制它所需要做的就是启动控制台(使用AllocConsole()),然后用SetWindowLong删除它的标题栏。这是一台win7机器。

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.

更新3:似乎不支持像这样与其他窗口“搞混”。控制台窗口会计算它的textarea,假设有一个标题,所以没有办法绕过这个。我认为在WPF中获得类似conso的行为的唯一选择是编写一个自定义WinForms控件,然后将其嵌入到WPF中。

2 个解决方案

#1


6  

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.

您应该能够使用与在HwndHost中重新养育所显示的Windows窗体应用程序相同的技术。您甚至可以调整Windows窗体代码,并将其直接放到WindowsFormsHost控件中。

#2


5  

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...

除了Reed Copsey关于在WPF应用程序中嵌入控制台窗口的优秀建议之外,另一种非常容易实现的策略是通过Process类发出命令并将两个流重定向到本地WPF文本块。这是一个截图……

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

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

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

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.

当你想要运行一个控制台实用程序时,当你点击它的时候,这个实用程序会在一个控制台窗口中快速运行,你永远不会看到发生了什么。它还连接到“csproj”文件,以便从Explorer中运行MSBuild。

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...

这个类执行命名为exe的文件并捕获输出,然后调用视图模型。整个编码训练大约需要一个小时左右。

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

Process类中的文档在这里:http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx

#1


6  

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.

您应该能够使用与在HwndHost中重新养育所显示的Windows窗体应用程序相同的技术。您甚至可以调整Windows窗体代码,并将其直接放到WindowsFormsHost控件中。

#2


5  

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...

除了Reed Copsey关于在WPF应用程序中嵌入控制台窗口的优秀建议之外,另一种非常容易实现的策略是通过Process类发出命令并将两个流重定向到本地WPF文本块。这是一个截图……

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

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

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

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.

当你想要运行一个控制台实用程序时,当你点击它的时候,这个实用程序会在一个控制台窗口中快速运行,你永远不会看到发生了什么。它还连接到“csproj”文件,以便从Explorer中运行MSBuild。

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...

这个类执行命名为exe的文件并捕获输出,然后调用视图模型。整个编码训练大约需要一个小时左右。

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

Process类中的文档在这里:http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx