背景:我正在努力为现有的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在启动后退出 immedietaly 。所以:
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。 This solution也不会这样做,因为它会创建一个新的控制台,即使应用程序是从cmd运行的。
编辑:澄清一下,我希望我的应用程序输出到现有控制台(如果有)和不以创建新的控制台(如果缺少)。
答案 0 :(得分:24)
在挖了一下之后,我找到了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
感谢您的投入!
答案 1 :(得分: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
}
}
}
答案 2 :(得分: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();
}
要在您的示例中使用此功能,您应该能够执行此操作:
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);
}
}
}
现在,如果从命令行运行它,您应该看到“Start”和“Stop”写入控制台。
答案 3 :(得分:-1)
在项目属性中,您可以将项目类型从Windows应用程序更改为控制台应用程序,而AFAIK唯一的区别是您可以在整个应用程序的生命周期内使用控制台。我没有尝试使用WPF。
如果要打开和关闭控制台,则应使用Alloc/FreeConsole
,但更改项目类型通常更简单。