2012-05-02 20 views
25

Antecedentes: Tengo dificultades para agregar capacidades de procesamiento por lotes y línea de comandos a una aplicación de Windows WPF existente. Cuando detecto algunas opciones al inicio, evito que aparezca la ventana, hago algún procesamiento y salgo de inmediato. Ahora, como no hay interfaz de usuario, me gustaría enviar algunos mensajes a stdout/stderr. Considere siguiente código:Consola de salida.WriteLine de WPF Aplicaciones de Windows a la consola real

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); 
     } 
    } 
} 

Cuando corro de línea de comandos que se esperaría siguiente resultado:

Start 
Stop 

Pero en su lugar:

C:\test>WpfConsoleTest.exe 

C:\test> 

Usted puede redirigir la salida, sin embargo:

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

C:\test>type out.txt 
Start 
Stop 

CON redirigir a no funciona, por desgracia:

C:\test>WpfConsoleTest.exe > CON 

C:\test> 

Otro problema es que se cierra WpfConsoleTest.exe immedietaly después del arranque. Por lo tanto:

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

C:\test> 

Pero:

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

La mejor solución que era capaz de venir con hasta ahora es utilizar start /B /wait:

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

Este enfoque es sobre todo bien - si envuelves en un bate puedes conservar el código de error, etc. La única gran caída es que obtienes resultados una vez que finaliza la aplicación, es decir, no hay manera de que puedas seguir el progreso, tienes que esperar a que todo lo que está pasando termine.

Por lo tanto, mi pregunta es: Cómo dar salida a la consola principal de WPF Aplicación de Windows? Además, ¿por qué es tan difícil obtener stdout/stderr de WPF?

Sé que podría cambiar el tipo de aplicación a Console Application en la configuración del proyecto, pero esto tiene un desagradable efecto secundario: la ventana de la consola está visible todo el tiempo, incluso si simplemente hace doble clic en el exe. This solution tampoco funciona, porque crea una nueva consola, incluso si la aplicación se ejecutó desde cmd.

EDIT: para aclarar, yo quiero que mi aplicación para la salida de la consola existente si hay uno y no para crear uno nuevo si no se encuentra.

+0

Pregunta similar: http://stackoverflow.com/questions/160587/no-output-to-console-from-a-wpf-application –

+2

El enlace a esta pregunta ya estaba en el último párrafo de mi pregunta. – gwiazdorrr

Respuesta

23

Después de desenterrar un poco, encontré this answer. El código es ahora:

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); 
     } 
    } 
} 

Llamando al exe directamente todavía tiene un efecto secundario desagradable, conectado con la llamada de regresar de inmediato:

C:\test>WpfConsoleTest.exe 

C:\test>Start 
Stop 

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

La solución es, una vez más, utilizar inicio:

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

Gracias por su opinión!

+0

Gracias, gwiazdorrr. Esta es probablemente la mejor solución. Pero todavía no estoy contento con el desagradable efecto que causa. ¿Has encontrado una mejor solución, tal vez? –

+0

@BorisModylevsky: me temo que no, me conformé con la versión 'start' invocada por un script por lotes. – gwiazdorrr

+0

muchas gracias, @gwiazdorrr! –

8

Una aplicación WPF no tendrá una consola de forma predeterminada, pero puede crear fácilmente una para ella y luego escribir en ella como en una aplicación de consola.He usado esta clase de ayuda antes:

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(); 
} 

Para utilizar esto en su ejemplo, usted debería ser capaz de hacer esto:

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); 
     } 
    } 
} 

Y ahora, si se ejecuta desde la línea de comandos, Debería ver "Iniciar" y "Detener" escritos en la consola.

+1

Sí, pero lo que veré es una ** nueva ** ventana de consola, incluso si la inicio desde la línea de comandos. Quiero ver el resultado en la consola ** existente **. – gwiazdorrr

+1

Hmmm ... Me funcionó en WinForms, pero parece que WPF hace algo diferente. Puede intentar AttachConsole (http://msdn.microsoft.com/en-us/library/windows/desktop/ms681952%28v=vs.85%29.aspx), pero necesitará obtener un control para el proceso que inició su aplicación (presumiblemente cmd.exe) –

+0

Para obtener el proceso principal, esto podría ayudar: http://www.rhyous.com/2010/04/30/how-to-get-the-parent-process-hat -launch-ac-application/ –

-1

En las propiedades del proyecto, puede cambiar el tipo de proyecto de una aplicación de Windows a una aplicación de consola, y AFAIK la única diferencia es que obtiene una consola para usar durante toda la vida de la aplicación. No lo he intentado con WPF sin embargo.

Si desea abrir y cerrar una consola, debe usar Alloc/FreeConsole, pero generalmente es más sencillo cambiar el tipo de proyecto.

+0

Como escribí, al cambiar a la Aplicación de consola creará una ventana de consola si hace doble clic en el archivo ejecutable. Lo que quiero es que la salida se imprima en la consola ** existente **, si no hay una, no cree una nueva. – gwiazdorrr

15

Lo que he hecho en el pasado es para que sea una aplicación de consola y llame P/Invoke para ocultar ventana asociada consola de la aplicación una vez que he demostrado mi ventana WPF:

//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 
      } 
     } 
    } 
+0

Esta es la mejor solución hasta el momento. Simplemente no me gusta que la ventana de la consola aparezca antes de que se muestre la ventana, creo que puede ser molesto y confuso para el usuario común. – gwiazdorrr

+3

Solución genial, aunque creo que estoy de acuerdo con @gwiazdorrr, pero para los proyectos de utilidad, creo que es aceptable ... simplemente no estoy seguro de los proyectos puros de "producción". –

+1

Agregue 'using System.Diagnostics;' y 'using System.Runtime.InteropServices;' para cualquiera que quiera compilarlo y preguntándose en qué espacios de nombres están los comandos. – SharpC

Cuestiones relacionadas