从WPF Windows应用程序输出Console.WriteLine到实际控制台
背景:我正在努力为现有的WPF Windows应用程序添加命令行和批处理function。 当我在启动时检测到一些选项时,我禁止窗口出现,进行一些处理并立即退出。 现在,因为没有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?
我知道我可以在项目设置中将应用程序类型更改为控制台应用程序 ,但这有一个令人讨厌的副作用 – 控制台窗口始终可见,即使您只是双击exe。 此解决方案也不会这样做,因为它创建了一个新的控制台,即使应用程序是从cmd运行的。
编辑:澄清一下,我希望我的应用程序输出到现有的控制台(如果有的话),如果缺少则不要创建新的控制台。
经过深入挖掘后,我找到了答案 。 代码现在是:
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
感谢您的投入!
我过去做的是将它作为一个控制台应用程序,并在我显示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 } } }
WPF应用程序默认情况下没有控制台,但您可以轻松地为它创建一个,然后像在控制台应用程序中一样写入它。 我之前使用过这个助手类:
public class ConsoleHelper { /// /// Allocates a new console for current process. /// [DllImport("kernel32.dll")] public static extern Boolean AllocConsole(); /// /// Frees the console. /// [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); } } }
现在,如果从命令行运行它,您应该看到写入控制台的“开始”和“停止”。
在项目属性中,您可以将项目类型从Windows应用程序更改为控制台应用程序,而AFAIK唯一的区别是您可以在整个应用程序的生命周期内使用控制台。 我没有尝试使用WPF。
如果要打开和关闭控制台,则应使用Alloc/FreeConsole
,但更改项目类型通常更简单。