web-dev-qa-db-fra.com

Afficher la console dans une application Windows?

Est-il possible d'afficher la console dans une application Windows?

Je veux faire quelque chose comme ça:

static class Program
{
    [STAThread]
    static void Main(string[] args) {
        bool consoleMode = Boolean.Parse(args[0]);

        if (consoleMode) {
            Console.WriteLine("consolemode started");
            // ...
        } else {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}
79
Ase

Ce que vous voulez faire n'est pas possible d'une manière saine. Il y avait une question similaire alors regardez les réponses .

Ensuite, il y a aussi une approche insensée (site en panne - sauvegarde disponible ici. ) écrite par Jeffrey Knight :

Question: Comment créer une application pouvant fonctionner en mode interface graphique (Windows) ou en mode ligne de commande/console?

À première vue, cela semblerait simple: vous créez une application console, vous y ajoutez un formulaire Windows et vous êtes opérationnel. Cependant, il y a un problème:

Problème: Si vous utilisez le mode interface graphique, vous obtenez à la fois une fenêtre et une console embêtante qui se cache à l’arrière-plan et vous n’avez aucun moyen de le cacher.

Ce que les gens semblent vouloir, c'est une véritable application amphibie qui peut fonctionner sans problème dans les deux modes.

Si vous décomposez, il y a en fait quatre cas d'utilisation ici:

User starts application from existing cmd window, and runs in GUI mode
User double clicks to start application, and runs in GUI mode
User starts application from existing cmd window, and runs in command mode
User double clicks to start application, and runs in command mode.

Je poste le code pour faire cela, mais avec une mise en garde.

En fait, je pense que ce type d’approche vous causera bien plus de problèmes qu’il ne le vaut. Par exemple, vous devrez avoir deux interfaces utilisateur différentes: une pour l'interface graphique et l'autre pour la commande/Shell. Vous allez devoir créer un étrange moteur logique central, abstrait de la GUI par rapport à la ligne de commande, et ça va devenir bizarre. Si c’était moi, je prendrais du recul et réfléchirais à la manière dont cela serait utilisé dans la pratique et si ce type de changement de mode valait la peine. Ainsi, à moins que des cas particuliers ne l'exigent, je n'utiliserais pas ce code moi-même, car dès que je rencontre des situations où j'ai besoin d'appels d'API pour obtenir quelque chose, j'ai tendance à m'arrêter et à me demander: "est-ce que je complique les choses? ".

Type de sortie = Application Windows

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;

namespace WindowsApplication
{
    static class Program
    {
        /*
    DEMO CODE ONLY: In general, this approach calls for re-thinking 
    your architecture!
    There are 4 possible ways this can run:
    1) User starts application from existing cmd window, and runs in GUI mode
    2) User double clicks to start application, and runs in GUI mode
    3) User starts applicaiton from existing cmd window, and runs in command mode
    4) User double clicks to start application, and runs in command mode.

    To run in console mode, start a cmd Shell and enter:
        c:\path\to\Debug\dir\WindowsApplication.exe console
        To run in gui mode,  EITHER just double click the exe, OR start it from the cmd Prompt with:
        c:\path\to\Debug\dir\WindowsApplication.exe (or pass the "gui" argument).
        To start in command mode from a double click, change the default below to "console".
    In practice, I'm not even sure how the console vs gui mode distinction would be made from a
    double click...
        string mode = args.Length > 0 ? args[0] : "console"; //default to console
    */

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeConsole();

        [DllImport("kernel32", SetLastError = true)]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [STAThread]
        static void Main(string[] args)
        {
            //TODO: better handling of command args, (handle help (--help /?) etc.)
            string mode = args.Length > 0 ? args[0] : "gui"; //default to gui

            if (mode == "gui")
            {
                MessageBox.Show("Welcome to GUI mode");

                Application.EnableVisualStyles();

                Application.SetCompatibleTextRenderingDefault(false);

                Application.Run(new Form1());
            }
            else if (mode == "console")
            {

                //Get a pointer to the forground window.  The idea here is that
                //IF the user is starting our application from an existing console
                //Shell, that Shell will be the uppermost window.  We'll get it
                //and attach to it
                IntPtr ptr = GetForegroundWindow();

                int  u;

                GetWindowThreadProcessId(ptr, out u);

                Process process = Process.GetProcessById(u);

                if (process.ProcessName == "cmd" )    //Is the uppermost window a cmd process?
                {
                    AttachConsole(process.Id);

                    //we have a console to attach to ..
                    Console.WriteLine("hello. It looks like you started me from an existing console.");
                }
                else
                {
                    //no console AND we're in console mode ... create a new console.

                    AllocConsole();

                    Console.WriteLine(@"hello. It looks like you double clicked me to start
                   AND you want console mode.  Here's a new console.");
                    Console.WriteLine("press any key to continue ...");
                    Console.ReadLine();       
                }

                FreeConsole();
            }
        }
    }
}
76
Igal Serban

C'est un peu vieux (OK, c'est très vieux), mais je fais exactement la même chose en ce moment. Voici une solution très simple qui fonctionne pour moi:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

public static void ShowConsoleWindow()
{
    var handle = GetConsoleWindow();

    if (handle == IntPtr.Zero)
    {
        AllocConsole();
    }
    else
    {
        ShowWindow(handle, SW_SHOW);
    }
}

public static void HideConsoleWindow()
{
    var handle = GetConsoleWindow();
    ShowWindow(handle, SW_HIDE);
}
65
Anthony

Le moyen le plus simple est de démarrer une application WinForms, d’accéder aux paramètres et de changer le type en application console.

17
ICR

Avertissement

Il existe un moyen très simple d'y parvenir, mais je ne dirais pas que c'est une bonne approche pour une application que vous allez laisser voir aux autres personnes. Toutefois, si certains développeurs doivent afficher simultanément les formulaires de la console et de Windows, vous pouvez le faire assez facilement.

Cette méthode prend également en charge l’affichage de la fenêtre de la console uniquement, mais pas l’affichage du formulaire Windows uniquement - c’est-à-dire que la console sera toujours affichée. Vous ne pouvez interagir (c.-à-d. Recevoir des données - Console.ReadLine(), Console.Read()) avec la fenêtre de la console que si vous ne présentez pas les formulaires Windows; sortie vers la console - Console.WriteLine() - fonctionne dans les deux modes.

Ceci est fourni tel quel; rien ne garantit que cela ne fera pas quelque chose d'horrible plus tard, mais ça marche.

Étapes du projet

Commencez par un standard application console.

Marquez la méthode Main comme [STAThread]

Ajouter une référence dans votre projet à System.Windows.Forms

Ajoutez un Windows Form à votre projet.

Ajoutez le code de démarrage Windows standard à votre méthode Main:

Résultat final

Vous aurez une application qui affiche la console et éventuellement les formulaires Windows.

Exemple de code

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    class Program {

        [STAThread]
        static void Main(string[] args) {

            if (args.Length > 0 && args[0] == "console") {
                Console.WriteLine("Hello world!");
                Console.ReadLine();
            }
            else {
                Application.EnableVisualStyles(); 
                Application.SetCompatibleTextRenderingDefault(false); 
                Application.Run(new Form1());
            }
        }
    }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Click(object sender, EventArgs e) {
            Console.WriteLine("Clicked");
        }
    }
}
13
Sam Meldrum

Ressusciter encore une fois un très vieux fil, aucune des réponses ici ne fonctionnant très bien pour moi.

J'ai trouvé un moyen simple qui semble assez robuste et simple. Cela a fonctionné pour moi. L'idée:

  • Compilez votre projet en tant qu'application Windows. Il peut y avoir une console parent au démarrage de votre exécutable, mais peut-être pas. L'objectif est de réutiliser la console existante s'il en existe une ou d'en créer une nouvelle sinon.
  • AttachConsole (-1) recherche la console du processus parent. S'il y en a un, il s'y attache et vous avez terminé. (J'ai essayé ceci et cela a fonctionné correctement lorsque j'ai appelé mon application depuis cmd)
  • Si AttachConsole a renvoyé la valeur false, il n'y a pas de console parent. Créez-en un avec AllocConsole.

Exemple:

static class Program
{
    [DllImport( "kernel32.dll", SetLastError = true )]
    static extern bool AllocConsole();

    [DllImport( "kernel32", SetLastError = true )]
    static extern bool AttachConsole( int dwProcessId );

    static void Main(string[] args)
    {
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }
}

Attention: il semble que si vous essayez d'écrire sur la console avant de connecter ou d'allouer une console, cette approche ne fonctionne pas. À mon avis, c'est la première fois que vous appelez Console.Write/WriteLine. S'il n'y a pas déjà de console, Windows crée automatiquement une console cachée quelque part pour vous. (Alors peut-être que la réponse de Anthony ShowConsoleWindow est meilleure après que vous avez déjà écrit sur la console, et ma réponse est meilleure si vous n'avez pas encore écrit sur la console). La chose importante à noter est que cela ne fonctionne pas:

static void Main(string[] args)
    {
        Console.WriteLine("Welcome to the program");   //< this ruins everything
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");   //< this doesn't get displayed on the parent console
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }
7
Kevin Holt

Ce qui a fonctionné pour moi a été d’écrire séparément une application console qui faisait ce que je voulais, de la compiler en un exe, puis de faire Process.Start("MyConsoleapp.exe","Arguments")

3
Ian

Vérifiez ce code source. Tout le code commenté - utilisé pour créer une console dans une application Windows. Uncommented - pour masquer la console dans une application console. De ici . (Précédemment ici.) Projet reg2run.

// Copyright (C) 2005-2015 Alexander Batishchev (abatishchev at gmail.com)

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Reg2Run
{
    static class ManualConsole
    {
        #region DllImport
        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool AllocConsole();
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)]string fileName, [MarshalAs(UnmanagedType.I4)]int desiredAccess, [MarshalAs(UnmanagedType.I4)]int shareMode, IntPtr securityAttributes, [MarshalAs(UnmanagedType.I4)]int creationDisposition, [MarshalAs(UnmanagedType.I4)]int flagsAndAttributes, IntPtr templateFile);
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool FreeConsole();

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr GetStdHandle([MarshalAs(UnmanagedType.I4)]int nStdHandle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetStdHandle(int nStdHandle, IntPtr handle);
        */
        #endregion

        #region Methods
        /*
        public static void Create()
        {
            var ptr = GetStdHandle(-11);
            if (!AllocConsole())
            {
                throw new Win32Exception("AllocConsole");
            }
            ptr = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
            if (!SetStdHandle(-11, ptr))
            {
                throw new Win32Exception("SetStdHandle");
            }
            var newOut = new StreamWriter(Console.OpenStandardOutput());
            newOut.AutoFlush = true;
            Console.SetOut(newOut);
            Console.SetError(newOut);
        }
        */

        public static void Hide()
        {
            var ptr = GetStdHandle(-11);
            if (!CloseHandle(ptr))
            {
                throw new Win32Exception();
            }
            ptr = IntPtr.Zero;
            if (!FreeConsole())
            {
                throw new Win32Exception();
            }
        }
        #endregion
    }
}
2
abatishchev

En fait, AllocConsole avec SetStdHandle dans une application graphique peut être une approche plus sûre. Le problème avec le "détournement de console" déjà mentionné, est que la console peut ne pas être une fenêtre de premier plan (en particulier compte tenu de l'afflux de nouveaux gestionnaires de fenêtres dans Vista/Windows 7).

1
EFraim

Dans wind32, les applications en mode console sont complètement différentes des applications de réception de messages en attente. Ils sont déclarés et compilés différemment. Vous pouvez créer une application comportant à la fois une partie console et une fenêtre normale et masquer l’une ou l’autre. Mais suspectez-vous de trouver un peu plus de travail que vous ne le pensiez.

0
Joe Soul-bringer