Pas de sortie vers la console à partir d'une application WPF?

113

J'utilise Console.WriteLine () à partir d'une application de test WPF très simple, mais lorsque j'exécute l'application à partir de la ligne de commande, je ne vois rien en cours d'écriture sur la console. Quelqu'un sait-il ce qui pourrait se passer ici?

Je peux le reproduire en créant une application WPF dans VS 2008, et en ajoutant simplement Console.WriteLine ("texte") partout où ils sont exécutés. Des idées?

Tout ce dont j'ai besoin pour le moment est quelque chose d'aussi simple que Console.WriteLine (). Je réalise que je pourrais utiliser log4net ou une autre solution de journalisation, mais je n'ai vraiment pas besoin de beaucoup de fonctionnalités pour cette application.

Edit: J'aurais dû me rappeler que Console.WriteLine () est pour les applications console. Eh bien, pas de questions stupides, non? :-) Je vais simplement utiliser System.Diagnostics.Trace.WriteLine () et DebugView pour le moment.

Rob
la source
Doublons possibles ici et ici (plus récent, mais avec quelques réponses intéressantes en utilisant AttachConsole de Kernel32.dll )
Max
1
@Max, ces questions sont des doublons possibles de cette question. Cette question a été posée 2 à 4 ans avant l'une ou l'autre de ces questions que vous avez postées.
Rob

Réponses:

91

Vous devrez créer une fenêtre de console manuellement avant d'appeler les méthodes Console.Write. Cela initiera la console à fonctionner correctement sans changer le type de projet (qui pour l'application WPF ne fonctionnera pas).

Voici un exemple de code source complet, illustrant à quoi peut ressembler une classe ConsoleManager et comment elle peut être utilisée pour activer / désactiver la console, indépendamment du type de projet.

Avec la classe suivante, il vous suffit d'écrire ConsoleManager.Show()quelque part avant tout appel à 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
la source
5
Il est possible d'essayer d'appeler AttachConsole (-1) d'abord et de vérifier sa valeur de retour pour l'attacher à la console du processus parent; s'il renvoie false, appelez AllocConsole. Cependant, l'application «retourne» toujours en premier et ensuite seulement les sorties vers la console, j'en publierai plus si je trouve une solution. De plus, si vous définissez le type d'application WPF sur Application console, le problème disparaît mais vous ne pouvez pas détacher la console sans qu'elle ne s'affiche brièvement à l'écran lorsque le programme est démarré, donc cela semble un peu gênant (mais si vous pouvez vivre avec , ça marche très bien).
Alex Paven
2
Eh, en fait non, je ne pense pas que ce soit possible sur les deux tableaux; une application console est marquée comme CUI dans son en-tête PE et coopère donc bien avec CMD automatiquement. Une application GUI, par contre, renvoie immédiatement le contrôle à CMD, et même si elle peut se rattacher à la console, la lecture et l'écriture seront mêlées aux prochaines sorties du pipeline, ce qui est évidemment très mauvais. Si, d'un autre côté, vous marquez l'application comme application de console, vous n'avez qu'à vivre avec CMD affiché brièvement au démarrage de l'application; vous pouvez ensuite utiliser FreeConsole pour détacher et attacher / allouer plus tard, etc.
Alex Paven
1
Pourquoi faire cela alors que la réponse de Brian fonctionne aussi bien et beaucoup plus facilement.
Wouter Janssens
2
Cela peut être évident, mais j'ai trouvé que Console.WriteLine ne fonctionnait toujours pas avec cette technique lorsque le débogueur Visual Studio était attaché. Quand j'ai exécuté l'application en dehors de VS, cela a fonctionné un régal. Merci.
aboy021
2
@Mark Ouais, mais ça ne marche pas ... Il y a une SetConsoleCtrlHandlerfonction qui permet d'être notifié quand l' CTRL_CLOSE_EVENTévénement se produit mais tu ne peux rien faire avec, il n'y a rien qui permet à ton application de continuer. Vous serez fermé. Si vous avez envie de piratage, vous pouvez probablement échanger le gestionnaire de messages Windows pour le processus de la console et simplement supprimer le message WM_CLOSE, je n'ai jamais essayé cela mais cela pourrait fonctionner. C'est juste une autre fenêtre, mais cela dit, à moins que vous ne souhaitiez entretenir cette idée, vous feriez probablement mieux de faire autre chose.
John Leidegren
129

Faites un clic droit sur le projet, «Propriétés», onglet «Application», changez «Type de sortie» en «Application console», et il aura également une console.

Brian
la source
2
Le seul problème avec cela est que vous aurez une cmd ouverte en arrière-plan, mais cela fonctionne :).
ykatchou
5
Génial, mais une fenêtre de ligne de commande sera créée lorsque l'application n'est pas exécutée à partir de cmd.exe (deux fenêtres créées pour une application). Mais pour cela, il existe également une solution: vous pouvez masquer la fenêtre cmd par ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . En utilisant cette solution, vous verrez le texte dans la console uniquement lorsque l'application WPF est exécutée à partir de la ligne de commande.
CoperNick
Notez que vous devrez le basculer à nouveau sur "Application Windows" lorsque vous travaillez dans Blend, car il n'affiche que XAML (sans accès à la vue de conception) pour les types "Application console". (à partir de Blend 2013)
2
Pas correct ans. Cache les fenêtres principales. Juste la console monte.
Yash
ne fonctionne pas avec wpf, pas de fenêtre principale
alp il y a
129

Vous pouvez utiliser

Trace.WriteLine("text");

Cela affichera la fenêtre «Sortie» dans Visual Studio (lors du débogage).

assurez-vous que l'assemblage Diagnostics est inclus:

using System.Diagnostics;
Phobis
la source
9
c'est la meilleure réponse, mais n'a pas la meilleure note
kiltek
Je suis d'accord - c'est exactement ce que demande op. Excellente alternative à Console.WriteLine () - la solution marquée comme la réponse est un exercice soigné mais déraisonnable à inclure dans une application de production.
nocarrier
4
PS pour les applications Windows Store (Windows Runtime) l'équivalent de Trace.WriteLine est Debug.WriteLine ()
nocarrier
C'est une solution simple et propre, mais cela n'a pas fonctionné pour moi. N'a pas fonctionné dans la méthode de départ du framework d'entité pendant la base de données de mise à jour. Sinon, ça marche partout ailleurs!
Charles W
C'est la meilleure solution. Ce serait mieux si la réponse expliquait également que ce Console.WriteLinen'est pas du tout destiné aux applications WPF et qu'il est destiné uniquement aux applications de ligne de commande.
Andrew Koster
12

Bien que John Leidegren n'arrête pas de rejeter l'idée, Brian a raison. Je viens de le faire fonctionner dans Visual Studio.

Pour être claire, une application WPF ne crée pas de fenêtre de console par défaut.

Vous devez créer une application WPF, puis changer le OutputType en "Application console". Lorsque vous exécutez le projet, vous verrez une fenêtre de console avec votre fenêtre WPF devant elle.

Cela n'a pas l'air très joli, mais je l'ai trouvé utile car je voulais que mon application soit exécutée à partir de la ligne de commande avec des commentaires, puis pour certaines options de commande, j'afficherais la fenêtre WPF.


la source
1
Parfait. Fait le travail.
frostymarvelous
10

Il est possible de voir la sortie destinée à la console en utilisant la redirection de ligne de commande .

Par exemple:

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

écrira tout le contenu dans le output.txtfichier.

Lu55
la source
Meilleure réponse car elle est simple et ne nécessite pas de modifications de la source
buckley
9

Ancien article, mais j'ai rencontré cela, donc si vous essayez de générer quelque chose vers Output dans un projet WPF dans Visual Studio, la méthode contemporaine est:

Incluez ceci:

using System.Diagnostics;

Puis:

Debug.WriteLine("something");
Smitty
la source
4

J'utilise Console.WriteLine () pour une utilisation dans la fenêtre Sortie ...

erodewald
la source
4
C'est une question vieille de 4 ans qui a été fortement éditée depuis que je l'ai vue pour la première fois. Maintenant, bien sûr, la question a été mieux formulée et ma réponse est devenue inutile.
erodewald
1

J'ai créé une solution, mélangé les informations de varius post.

C'est un formulaire, qui contient une étiquette et une zone de texte. La sortie de la console est redirigée vers la zone de texte.

Il existe également une classe appelée ConsoleView qui implémente trois méthodes publiques: Show (), Close () et Release (). La dernière consiste à laisser la console ouverte et à activer le bouton Fermer pour afficher les résultats.

Les formulaires sont appelés FrmConsole. Voici le XAML et le code c #.

L'utilisation est très simple:

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

Pour ouvrir la console. Utilisation:

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

Pour le texte de sortie vers la console.

Utilisation:

ConsoleView.Close();

Pour Fermer la console.

ConsoleView.Release();

Laisse la console ouverte et active le bouton Fermer

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>

Le code de la fenêtre:

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  
}

le code de la 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
}

J'espère que ce résultat sera utile.

Emelias Alvarez
la source
-17

Autant que je sache, Console.WriteLine () est uniquement pour les applications console. Je pense que c'est votre problème.

GEOCHET
la source
1
Je ne connais pas WPF, mais ce n'est certainement pas le cas pour WinForms. Console.WriteLine fonctionne bien ici, mais, bien sûr, vous ne verrez pas la console, vous la verrez dans la fenêtre de sortie du débogueur et si vous écoutez la sortie standard.
Jeff Yates
2
vous pouvez définir le projet sur une application console et il fonctionnera toujours en tant qu'application Windows, mais il aura également une console visible
Mark Cidade
C'est incorrect, le processus de construction d'une application non console n'attache pas une console par défaut. Vous pouvez le faire manuellement en appelant la fonction API Win32 AllocConsole () avant tout appel à Console.Write, la classe Console sera alors initialisée pour fonctionner avec cette fenêtre Console.
John Leidegren