Comment afficher une sortie / fenêtre de console dans une application de formulaires?

131

Pour rester coincé tout de suite, un exemple très basique:

using System;
using System.Windows.Forms;

class test
{ 
    static void Main()
    { 
        Console.WriteLine("test");
        MessageBox.Show("test");
    }
}

Si je compile cela avec les options par défaut (en utilisant csc en ligne de commande), comme prévu, il se compilera dans une application console. De plus, parce que j'ai importé System.Windows.Forms, il affichera également une boîte de message.

Maintenant, si j'utilise l'option /target:winexe, ce qui, je pense, équivaut Windows Applicationà choisir parmi les options du projet, comme prévu, je ne verrai que la boîte de message et aucune sortie de console.

(En fait, au moment où il est lancé à partir de la ligne de commande, je peux émettre la commande suivante avant même que l'application ne soit terminée).

Donc, ma question est - je sais que vous pouvez avoir une sortie "windows" / formulaires à partir d'une application console, mais y a-t-il quand même pour afficher la console à partir d'une application Windows?

Wil
la source
2
que voyez-vous comme la différence entre les deux? Pourquoi ne pas simplement compiler comme console et afficher un formulaire.
Doggett
7
@Doggett, simple - j'apprends et je veux comprendre pourquoi / comment le faire, même si je ne finis jamais par l'utiliser dans une vraie application .... Pour le moment, je pense à une option qui donne des commandes supplémentaires / sortie comme dans VLC, cependant TBH, je n'en ai pas besoin - encore une fois, juste apprendre et je veux le comprendre!
Wil
J'ai accompli cela en utilisant ce tutoriel: saezndaree.wordpress.com/2009/03/29/…
vivanov

Réponses:

153

celui-ci devrait fonctionner.

using System.Runtime.InteropServices;

private void Form1_Load(object sender, EventArgs e)
{
    AllocConsole();
}

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
Wizzardz
la source
8
Génial, cette question semble avoir été beaucoup posée, c'est la seule réponse réelle à la question que j'ai pu trouver, +1
RobJohnson
5
Problème principal: lorsque vous le fermez, toute l'application se ferme.
Mark
4
J'ai testé sur Windows 8 et Windows 10: - AttachConsole fonctionne à partir d'une boîte cmd - AllocConsole fonctionne à partir de Visual Studio. Lorsque alloc est nécessaire, AttachConsole renvoie false. Vous devez également appeler FreeConsole () avant de terminer l'application en mode console. Dans mon programme, j'ai utilisé le code de Matthew Strawbridge (voir ci-dessous), avec la ligne AttachConsole () modifiée en: if (! AttachConsole (-1)) AllocConsole ();
Berend Engelbrecht
Cela fonctionnera-t-il dans un contrôle utilisateur? Je travaille sur la création d'un contrôle SSH en tant que composant winforms en utilisant Granados (par exemple), et il s'agit uniquement d'un composant d'arrière-plan. Je voudrais ajouter un joli wrapper pour qu'il affiche et utilise également la console dans un composant.
Kraang Prime
2
Ce n'est pas génial, lorsqu'il est exécuté à partir de la ligne de commande, il ouvre une fenêtre de console distincte , et lors de l'exécution à partir de la ligne de commande et en essayant d'utiliser >pour rediriger la sortie, j'obtiens une fenêtre de console séparée et aucune sortie dans mon fichier.
uglycoyote
140

C'est peut-être trop simpliste ...

Créer un projet Windows Form ...

Ensuite: Propriétés du projet -> Application -> Type de sortie -> Application console

Ensuite, la console et les formulaires peuvent fonctionner ensemble, cela fonctionne pour moi

Chaz
la source
2
Cela semble le plus simple, j'ai également résolu mon problème.
dadude999
2
C'est définitivement la meilleure solution! D'autres sont intelligents mais bien compliqués
LM.Croisez
3
Simple et bien travaillé. Cela devrait être la réponse acceptée.
madu
7
Bien que, oui, techniquement, cela puisse être utilisé pour permettre ce que l'affiche demande - ce n'est pas une excellente solution. En faisant cela, si vous démarrez ensuite votre application winforms avec l'interface graphique, vous obtiendrez également une fenêtre de console ouverte. Dans ce cas, vous auriez besoin de quelque chose de plus comme la réponse de Mike de Klerk.
Justin Greywolf
2
C'est la seule solution où j'ai pu obtenir mon application Winforms pour écrire la sortie sur la console lorsqu'elle est exécutée à partir de la ligne de commande, ou pour écrire dans un fichier lorsqu'elle est redirigée sur la ligne de commande avec >. Cependant, j'espérais une solution qui expliquerait comment s'exécuter en tant qu '"application console" seulement de temps en temps (c'est-à-dire pour activer par programme tout ce que changer ce paramètre mystérieux de Visual Studio). Quelqu'un sait-il comment cela fonctionne sous le capot?
uglycoyote
64

Si vous ne vous souciez pas d'ouvrir une console à la commande, vous pouvez accéder aux propriétés de votre projet et le changer en Application console

capture d'écran de la modification du type de projet.

Cela affichera toujours votre formulaire ainsi qu'une fenêtre de console. Vous ne pouvez pas fermer la fenêtre de la console, mais cela fonctionne comme un excellent enregistreur temporaire pour le débogage.

N'oubliez pas de le désactiver avant de déployer le programme.

gunr2171
la source
1
Agréable. Cela résout le problème que j'ai avec mon application de formulaires, dont j'ai besoin pour pouvoir sortir dans une fenêtre de console tout en prenant en charge la redirection de la sortie vers un fichier. Et je n'ai pas besoin de connecter une console manuellement ...
Kai Hartmann
3
@JasonHarrison Si vous fermez la fenêtre de la console, le programme se ferme. De plus, la fenêtre est toujours ouverte pendant l'exécution du programme.
gunr2171
2
@ gun2171: Merci. Les inconvénients de cette approche sont notés dans la réponse: la fenêtre de la console apparaîtra si l'application est lancée avec un double clic, menu Démarrer, etc.
Jason Harrison
Un moyen de suivre cet événement de fermeture de console?
Elshan il y a
17

Vous pouvez appeler en AttachConsoleutilisant pinvoke pour obtenir une fenêtre de console attachée à un projet WinForms: http://www.csharp411.com/console-output-from-winforms-application/

Vous pouvez également envisager Log4net ( http://logging.apache.org/log4net/index.html ) pour configurer la sortie du journal dans différentes configurations.

Adam Vandenberg
la source
+1 - Wow, j'espérais une console.show ou similaire! beaucoup plus compliqué que je ne le pensais! Je laisserai ouvert pour le moment au cas où il y aurait une réponse meilleure / plus facile.
Wil
Cela a fonctionné pour moi, AllocConsole () n'a pas fait parce qu'il a engendré une nouvelle fenêtre de console (je n'ai pas creusé plus loin dans AllocConsole, peut-être que j'ai raté quelque chose là-bas).
derFunk
14

Cela a fonctionné pour moi, pour diriger la sortie vers un fichier. Appeler la console avec

cmd / c "C: \ chemin \ vers \ votre \ application.exe"> monfichier.txt

Ajoutez ce code à votre application.

    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(UInt32 dwProcessId);
    [DllImport("kernel32.dll")]
    private static extern bool GetFileInformationByHandle(
        SafeFileHandle hFile,
        out BY_HANDLE_FILE_INFORMATION lpFileInformation
        );
    [DllImport("kernel32.dll")]
    private static extern SafeFileHandle GetStdHandle(UInt32 nStdHandle);
    [DllImport("kernel32.dll")]
    private static extern bool SetStdHandle(UInt32 nStdHandle, SafeFileHandle hHandle);
    [DllImport("kernel32.dll")]
    private static extern bool DuplicateHandle(
        IntPtr hSourceProcessHandle,
        SafeFileHandle hSourceHandle,
        IntPtr hTargetProcessHandle,
        out SafeFileHandle lpTargetHandle,
        UInt32 dwDesiredAccess,
        Boolean bInheritHandle,
        UInt32 dwOptions
        );
    private const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;
    private const UInt32 STD_OUTPUT_HANDLE = 0xFFFFFFF5;
    private const UInt32 STD_ERROR_HANDLE = 0xFFFFFFF4;
    private const UInt32 DUPLICATE_SAME_ACCESS = 2;
    struct BY_HANDLE_FILE_INFORMATION
    {
        public UInt32 FileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
        public UInt32 VolumeSerialNumber;
        public UInt32 FileSizeHigh;
        public UInt32 FileSizeLow;
        public UInt32 NumberOfLinks;
        public UInt32 FileIndexHigh;
        public UInt32 FileIndexLow;
    }
    static void InitConsoleHandles()
    {
        SafeFileHandle hStdOut, hStdErr, hStdOutDup, hStdErrDup;
        BY_HANDLE_FILE_INFORMATION bhfi;
        hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
        hStdErr = GetStdHandle(STD_ERROR_HANDLE);
        // Get current process handle
        IntPtr hProcess = Process.GetCurrentProcess().Handle;
        // Duplicate Stdout handle to save initial value
        DuplicateHandle(hProcess, hStdOut, hProcess, out hStdOutDup,
        0, true, DUPLICATE_SAME_ACCESS);
        // Duplicate Stderr handle to save initial value
        DuplicateHandle(hProcess, hStdErr, hProcess, out hStdErrDup,
        0, true, DUPLICATE_SAME_ACCESS);
        // Attach to console window – this may modify the standard handles
        AttachConsole(ATTACH_PARENT_PROCESS);
        // Adjust the standard handles
        if (GetFileInformationByHandle(GetStdHandle(STD_OUTPUT_HANDLE), out bhfi))
        {
            SetStdHandle(STD_OUTPUT_HANDLE, hStdOutDup);
        }
        else
        {
            SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
        }
        if (GetFileInformationByHandle(GetStdHandle(STD_ERROR_HANDLE), out bhfi))
        {
            SetStdHandle(STD_ERROR_HANDLE, hStdErrDup);
        }
        else
        {
            SetStdHandle(STD_ERROR_HANDLE, hStdErr);
        }
    }

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        // initialize console handles
        InitConsoleHandles();

        if (args.Length != 0)
        {

            if (args[0].Equals("waitfordebugger"))
            {
                MessageBox.Show("Attach the debugger now");
            }
            if (args[0].Equals("version"))
            {
#if DEBUG
                String typeOfBuild = "d";
#else
                String typeOfBuild = "r";
#endif
                String output = typeOfBuild + Assembly.GetExecutingAssembly()
                    .GetName().Version.ToString();
                //Just for the fun of it
                Console.Write(output);
                Console.Beep(4000, 100);
                Console.Beep(2000, 100);
                Console.Beep(1000, 100);
                Console.Beep(8000, 100);
                return;
            }
        }
    }

J'ai trouvé ce code ici: http://www.csharp411.com/console-output-from-winforms-application/ J'ai pensé qu'il valait la peine de le poster ici également.

Mike de Klerk
la source
5
Cela fonctionne très bien SAUF il échoue maintenant dans Windows 8 et Windows 10. Par échoue, je veux dire qu'il n'y a pas de sortie sauf et une invite supplémentaire (si c'est un indice). Quelqu'un a suggéré AllocConsole mais qui a juste flashé une fenêtre cmd.
Simon Heffer
J'ai également essayé la réponse de Chaz ci-dessus, mais cela donne une nouvelle console dans Windows 7 (pas en 8 ou 10). J'ai juste besoin de l'option pour exécuter avec la redirection sur la ligne de commande ou exécuter en tant qu'interface graphique s'il n'y a pas d'arguments.
Simon Heffer
J'ai essayé cela mais je n'ai pas fonctionné. Avec juste AttachConsole(ATTACH_PARENT_PROCESS)j'obtiens la sortie de la console mais la rediriger sur la ligne de commande avec >ne fonctionne pas. Lorsque j'essaie cette réponse, je ne peux obtenir aucune sortie, que ce soit dans la console ou dans un fichier.
uglycoyote
12

Il y a essentiellement deux choses qui peuvent arriver ici.

Sortie de la console Il est possible pour un programme winforms de s'attacher à la fenêtre de console qui l'a créé (ou à une fenêtre de console différente, voire à une nouvelle fenêtre de console si vous le souhaitez). Une fois attaché à la fenêtre de la console, Console.WriteLine () etc. fonctionne comme prévu. Une solution à cette approche est que le programme retourne immédiatement le contrôle à la fenêtre de la console, puis continue à y écrire, de sorte que l'utilisateur peut également taper dans la fenêtre de la console. Vous pouvez utiliser start avec le paramètre / wait pour gérer cela, je pense.

Lien pour démarrer la syntaxe de la commande

Sortie de console redirigée C'est quand quelqu'un redirige la sortie de votre programme ailleurs, par exemple.

yourapp> fichier.txt

Dans ce cas, l'attachement à une fenêtre de console ignore effectivement la tuyauterie. Pour que cela fonctionne, vous pouvez appeler Console.OpenStandardOutput () pour obtenir un handle vers le flux vers lequel la sortie doit être redirigée. Cela ne fonctionne que si la sortie est acheminée, donc si vous souhaitez gérer les deux scénarios, vous devez ouvrir la sortie standard et y écrire et l'attacher à la fenêtre de la console. Cela signifie que la sortie est envoyée à la fenêtre de la console et au tube, mais c'est la meilleure solution que j'ai pu trouver. Ci-dessous le code que j'utilise pour ce faire.

// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool AttachConsole(int dwProcessId);

    private const int ATTACH_PARENT_PROCESS = -1;

    StreamWriter _stdOutWriter;

    // this must be called early in the program
    public GUIConsoleWriter()
    {
        // this needs to happen before attachconsole.
        // If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
        // I guess it probably does write somewhere, but nowhere I can find out about
        var stdout = Console.OpenStandardOutput();
        _stdOutWriter = new StreamWriter(stdout);
        _stdOutWriter.AutoFlush = true;

        AttachConsole(ATTACH_PARENT_PROCESS);
    }

    public void WriteLine(string line)
    {
        _stdOutWriter.WriteLine(line);
        Console.WriteLine(line);
    }
}
cedd
la source
Je n'ai pas pu écrire sur la console; attacher d'abord le processus parent a fait l'affaire. Je vous remercie.
Pupper
Il semblerait que cette réponse vous oblige à réécrire tous les appels pour appeler Console.WriteLineà la place le nouveau WriteLinedéfini ci-dessus. Même si j'ai essayé de ne pas pouvoir avec ce code obtenir quoi que ce soit redirigé vers un fichier lors de l'exécution de l'application sur la ligne de commande et de la redirection >vers un fichier.
uglycoyote
@uglycoyote, assurez-vous de construire le GUIConsoleWriter le plus tôt possible dans votre application, sinon cela ne fonctionnera pas pour des raisons mystérieuses de type Windows. Je dirais que l'encapsulation des appels à Console.WriteLineest juste une bonne pratique, car elle vous permet de tester et de modifier facilement les endroits auxquels vous vous connectez (par exemple, vous voudrez peut-être commencer à vous connecter à un service de journalisation basé sur le cloud comme PaperTrail, ou autre. )
cedd
cela a bien fonctionné pour moi dans Win10 sans mêmeStreamWriter _stdOutWriter;
TS
La tuyauterie est la réponse, mais au lieu d'un fichier, utilisez simplement PLUS, comme: yourapp | plus ; veuillez vous référer à stackoverflow.com/a/13010823/1845672
Roland le
9

Créez une application Windows Forms et modifiez le type de sortie en Console.

Il en résultera à la fois une console et le formulaire à ouvrir.

entrez la description de l'image ici

Pedro Rodrigues
la source
C'est exactement ce que je recherche. Simple et n'utilisant pas de WINAPI.
Michael Coxon
J'ai essayé de nombreux exemples, mais aucun d'entre eux n'a produit de résultats à la hauteur de mes attentes. Cette solution est cependant exactement ce que je voulais et de loin la solution la plus simple.
inexcitus
4
//From your application set the Console to write to your RichTextkBox 
//object:
Console.SetOut(new RichTextBoxWriter(yourRichTextBox));

//To ensure that your RichTextBox object is scrolled down when its text is 
//changed add this event:
private void yourRichTextBox_TextChanged(object sender, EventArgs e)
{
    yourRichTextBox.SelectionStart = yourRichTextBox.Text.Length;
    yourRichTextBox.ScrollToCaret();
}

public delegate void StringArgReturningVoidDelegate(string text);
public class RichTextBoxWriter : TextWriter
{
    private readonly RichTextBox _richTextBox;
    public RichTextBoxWriter(RichTextBox richTexttbox)
    {
        _richTextBox = richTexttbox;
    }

    public override void Write(char value)
    {
        SetText(value.ToString());
    }

    public override void Write(string value)
    {
        SetText(value);
    }

    public override void WriteLine(char value)
    {
        SetText(value + Environment.NewLine);
    }

    public override void WriteLine(string value)
    {
        SetText(value + Environment.NewLine);
    }

    public override Encoding Encoding => Encoding.ASCII;

    //Write to your UI object in thread safe way:
    private void SetText(string text)
    {
        // InvokeRequired required compares the thread ID of the  
        // calling thread to the thread ID of the creating thread.  
        // If these threads are different, it returns true.  
        if (_richTextBox.InvokeRequired)
        {
            var d = new StringArgReturningVoidDelegate(SetText);
            _richTextBox.Invoke(d, text);
        }
        else
        {
            _richTextBox.Text += text;
        }
    }
}
Kamil Kh
la source
3
using System;
using System.Runtime.InteropServices;

namespace SomeProject
{
    class GuiRedirect
    {
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool AttachConsole(int dwProcessId);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern FileType GetFileType(IntPtr handle);

    private enum StandardHandle : uint
    {
        Input = unchecked((uint)-10),
        Output = unchecked((uint)-11),
        Error = unchecked((uint)-12)
    }

    private enum FileType : uint
    {
        Unknown = 0x0000,
        Disk = 0x0001,
        Char = 0x0002,
        Pipe = 0x0003
    }

    private static bool IsRedirected(IntPtr handle)
    {
        FileType fileType = GetFileType(handle);

        return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
    }

    public static void Redirect()
    {
        if (IsRedirected(GetStdHandle(StandardHandle.Output)))
        {
            var initialiseOut = Console.Out;
        }

        bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
        if (errorRedirected)
        {
            var initialiseError = Console.Error;
        }

        AttachConsole(-1);

        if (!errorRedirected)
            SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
    }
}
chiffon
la source
1
Fonctionne correctement à partir d'une invite de commandes, mais pas à partir de Démarrer> Exécuter ou dans Visual Studio. Pour que cela fonctionne dans tous les cas, remplacez la ligne AttachConsole par: if (! AttachConsole (-1)) AllocConsole (); Si AllocConsole () est appelé, FreeConsole () doit également être appelé, sinon l'hôte de la console continue de fonctionner après la fin du programme.
Berend Engelbrecht
2
Quelle est l'utilisation prévue de initialiseOut et initialiseError, car ils ne sont pas utilisés?
Edwin
StandardHandle : uintest faux ici ... devrait être IntPtr pour fonctionner à la fois sur x86 et x64
Dmitry Gusarov
1

Vous pouvez à tout moment basculer entre les types d'applications, la console ou les fenêtres. Donc, vous n'écrirez pas de logique spéciale pour voir le stdout. En outre, lors de l'exécution de l'application dans le débogueur, vous verrez tous les stdout dans la fenêtre de sortie. Vous pouvez également simplement ajouter un point d'arrêt, et dans les propriétés du point d'arrêt, modifier "Quand il est atteint ...", vous pouvez afficher tous les messages et variables. Vous pouvez également cocher / décocher "Continuer l'exécution", et votre point d'arrêt deviendra carré. Ainsi, les messages de point d'arrêt sans rien changer dans l'application dans la fenêtre de sortie de débogage.

armagedescu
la source
0

Pourquoi ne pas simplement la laisser en tant qu'application Window Forms et créer un formulaire simple pour imiter la console. Le formulaire peut être conçu pour ressembler à la console à écran noir et répondre directement à la pression d'une touche. Ensuite, dans le fichier program.cs, vous décidez si vous devez exécuter le formulaire principal ou le ConsoleForm. Par exemple, j'utilise cette approche pour capturer les arguments de ligne de commande dans le fichier program.cs. Je crée le ConsoleForm, je le cache initialement, puis je passe les chaînes de ligne de commande à une fonction AddCommand, qui affiche les commandes autorisées. Enfin, si l'utilisateur a donné le -h ou -? , j'appelle le .Show sur le ConsoleForm et lorsque l'utilisateur appuie sur une touche, j'arrête le programme. Si l'utilisateur ne donne pas le -? commande, je ferme le ConsoleForm caché et lance le formulaire principal.

gverge
la source
2
Bonjour et bienvenue sur StackOverflow, évitez de poster des questions comme réponses, utilisez la section commentaires.
Pedro Rodrigues