¿No hay salida a la consola desde una aplicación WPF?

112

Estoy usando Console.WriteLine () desde una aplicación de prueba WPF muy simple, pero cuando ejecuto la aplicación desde la línea de comandos, no veo nada escrito en la consola. ¿Alguien sabe lo que podría estar pasando aquí?

Puedo reproducirlo creando una aplicación WPF en VS 2008, y simplemente agregando Console.WriteLine ("texto") en cualquier lugar que se ejecute. ¿Algunas ideas?

Todo lo que necesito ahora es algo tan simple como Console.WriteLine (). Me doy cuenta de que podría usar log4net o alguna otra solución de registro, pero realmente no necesito tanta funcionalidad para esta aplicación.

Editar: debería haber recordado que Console.WriteLine () es para aplicaciones de consola. Oh, bueno, sin preguntas estúpidas, ¿verdad? :-) Solo usaré System.Diagnostics.Trace.WriteLine () y DebugView por ahora.

Robar
fuente
Posibles duplicados aquí y aquí (más nuevo, pero con algunas respuestas interesantes usando AttachConsole de Kernel32.dll )
Max
1
@Max, esas preguntas son posibles duplicados de esta pregunta. Esta pregunta se hizo de 2 a 4 años antes de cualquiera de las preguntas que publicó.
Rob

Respuestas:

90

Tendrá que crear una ventana de consola manualmente antes de llamar a cualquier método Console.Write. Eso hará que la consola funcione correctamente sin cambiar el tipo de proyecto (que para la aplicación WPF no funcionará).

Aquí hay un ejemplo de código fuente completo, de cómo se vería una clase ConsoleManager y cómo se puede usar para habilitar / deshabilitar la consola, independientemente del tipo de proyecto.

Con la siguiente clase, solo necesita escribir en ConsoleManager.Show()algún lugar antes de cualquier llamada a Console.Write...

[SuppressUnmanagedCodeSecurity]
public static class ConsoleManager
{
    private const string Kernel32_DllName = "kernel32.dll";

    [DllImport(Kernel32_DllName)]
    private static extern bool AllocConsole();

    [DllImport(Kernel32_DllName)]
    private static extern bool FreeConsole();

    [DllImport(Kernel32_DllName)]
    private static extern IntPtr GetConsoleWindow();

    [DllImport(Kernel32_DllName)]
    private static extern int GetConsoleOutputCP();

    public static bool HasConsole
    {
        get { return GetConsoleWindow() != IntPtr.Zero; }
    }

    /// <summary>
    /// Creates a new console instance if the process is not attached to a console already.
    /// </summary>
    public static void Show()
    {
        //#if DEBUG
        if (!HasConsole)
        {
            AllocConsole();
            InvalidateOutAndError();
        }
        //#endif
    }

    /// <summary>
    /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
    /// </summary>
    public static void Hide()
    {
        //#if DEBUG
        if (HasConsole)
        {
            SetOutAndErrorNull();
            FreeConsole();
        }
        //#endif
    }

    public static void Toggle()
    {
        if (HasConsole)
        {
            Hide();
        }
        else
        {
            Show();
        }
    }

    static void InvalidateOutAndError()
    {
        Type type = typeof(System.Console);

        System.Reflection.FieldInfo _out = type.GetField("_out",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.FieldInfo _error = type.GetField("_error",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        Debug.Assert(_out != null);
        Debug.Assert(_error != null);

        Debug.Assert(_InitializeStdOutError != null);

        _out.SetValue(null, null);
        _error.SetValue(null, null);

        _InitializeStdOutError.Invoke(null, new object[] { true });
    }

    static void SetOutAndErrorNull()
    {
        Console.SetOut(TextWriter.Null);
        Console.SetError(TextWriter.Null);
    }
} 
John Leidegren
fuente
5
Es posible intentar llamar a AttachConsole (-1) primero y verificar su valor de retorno para adjuntarlo a la consola del proceso padre; si devuelve falso, llame a AllocConsole. Sin embargo, la aplicación todavía 'regresa' primero y solo luego se envía a la consola, publicaré más si encuentro una solución. Además, si configura el tipo de aplicación WPF en Aplicación de consola, el problema desaparece pero no puede desconectar la consola sin que se muestre en la pantalla brevemente cuando se inicia el programa, por lo que parece un poco extraño (pero si puede vivir con él , funciona muy bien).
Alex Paven
2
Eh, en realidad no, no creo que sea posible tener las dos cosas; una aplicación de consola está marcada como CUI en su encabezado PE y, por lo tanto, coopera bien con CMD automáticamente. Una aplicación GUI, por otro lado, devuelve el control a CMD inmediatamente, e incluso si se puede volver a conectar a la consola, la lectura y la escritura se entremezclarán con las siguientes salidas en la tubería, lo que obviamente es muy malo. Si, por otro lado, marca la aplicación como aplicación de consola, solo tiene que vivir con CMD mostrándose brevemente al inicio de la aplicación; luego puede usar FreeConsole para separar y Adjuntar / Alloc más tarde
etc.- Alex Paven
1
¿Por qué hacer esto cuando la respuesta de Brian funciona tan bien y es mucho más fácil?
Wouter Janssens
2
Puede ser obvio, pero descubrí que Console.WriteLine todavía no funcionaba con esta técnica cuando se adjuntó el depurador de Visual Studio. Cuando ejecuté la aplicación fuera de VS, funcionó de maravilla. Gracias.
aboy021
2
@Mark Sí, pero no funciona ... Hay una SetConsoleCtrlHandlerfunción que permite ser notificado cuando CTRL_CLOSE_EVENTocurre el evento pero no puedes hacer nada con él, no hay nada que permita que tu aplicación continúe. Serás cerrado. Si tiene ganas de piratear, probablemente podría cambiar el controlador de mensajes de Windows por el proceso de la consola y simplemente soltar el mensaje WM_CLOSE, nunca lo he intentado, pero podría funcionar. Es solo otra ventana, pero dicho esto, a menos que quieras entretener esta idea, probablemente sea mejor invertir tu esfuerzo en otra cosa.
John Leidegren
129

Haga clic derecho en el proyecto, "Propiedades", pestaña "Aplicación", cambie "Tipo de salida" a "Aplicación de consola", y luego también tendrá una consola.

Brian
fuente
2
El único problema con eso es que tendrá un cmd abierto en segundo plano, pero funciona :).
ykatchou
5
Genial, pero se creará una ventana de línea de comandos cuando la aplicación no se ejecute desde cmd.exe (dos ventanas creadas para una aplicación). Pero para esto también hay una solución: puede ocultar la ventana cmd por ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . Con esta solución, verá texto en la consola solo cuando la aplicación WPF se ejecute desde la línea de comandos.
CoperNick
Tenga en cuenta que tendrá que volver a cambiarlo a "Aplicación de ventana" mientras trabaja en Blend, ya que solo muestra XAML (sin acceso a la Vista de diseño) para los tipos de "Aplicación de consola". (a partir de Blend 2013)
1
No es correcto ans. Oculta las ventanas principales. Solo aparece la consola.
Yash
129

Puedes usar

Trace.WriteLine("text");

Esto dará como resultado la ventana "Salida" en Visual Studio (al depurar).

asegúrese de incluir el conjunto de diagnóstico:

using System.Diagnostics;
Phobis
fuente
9
esta es la mejor respuesta, pero no tiene la calificación más alta
kiltek
Estoy de acuerdo, esto es exactamente lo que pide op. Excelente alternativa a Console.WriteLine (): la solución marcada como la respuesta es un buen ejercicio, pero no es razonable incluirla en una aplicación de producción.
nocarrier
4
PD para aplicaciones de la Tienda Windows (Windows Runtime) el equivalente de Trace.WriteLine es Debug.WriteLine ()
nocarrier
Esta es una solución simple y limpia, sin embargo, no funcionó para mí. No funcionó en el método semilla del marco de entidad durante la actualización de la base de datos. De lo contrario, funciona en cualquier otro lugar.
Charles W
Esta es la mejor solución. Sería mejor si la respuesta también explicara que Console.WriteLineno está diseñado para aplicaciones WPF en absoluto, y que está destinado solo para aplicaciones de línea de comandos.
Andrew Koster
12

Aunque John Leidegren sigue rechazando la idea, Brian tiene razón. Lo acabo de hacer funcionar en Visual Studio.

Para que quede claro, una aplicación WPF no crea una ventana de consola de forma predeterminada.

Debe crear una aplicación WPF y luego cambiar OutputType a "Aplicación de consola". Cuando ejecute el proyecto, verá una ventana de consola con su ventana de WPF al frente.

No se ve muy bonito, pero lo encontré útil porque quería que mi aplicación se ejecutara desde la línea de comandos con comentarios allí, y luego, para ciertas opciones de comando, mostraría la ventana de WPF.


fuente
1
Perfecto. Hace el trabajo.
frostymarvelous
10

Es posible ver la salida destinada a la consola mediante la redirección de la línea de comandos .

Por ejemplo:

C:\src\bin\Debug\Example.exe > output.txt

escribirá todo el contenido en el output.txtarchivo.

Lu55
fuente
La mejor respuesta, ya que es simple y no requiere cambios en la fuente
Buckley
9

Publicación anterior, pero me encontré con esto, por lo que si está tratando de generar algo en Salida en un proyecto WPF en Visual Studio, el método contemporáneo es:

Incluya esto:

using System.Diagnostics;

Y entonces:

Debug.WriteLine("something");
Smitty
fuente
4

Yo uso Console.WriteLine () para usarlo en la ventana de Salida ...

Erodewald
fuente
4
Es una pregunta de 4 años que se ha editado mucho desde que la vi por primera vez. Ahora, por supuesto, la pregunta ha sido mejor redactada y mi respuesta se hizo irrelevante.
Erodewald
1

Creé una solución, mezclé la información de varias publicaciones.

Es un formulario que contiene una etiqueta y un cuadro de texto. La salida de la consola se redirige al cuadro de texto.

También hay una clase llamada ConsoleView que implementa tres métodos públicos: Show (), Close () y Release (). El último es para dejar abierta la consola y activar el botón Cerrar para ver los resultados.

Los formularios se denominan FrmConsole. Aquí están el código XAML y c #.

El uso es muy sencillo:

ConsoleView.Show("Title of the Console");

Para abrir la consola. Utilizar:

System.Console.WriteLine("The debug message");

Para enviar texto a la consola.

Utilizar:

ConsoleView.Close();

Para Cerrar la consola.

ConsoleView.Release();

Deja abierta la consola y habilita el botón Cerrar

XAML

<Window x:Class="CustomControls.FrmConsole"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CustomControls"
    mc:Ignorable="d"
    Height="500" Width="600" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Topmost="True" Icon="Images/icoConsole.png">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="40"/>
    </Grid.RowDefinitions>
    <Label Grid.Row="0" Name="lblTitulo" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" FontFamily="Arial" FontSize="14" FontWeight="Bold" Content="Titulo"/>
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="1" Name="txtInner" FontFamily="Arial" FontSize="10" ScrollViewer.CanContentScroll="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" TextWrapping="Wrap"/>
    </Grid>
    <Button Name="btnCerrar" Grid.Row="2" Content="Cerrar" Width="100" Height="30" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"/>
</Grid>

El código de la ventana:

partial class FrmConsole : Window
{
    private class ControlWriter : TextWriter
    {
        private TextBox textbox;
        public ControlWriter(TextBox textbox)
        {
            this.textbox = textbox;
        }

        public override void WriteLine(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void WriteLine(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.ScrollToEnd();
            }));
        }

        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }

        }
    }

    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE

    #endregion


    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    public FrmConsole(string titulo)
    {
        InitializeComponent();
        lblTitulo.Content = titulo;
        Clear();
        btnCerrar.Click += new RoutedEventHandler(BtnCerrar_Click);
        Console.SetOut(new ControlWriter(txtInner));
        DesactivarCerrar();
    }

    #endregion


    //PROPIEDADES
    #region PROPIEDADES

    #endregion


    //DELEGADOS
    #region DELEGADOS

    private void BtnCerrar_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    #endregion


    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public void ActivarCerrar()
    {
        btnCerrar.IsEnabled = true;
    }

    public void Clear()
    {
        txtInner.Clear();
    }

    public void DesactivarCerrar()
    {
        btnCerrar.IsEnabled = false;
    }

    #endregion  
}

el código de la clase ConsoleView

static public class ConsoleView
{
    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE
    static FrmConsole console;
    static Thread StatusThread;
    static bool isActive = false;
    #endregion

    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    #endregion

    //PROPIEDADES
    #region PROPIEDADES

    #endregion

    //DELEGADOS
    #region DELEGADOS

    #endregion

    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public static void Show(string label)
    {
        if (isActive)
        {
            return;
        }

        isActive = true;
        //create the thread with its ThreadStart method
        StatusThread = new Thread(() =>
        {
            try
            {
                console = new FrmConsole(label);
                console.ShowDialog();
                //this call is needed so the thread remains open until the dispatcher is closed
                Dispatcher.Run();
            }
            catch (Exception)
            {
            }
        });

        //run the thread in STA mode to make it work correctly
        StatusThread.SetApartmentState(ApartmentState.STA);
        StatusThread.Priority = ThreadPriority.Normal;
        StatusThread.Start();

    }

    public static void Close()
    {
        isActive = false;
        if (console != null)
        {
            //need to use the dispatcher to call the Close method, because the window is created in another thread, and this method is called by the main thread
            console.Dispatcher.InvokeShutdown();
            console = null;
            StatusThread = null;
        }

        console = null;
    }

    public static void Release()
    {
        isActive = false;
        if (console != null)
        {
            console.Dispatcher.Invoke(console.ActivarCerrar);
        }

    }
    #endregion
}

Espero que este resultado sea de utilidad.

Emelias Alvarez
fuente
-17

Hasta donde yo sé, Console.WriteLine () es solo para aplicaciones de consola. Creo que este es tu problema.

GEOCHET
fuente
1
No sé sobre WPF, pero este ciertamente no es el caso de WinForms. Console.WriteLine funciona bien allí, pero, por supuesto, no verá la consola, la verá en la ventana de salida del depurador y si escucha la salida estándar.
Jeff Yates
2
puede configurar el proyecto en una aplicación de consola y aún se ejecutará como una aplicación de Windows, pero también tendrá una consola visible
Mark Cidade
Eso es incorrecto, el proceso de compilación de una aplicación que no es de consola no conecta una consola de forma predeterminada. Puede hacer esto manualmente llamando a la función de la API de Win32 de AllocConsole () antes de cualquier llamada a Console.Write, la clase de la consola se inicializará para trabajar con esa ventana de la consola.
John Leidegren