Keine Ausgabe einer WPF-Anwendung an die Konsole?

112

Ich verwende Console.WriteLine () aus einer sehr einfachen WPF-Testanwendung, aber wenn ich die Anwendung über die Befehlszeile ausführe, wird nichts in die Konsole geschrieben. Weiß jemand, was hier los sein könnte?

Ich kann es reproduzieren, indem ich eine WPF-Anwendung in VS 2008 erstelle und einfach Console.WriteLine ("Text") an einer beliebigen Stelle hinzufüge, die ausgeführt wird. Irgendwelche Ideen?

Alles, was ich jetzt brauche, ist etwas so Einfaches wie Console.WriteLine (). Mir ist klar, dass ich log4net oder eine andere Protokollierungslösung verwenden könnte, aber ich brauche wirklich nicht so viele Funktionen für diese Anwendung.

Bearbeiten: Ich hätte daran denken sollen, dass Console.WriteLine () für Konsolenanwendungen ist. Na ja, keine dummen Fragen, oder? :-) Ich verwende jetzt nur System.Diagnostics.Trace.WriteLine () und DebugView.

rauben
quelle
Mögliche Duplikate hier und hier (neuer, aber mit einigen interessanten Antworten mit AttachConsole von Kernel32.dll )
Max
1
@Max, diese Fragen sind mögliche Duplikate dieser Frage. Diese Frage wurde 2-4 Jahre vor einer der von Ihnen gestellten Fragen gestellt.
Rob

Antworten:

90

Sie müssen ein Konsolenfenster manuell erstellen, bevor Sie tatsächlich Console.Write-Methoden aufrufen. Dadurch funktioniert die Konsole ordnungsgemäß, ohne den Projekttyp zu ändern (was für WPF-Anwendungen nicht funktioniert).

Hier ist ein vollständiges Beispiel für den Quellcode, wie eine ConsoleManager-Klasse aussehen könnte und wie sie zum Aktivieren / Deaktivieren der Konsole unabhängig vom Projekttyp verwendet werden kann.

Mit der folgenden Klasse müssen Sie nur ConsoleManager.Show()irgendwo schreiben, bevor Sie 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
quelle
5
Es ist möglich, zuerst AttachConsole (-1) aufzurufen und den Rückgabewert zu überprüfen, um ihn an die Konsole des übergeordneten Prozesses anzuhängen. Wenn false zurückgegeben wird, rufen Sie AllocConsole auf. Die Anwendung wird jedoch immer noch zuerst "zurückgegeben" und erst dann an die Konsole ausgegeben. Wenn ich eine Lösung finde, werde ich mehr veröffentlichen. Wenn Sie den WPF-App-Typ auf Konsolenanwendung setzen, verschwindet das Problem, aber Sie können die Konsole nicht trennen, ohne dass sie beim Starten des Programms kurz auf dem Bildschirm angezeigt wird. Sie sieht also etwas umständlich aus (aber wenn Sie damit leben können) , es funktioniert super).
Alex Paven
2
Eh, eigentlich nein, ich denke nicht, dass es möglich ist, es in beide Richtungen zu haben; Eine Konsolenanwendung ist in ihrem PE-Header als CUI markiert und arbeitet daher automatisch gut mit CMD zusammen. Eine GUI-Anwendung hingegen gibt die Steuerung sofort an CMD zurück, und selbst wenn sie wieder an die Konsole angeschlossen werden kann, werden Lesen und Schreiben mit den nächsten Ausgaben in der Pipeline vermischt, was offensichtlich sehr schlecht ist. Wenn Sie andererseits die Anwendung als Konsolenanwendung markieren, müssen Sie nur mit CMD leben, das beim Start der App kurz angezeigt wird. Sie können dann FreeConsole verwenden, um später zu trennen und anzuhängen / zuzuweisen usw.
Alex Paven
1
Warum das tun, wenn die Antwort von Brian genauso gut und viel einfacher funktioniert?
Wouter Janssens
2
Könnte offensichtlich sein, aber ich stellte fest, dass Console.WriteLine mit dieser Technik immer noch nicht funktionierte, als der Visual Studio-Debugger angehängt wurde. Als ich die App außerhalb von VS ausführte, war es ein Vergnügen. Vielen Dank.
aboy021
2
@Mark Ja, aber es funktioniert nicht ... Es gibt eine SetConsoleCtrlHandlerFunktion, mit der Sie benachrichtigt werden können, wenn das CTRL_CLOSE_EVENTEreignis eintritt, aber Sie können nichts damit anfangen. Es gibt nichts, was es Ihrer Anwendung ermöglicht, fortzufahren. Sie werden heruntergefahren. Wenn Sie Lust auf Hacking haben, können Sie wahrscheinlich den Windows-Nachrichtenhandler gegen den Konsolenprozess austauschen und einfach die WM_CLOSE-Nachricht löschen. Ich habe dies noch nie versucht, aber es könnte funktionieren. Es ist nur ein weiteres Fenster, aber wenn Sie diese Idee nicht unterhalten möchten, ist es wahrscheinlich besser, etwas anderes zu tun.
John Leidegren
129

Klicken Sie mit der rechten Maustaste auf das Projekt, "Eigenschaften", "Anwendung", ändern Sie "Ausgabetyp" in "Konsolenanwendung", und es wird auch eine Konsole angezeigt.

Brian
quelle
2
Das einzige Problem dabei ist, dass im Hintergrund ein cmd geöffnet ist, aber es funktioniert :).
Ykatchou
5
Großartiges, aber Befehlszeilenfenster wird erstellt, wenn die Anwendung nicht über cmd.exe ausgeführt wird (zwei Fenster, die für eine Anwendung erstellt wurden). Dafür gibt es aber auch eine Lösung: Sie können das cmd-Fenster mit ShowWindow (hWnd, 0) ausblenden. stackoverflow.com/a/10416180/1457197 . Bei Verwendung dieser Lösung wird Text in der Konsole nur angezeigt, wenn die WPF-Anwendung über die Befehlszeile ausgeführt wird.
CoperNick
Beachten Sie, dass Sie es während der Arbeit in Blend wieder auf "Fensteranwendung" umschalten müssen, da nur XAML (ohne Zugriff auf die Entwurfsansicht) für "Konsolenanwendungstypen" angezeigt wird. (Stand Blend 2013)
1
Nicht richtig ans. Blendet Hauptfenster aus. Nur die Konsole kommt hoch.
Yash
128

Sie können verwenden

Trace.WriteLine("text");

Dies wird in das Studio "Ausgabe" in Visual Studio ausgegeben (beim Debuggen).

Stellen Sie sicher, dass die Diagnosebaugruppe enthalten ist:

using System.Diagnostics;
Phobis
quelle
9
Dies ist die beste Antwort, hat aber nicht die höchste Bewertung
Kiltek
Ich stimme zu - genau darum bittet op. Tolle Alternative zu Console.WriteLine () - Die als Antwort gekennzeichnete Lösung ist eine ordentliche Übung, die jedoch nicht in eine Produktionsanwendung aufgenommen werden kann.
Nocarrier
4
PS für Windows Store-Apps (Windows Runtime) entspricht Trace.WriteLine Debug.WriteLine ()
Nocarrier
Dies ist eine einfache, saubere Lösung, die jedoch bei mir nicht funktioniert hat. Funktionierte während der Update-Datenbank nicht in der Seed-Methode des Entity Frameworks. Ansonsten funktioniert überall anders!
Charles W
Dies ist die beste Lösung. Wäre besser, wenn in der Antwort auch erklärt würde, dass dies überhaupt Console.WriteLinenicht für WPF-Anwendungen und nur für Befehlszeilen-Apps vorgesehen ist.
Andrew Koster
12

Obwohl John Leidegren die Idee immer wieder abschießt, hat Brian Recht. Ich habe es gerade in Visual Studio zum Laufen gebracht.

Um klar zu sein, erstellt eine WPF-Anwendung standardmäßig kein Konsolenfenster.

Sie müssen eine WPF-Anwendung erstellen und dann den OutputType in "Konsolenanwendung" ändern. Wenn Sie das Projekt ausführen, sehen Sie ein Konsolenfenster mit Ihrem WPF-Fenster davor.

Es sieht nicht sehr hübsch aus, aber ich fand es hilfreich, da ich wollte, dass meine App über die Befehlszeile mit Feedback ausgeführt wird, und dann für bestimmte Befehlsoptionen das WPF-Fenster anzeige.


quelle
1
Perfekt. Macht den Job.
frostymarvelous
10

Es ist möglich, die für die Konsole bestimmte Ausgabe mithilfe von anzuzeigen Befehlszeilenumleitung können anzeigen .

Beispielsweise:

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

schreibt den gesamten Inhalt in die output.txtDatei.

Lu55
quelle
Beste Antwort, da es einfach ist und keine Änderungen an der Quelle erfordert
Buckley
9

Alter Beitrag, aber ich bin darauf gestoßen. Wenn Sie also versuchen, etwas für die Ausgabe in einem WPF-Projekt in Visual Studio auszugeben, lautet die zeitgemäße Methode:

Schließen Sie dies ein:

using System.Diagnostics;

Und dann:

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

Ich verwende Console.WriteLine () zur Verwendung im Ausgabefenster ...

Erodierwald
quelle
4
Es ist eine 4 Jahre alte Frage, die stark bearbeitet wurde, seit ich sie zum ersten Mal gesehen habe. Jetzt wurde die Frage natürlich besser formuliert und meine Antwort wurde irrelevant.
Erodestwald
1

Ich habe eine Lösung erstellt, die Informationen von varius post gemischt.

Es ist ein Formular, das eine Beschriftung und ein Textfeld enthält. Die Konsolenausgabe wird in das Textfeld umgeleitet.

Es gibt auch eine Klasse namens ConsoleView, die drei öffentliche Methoden implementiert: Show (), Close () und Release (). Die letzte Möglichkeit besteht darin, die Konsole offen zu lassen und die Schaltfläche Schließen zu aktivieren, um die Ergebnisse anzuzeigen.

Die Formulare heißen FrmConsole. Hier sind die XAML und der c # -Code.

Die Verwendung ist sehr einfach:

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

Zum Öffnen der Konsole. Verwenden:

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

Für die Ausgabe von Text an die Konsole.

Verwenden:

ConsoleView.Close();

Zum Schließen der Konsole.

ConsoleView.Release();

Lässt die Konsole geöffnet und aktiviert die Schaltfläche Schließen

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>

Der Code des Fensters:

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  
}

der Code der ConsoleView-Klasse

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
}

Ich hoffe, dieses Ergebnis ist nützlich.

Emelias Alvarez
quelle
-17

Soweit ich weiß, ist Console.WriteLine () nur für Konsolenanwendungen. Ich denke das ist dein Problem.

GEOCHET
quelle
1
Ich weiß nichts über WPF, aber dies ist bei WinForms sicherlich nicht der Fall. Console.WriteLine funktioniert dort einwandfrei, aber natürlich sehen Sie die Konsole nicht, Sie sehen sie im Debugger-Ausgabefenster und wenn Sie die Standardausgabe anhören.
Jeff Yates
2
Sie können das Projekt auf eine Konsolenanwendung einstellen und es wird weiterhin als Windows-App ausgeführt, aber es wird auch eine sichtbare Konsole haben
Mark Cidade
Das ist falsch. Der Erstellungsprozess einer Nicht-Konsolenanwendung hängt standardmäßig keine Konsole an. Sie können dies manuell tun, indem Sie die Win32-API-Funktion AllocConsole () vor jedem Aufruf von Console.Write aufrufen. Die Console-Klasse wird dann initialisiert, um mit diesem Konsolenfenster zu arbeiten.
John Leidegren