从WPF Windows应用程序到实际控制台输出Console.WriteLine

gwi*_*rrr 27 c# wpf cmd batch-file

背景:我正在努力为现有的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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Start
Stop
Run Code Online (Sandbox Code Playgroud)

但反而:

C:\test>WpfConsoleTest.exe

C:\test>
Run Code Online (Sandbox Code Playgroud)

但是,您可以重定向输出:

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

C:\test>type out.txt
Start
Stop
Run Code Online (Sandbox Code Playgroud)

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

C:\test>WpfConsoleTest.exe > CON

C:\test>
Run Code Online (Sandbox Code Playgroud)

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

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

C:\test>
Run Code Online (Sandbox Code Playgroud)

但:

C:\test>WpfConsoleTest.exe > out.txt & ping localhost > nul & type out.txt
Start
Stop
Run Code Online (Sandbox Code Playgroud)

到目前为止,我能够提供的最佳解决方案是使用start /B /wait:

C:\test>start /B /wait WpfConsoleTest.exe > out.txt & type out.txt
Start
Stop
Run Code Online (Sandbox Code Playgroud)

这种方法大多没问题 - 如果你把它包装成一个蝙蝠,你可以保留错误代码等等.一个巨大的垮台是你在申请结束后得到输出,即你无法跟踪进展,你必须等待正在进行的任何事情才能完成.

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

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

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

gwi*_*rrr 26

挖了一下后,我找到了答案.代码现在是:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

直接调用exe仍然有一个令人讨厌的副作用,与立即返回的调用相关联:

C:\test>WpfConsoleTest.exe

C:\test>Start
Stop

^^^^
The cursor will stay here waiting for the user to press enter!
Run Code Online (Sandbox Code Playgroud)

解决方案是再次使用start:

C:\test>start /wait WpfConsoleTest.exe
Start
Stop
Run Code Online (Sandbox Code Playgroud)

感谢您的投入!


Ste*_*ner 17

我过去所做的是将它作为控制台应用程序,并在我显示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
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 很酷的解决方案,虽然我认为我同意@gwiazdorrr,但对于公用事业项目,我认为这是可以接受的......只是不确定纯粹的"生产"项目. (3认同)

Mat*_*and 8

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();
}
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果从命令行运行它,您应该看到写入控制台的"开始"和"停止".

  • 这项建议并不是解决所有人问题的办法。在这里,我们总是使用 AllocConsole 创建一个新控制台,无论我们是否已经打开了一个控制台。这意味着当您从命令行启动 WPF 应用程序时,您仍然无法 console.writeline 到命令行,并且在程序结束时消息全部消失(因为额外的控制台已关闭)。 (2认同)