Вывод Console.WriteLine из приложений Windows WPF на реальную консоль
Справочная информация. Я пытаюсь добавить возможности командной строки и пакетной обработки в существующее приложение 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 завершает работу instantietaly. Итак:
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?. Почему так сложно захватить stdout/stderr из WPF?
Я знаю, что я могу изменить тип приложения на Консольное приложение в настройках проекта, но у этого есть неприятный побочный эффект - окно консоли видимо постоянно, даже если вы просто дважды щелкните EXE. Это решение также не будет выполнено, поскольку оно создает консоль новую, даже если приложение было запущено из cmd.
РЕДАКТИРОВАТЬ: я хочу, чтобы мое приложение выводилось в консоль существующей, если есть один и не, чтобы создать новый, если он отсутствует.
Ответы
Ответ 1
После того, как вы немного разобрались, я нашел этот ответ. Код теперь:
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
Спасибо за ввод!
Ответ 2
То, что я делал в прошлом, - это сделать консольное приложение и вызвать P/Invoke, чтобы скрыть окно консоли, связанное с приложением, как только я покажу свое окно WPF:
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
}
}
}
Ответ 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);
}
}
}
И теперь, если вы запустите его из командной строки, вы должны увидеть "Пуск" и "Остановить", записанные на консоль.
Ответ 4
В свойствах проекта вы можете изменить тип проекта из приложения Windows в приложение консоли, а AFAIK - единственное отличие в том, что вы получаете консоль для использования в течение всей жизни приложения. Я даже не пробовал это с WPF.
Если вы хотите открыть и закрыть консоль, вы должны использовать Alloc/FreeConsole
, но обычно проще изменить тип проекта.