【问题标题】:Output Console.WriteLine from WPF Windows Applications to actual console从 WPF Windows 应用程序输出 Console.WriteLine 到实际控制台
【发布时间】:2012-05-02 14:28:50
【问题描述】:

背景:我正在努力将命令行和批处理功能添加到现有的 WPF Windows 应用程序。当我在启动时检测到一些选项时,我会抑制窗口出现,进行一些处理并立即退出。现在,因为没有 UI,我想将一些消息输出到 stdout/stderr。考虑以下代码:

namespace WpfConsoleTest
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            Console.WriteLine("Start");
            System.Threading.Thread.Sleep(1000);
            Console.WriteLine("Stop");
            Shutdown(0);
        }
    }
}

当我从命令行运行时,我希望得到以下输出:

Start
Stop

但是:

C:\test>WpfConsoleTest.exe

C:\test>

不过,您可以重定向输出:

C:\test>WpfConsoleTest.exe > out.txt

C:\test>type out.txt
Start
Stop

不幸的是,重定向到 CON 不起作用:

C:\test>WpfConsoleTest.exe > CON

C:\test>

另一个问题是 WpfConsoleTest.exe 在启动后立即退出。所以:

C:\test>WpfConsoleTest.exe > out.txt & type out.txt

C:\test>

但是:

C:\test>WpfConsoleTest.exe > out.txt & ping localhost > nul & type out.txt
Start
Stop

到目前为止我能想到的最好的解决方案是使用start /B /wait

C:\test>start /B /wait WpfConsoleTest.exe > out.txt & type out.txt
Start
Stop

这种方法大部分都可以——如果你把它包装在一个蝙蝠中,你可以保留错误代码等等。一个巨大的缺点是您在应用程序结束后获得输出,即您无法跟踪进度,您必须等待正在发生的事情完成。

因此,我的问题是:如何从 WPF 输出到父控制台Windows 应用程序另外,为什么从 WPF 中获取 stdout/stderr 这么难? p>

我知道我可以在项目设置中将应用程序类型更改为 控制台应用程序,但这有一个令人讨厌的副作用 - 控制台窗口始终可见,即使您只是双击 exe。 This solution 也不会这样做,因为它会创建一个新的控制台,即使应用程序是从 cmd 运行的。

编辑:澄清一下,我希望我的应用程序输出到 现有 控制台(如果有),如果缺少则创建一个新控制台。 p>

【问题讨论】:

标签: c# wpf batch-file cmd


【解决方案1】:

经过一番挖掘,我找到了this answer。现在的代码是:

namespace WpfConsoleTest
{
    public partial class App : Application
    {
        [DllImport("Kernel32.dll")]
        public static extern bool AttachConsole(int processId);

        protected override void OnStartup(StartupEventArgs e)
        {
            AttachConsole(-1);
            Console.WriteLine("Start");
            System.Threading.Thread.Sleep(1000);
            Console.WriteLine("Stop");
            Shutdown(0);
        }
    }
}

直接调用exe还是有一个讨厌的副作用,与调用立即返回有关:

C:\test>WpfConsoleTest.exe

C:\test>Start
Stop

^^^^
The cursor will stay here waiting for the user to press enter!

解决方案再次使用start

C:\test>start /wait WpfConsoleTest.exe
Start
Stop

感谢您的意见!

【讨论】:

  • 谢谢你,gwiazdorrr。这可能是更好的解决方案。但我仍然对它造成的不良影响不满意。您是否找到了更好的解决方案?
  • @BorisModylevsky:恐怕不行,我决定使用批处理脚本调用的start 版本。
  • 太好了,比将应用程序更改为控制台并在启动时隐藏控制台窗口要好得多(糟糕的体验,因为控制台窗口会在 UI 打开之前短暂出现)
  • AttachConsole 的问题是应用程序完成后,用户必须点击“进入”才能继续使用控制台(它似乎挂起)。您可以调用 SendKeys.SendWait("{ENTER}");最后释放控制台。
  • 我这样做了,但我没有看到控制台窗口
【解决方案2】:

我过去所做的是使其成为控制台应用程序,并在显示 WPF 窗口后调用 P/Invoke 以隐藏应用程序的关联控制台窗口:

//added using statements per comments...
using System.Diagnostics;
using System.Runtime.InteropServices;

    internal sealed class Win32
    {
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        public static void HideConsoleWindow()
        {
            IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

            if (hWnd != IntPtr.Zero)
            {
                ShowWindow(hWnd, 0); // 0 = SW_HIDE
            }
        }
    }

【讨论】:

  • 这是迄今为止最好的解决方案。我只是不喜欢在显示窗口之前出现控制台窗口,我认为这可能会让普通用户感到不安和困惑。
  • 很酷的解决方案,虽然我认为我同意@gwiazdorrr,但对于公用事业项目,我认为这是可以接受的......只是不确定纯粹的“生产”项目。
  • 添加 using System.Diagnostics;using System.Runtime.InteropServices; 供任何想要编译它并想知道命令位于哪些命名空间中的人使用。
  • 下面的解决方案要好得多。在启动时将其保留为 Window 应用程序和 AttachConsole。这样控制台窗口就不会在 UI 之前短暂出现(看起来不专业)
【解决方案3】:

默认情况下,WPF 应用程序不会有控制台,但您可以轻松地为它创建一个控制台,然后像在控制台应用程序中一样写入它。我以前用过这个助手类:

public class ConsoleHelper
{
    /// <summary>
    /// Allocates a new console for current process.
    /// </summary>
    [DllImport("kernel32.dll")]
    public static extern Boolean AllocConsole();

    /// <summary>
    /// Frees the console.
    /// </summary>
    [DllImport("kernel32.dll")]
    public static extern Boolean FreeConsole();
}

要在您的示例中使用它,您应该能够这样做:

namespace WpfConsoleTest
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            ConsoleHelper.AllocConsole(); 
            Console.WriteLine("Start");
            System.Threading.Thread.Sleep(1000);
            Console.WriteLine("Stop");
            ConsoleHelper.FreeConsole();
            Shutdown(0);
        }
    }
}

现在,如果您从命令行运行它,您应该会看到“开始”和“停止”写入控制台。

【讨论】:

  • 是的,但我会看到一个 new 控制台窗口,即使我是从命令行启动它。我想在 existing 控制台中查看输出。
  • 嗯...这在 WinForms 中对我来说效果很好,但似乎 WPF 做了一些不同的事情。您可以尝试 AttachConsole (msdn.microsoft.com/en-us/library/windows/desktop/…),但您需要获取启动应用程序的进程的句柄(可能是 cmd.exe)
  • 要获取父进程,这可能会有所帮助:rhyous.com/2010/04/30/…
  • 效果很好。仅适用于一些新手...这是所需的代码... protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); ConsoleHelper.AllocConsole(); } 受保护的覆盖无效 OnExit(ExitEventArgs e) { ConsoleHelper.FreeConsole(); base.OnExit(e); }
  • 这个提议并不是所有的解决方案。在这里,我们总是使用 AllocConsole 创建一个新控制台,无论我们是否已经打开了一个。这意味着当您从命令行启动 WPF 应用程序并且在程序结束时消息全部消失(因为额外的控制台已关闭)时,您仍然无法 console.writeline 到命令行。
【解决方案4】:

在项目属性中,您可以将项目类型从 Windows 应用程序更改为控制台应用程序,而 AFAIK 唯一的区别是您可以在应用程序的整个生命周期中使用控制台。不过,我还没有用 WPF 尝试过。

如果你想打开和关闭一个控制台,你应该使用Alloc/FreeConsole,但是更改项目类型通常更简单。

【讨论】:

  • 正如我所写,如果您双击 exe,更改为控制台应用程序将创建一个控制台窗口。我想要的是将输出打印到 existing 控制台,如果没有,请不要创建一个新控制台。
猜你喜欢
  • 2010-09-14
  • 2015-11-18
  • 2011-12-08
  • 2023-03-23
  • 2010-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-30
相关资源
最近更新 更多