Nessun output alla console da un'applicazione WPF?


113

Sto usando Console.WriteLine () da un'applicazione di test WPF molto semplice, ma quando eseguo l'applicazione dalla riga di comando, non vedo nulla scritto sulla console. Qualcuno sa cosa potrebbe succedere qui?

Posso riprodurlo creando un'applicazione WPF in VS 2008 e aggiungendo semplicemente Console.WriteLine ("text") ovunque venga eseguita. Qualche idea?

Tutto ciò di cui ho bisogno in questo momento è qualcosa di semplice come Console.WriteLine (). Mi rendo conto che potrei usare log4net o qualche altra soluzione di registrazione, ma non ho davvero bisogno di tante funzionalità per questa applicazione.

Modifica: avrei dovuto ricordare che Console.WriteLine () è per le applicazioni da console. Oh beh, niente domande stupide, giusto? :-) Per ora userò semplicemente System.Diagnostics.Trace.WriteLine () e DebugView.


Possibili duplicati qui e qui (più recenti, ma con alcune risposte interessanti utilizzando AttachConsole da Kernel32.dll )
Max

1
@ Max, quelle domande sono possibili duplicati di questa domanda. Questa domanda è stata posta 2-4 anni prima di entrambe le domande che hai pubblicato.
Rob il

Risposte:


91

Dovrai creare una finestra Console manualmente prima di chiamare effettivamente i metodi Console.Write. Ciò consentirà alla console di funzionare correttamente senza modificare il tipo di progetto (che per l'applicazione WPF non funzionerà).

Ecco un esempio di codice sorgente completo, di come potrebbe apparire una classe ConsoleManager e di come può essere utilizzata per abilitare / disabilitare la Console, indipendentemente dal tipo di progetto.

Con la seguente classe, devi solo scrivere ConsoleManager.Show()da qualche parte prima di qualsiasi chiamata 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);
    }
} 

5
È possibile provare a chiamare prima AttachConsole (-1) e controllare il suo valore di ritorno per collegarsi alla console del processo genitore; se restituisce false, chiama AllocConsole. Tuttavia, l'applicazione ancora "ritorna" prima e solo successivamente viene inviata alla console, ne posterò altre se trovo una soluzione. Inoltre, se si imposta il tipo di app WPF su Applicazione console, il problema scompare ma non è possibile scollegare la console senza che venga visualizzata brevemente sullo schermo all'avvio del programma, quindi sembra un po 'imbarazzante (ma se riesci a conviverci , funziona benissimo).
Alex Paven

2
Eh, in realtà no, non credo sia possibile averlo in entrambe le direzioni; un'applicazione console è contrassegnata come CUI nella sua intestazione PE e quindi collabora bene con CMD automaticamente. Un'applicazione GUI d'altra parte restituisce immediatamente il controllo a CMD, e anche se può ricollegarsi alla console, la lettura e la scrittura saranno mescolate con i successivi output nella pipeline, il che è ovviamente molto negativo. Se invece contrassegni l'applicazione come applicazione Console, devi solo convivere con CMD visualizzato brevemente all'avvio dell'app; è quindi possibile utilizzare FreeConsole per scollegare e allegare / allocare in un secondo momento, ecc.
Alex Paven

1
Perché farlo quando la risposta di Brian funziona altrettanto bene e molto più facilmente.
Wouter Janssens

2
Potrebbe essere ovvio, ma ho scoperto che Console.WriteLine non funzionava ancora utilizzando questa tecnica quando era collegato il debugger di Visual Studio. Quando ho eseguito l'app al di fuori di VS ha funzionato a meraviglia. Grazie.
aboy021

2
@Mark Sì, ma non funziona ... C'è una SetConsoleCtrlHandlerfunzione che ti permette di essere avvisato quando CTRL_CLOSE_EVENTaccade l' evento ma non puoi farci niente, non c'è niente che permetta alla tua applicazione di continuare. Sarai chiuso. Se hai voglia di hackerare, potresti probabilmente scambiare il gestore dei messaggi di Windows con il processo della console e semplicemente rilasciare il messaggio WM_CLOSE, non l'ho mai provato ma potrebbe funzionare. È solo un'altra finestra, ma detto questo, a meno che tu non voglia intrattenere questa idea, probabilmente è meglio spendere il tuo impegno facendo qualcos'altro.
John Leidegren,

129

Fare clic con il pulsante destro del mouse sul progetto, "Proprietà", scheda "Applicazione", modificare "Tipo di output" in "Applicazione console", quindi avrà anche una console.


2
L'unico problema è che avrai un cmd aperto in background, ma funziona :).
ykatchou

5
Ottimo, ma la finestra della riga di comando verrà creata quando l'applicazione non viene eseguita da cmd.exe (due finestre create per un'applicazione). Ma per questo c'è anche una soluzione: puoi nascondere la finestra cmd da ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . Usando questa soluzione vedrai il testo nella console solo quando l'applicazione WPF viene eseguita dalla riga di comando.
CoperNick

Nota che dovrai tornare a "Applicazione finestra" mentre lavori in Blend, poiché mostra solo XAML (senza accesso alla visualizzazione struttura) per i tipi di "Applicazione console". (a partire da Blend 2013)

2
Ans non corretta. Nasconde le finestre principali. Viene fuori solo la console.
Yash

non funziona con wpf, nessuna finestra principale
alp

129

Puoi usare

Trace.WriteLine("text");

Questo verrà generato nella finestra "Output" in Visual Studio (durante il debug).

assicurati di avere il gruppo Diagnostica incluso:

using System.Diagnostics;

9
questa è la risposta migliore, ma non ha il punteggio più alto
kiltek

Sono d'accordo: questo è esattamente ciò che sta chiedendo op. Ottima alternativa a Console.WriteLine (): la soluzione contrassegnata come risposta è un esercizio accurato ma irragionevole da includere in un'applicazione di produzione.
nocarrier

4
PS per app di Windows Store (Windows Runtime) l'equivalente di Trace.WriteLine è Debug.WriteLine ()
nocarrier

Questa è una soluzione semplice e pulita, tuttavia non ha funzionato per me. Non ha funzionato nel metodo seed del framework dell'entità durante l'aggiornamento del database. Altrimenti, funziona ovunque!
Charles W

Questa è la soluzione migliore. Sarebbe meglio se la risposta spiegasse anche che Console.WriteLinenon è affatto destinato alle applicazioni WPF e che è inteso solo per le app della riga di comando.
Andrew Koster

12

Sebbene John Leidegren continui ad abbattere l'idea, Brian ha ragione. Ho appena funzionato in Visual Studio.

Per essere chiari, un'applicazione WPF non crea una finestra della console per impostazione predefinita.

È necessario creare un'applicazione WPF e quindi modificare OutputType in "Applicazione console". Quando esegui il progetto vedrai una finestra della console con la tua finestra WPF di fronte ad essa.

Non sembra molto carino, ma l'ho trovato utile perché volevo che la mia app venisse eseguita dalla riga di comando con feedback in essa, quindi per alcune opzioni di comando avrei visualizzato la finestra WPF.


1
Perfetto. Fa il lavoro.
frostymarvelous

10

È possibile vedere l'output destinato alla console utilizzando il reindirizzamento della riga di comando .

Per esempio:

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

scriverà tutto il contenuto su output.txtfile.


La migliore risposta in quanto è semplice e non richiede modifiche alla fonte
Buckley

9

Vecchio post, ma mi sono imbattuto in questo, quindi se stai provando a produrre qualcosa in Output in un progetto WPF in Visual Studio, il metodo contemporaneo è:

Includere questo:

using System.Diagnostics;

E poi:

Debug.WriteLine("something");

4

Uso Console.WriteLine () per l'uso nella finestra di output ...


4
È una domanda di 4 anni che è stata modificata pesantemente dalla prima volta che l'ho vista. Ora, naturalmente, la domanda è stata formulata meglio e la mia risposta è stata resa irrilevante.
erodewald

1

Ho creato una soluzione, mischiato le informazioni di vari post.

È un modulo che contiene un'etichetta e una casella di testo. L'output della console viene reindirizzato alla casella di testo.

C'è anche una classe chiamata ConsoleView che implementa tre metodi pubblici: Show (), Close () e Release (). L'ultimo è per lasciare aperta la console e attivare il pulsante Chiudi per visualizzare i risultati.

Il form si chiama FrmConsole. Ecco il codice XAML e c #.

L'utilizzo è molto semplice:

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

Per aprire la console. Uso:

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

Per il testo di output sulla console.

Uso:

ConsoleView.Close();

Per chiudere la console.

ConsoleView.Release();

Lascia aperta la console e abilita il pulsante Chiudi

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>

Il codice della finestra:

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  
}

il codice della classe 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
}

Spero che questo risultato sia utile.



-17

Per quanto ne so, Console.WriteLine () è solo per applicazioni console. Penso che questo sia il tuo problema.


1
Non so di WPF, ma questo non è certamente il caso di WinForms. Console.WriteLine funziona bene lì, ma, ovviamente, non vedrai la console, la vedrai nella finestra di output del debugger e se ascolti l'output standard.
Jeff Yates

2
puoi impostare il progetto su un'applicazione console e verrà comunque eseguito come app Windows ma avrà anche una console visibile
Mark Cidade

Non è corretto, il processo di compilazione di un'applicazione non console non collega una console tramite deffault. È possibile farlo manualmente chiamando la funzione API Win32 AllocConsole () prima di qualsiasi chiamata a Console.Write, la classe Console verrà quindi inizializzata per funzionare con quella finestra di Console.
John Leidegren,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.