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

时间:2010-07-19 19:53:17

标签: .net wpf console console-application

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

作为一个小背景,起初,我试图在WPF中从头开始实现一个控制台窗口,除了一个巨大的问题之外它是成功的 - 它非常慢。请看这里的问题:
VT100 Terminal Emulation in Windows WPF or Silverlight

由于这似乎不是一个选项,我反而在我的WPF应用程序中托管一个实际的控制台窗口,我已经学会了如下所述:

No output to console from a WPF application?

这很棒,但理想情况下,我希望控制台窗口看起来像是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);

但是,有一个大问题。出于某种原因,控制台窗口具有渲染工件。就好像它不会在左下角和右上角重新粉刷一样。工件的宽度类似于标题栏的宽度和粗边框,事实上,如果我留下粗制边框的工件大小下降。但只是重新绘制它将无济于事,因为它再次出现。例如,我可以将窗口移出屏幕并再次返回以修复它,但很快就会重新出现:

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

更新2:即使我没有将其置于WindowsFormsHost控件中,也会发生这种效果。我需要做的就是重现它是启动控制台(使用AllocConsole())然后用SetWindowLong删除它的标题栏。这是一台win7机器。

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

2 个答案:

答案 0 :(得分:8)

除了Reed Copsey关于在WPF应用程序中嵌入控制台窗口的出色建议之外,一个非常容易实现的替代策略是简单地通过Process类发出命令并重定向两个流到本机WPF TextBlocks。这是截图...

enter image description here

此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'文件并捕获输出,然后调用View Model。整个编码演练大约需要一个小时......

Process类上的文档位于:http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx

答案 1 :(得分:7)

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