Mostra la console nell'applicazione Windows?


85

C'è un modo per mostrare la console in un'applicazione Windows?

Voglio fare qualcosa del genere:

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());
        }
    }
}

Risposte:


77

Quello che vuoi fare non è possibile in modo sano. C'era una domanda simile, quindi guarda le risposte .

Poi c'è anche un approccio folle (sito inattivo - backup disponibile qui. ) Scritto da Jeffrey Knight :

Domanda: come si crea un'applicazione che può essere eseguita in modalità GUI (Windows) o in modalità riga di comando / console?

In apparenza, questo sembrerebbe facile: crei un'applicazione console, aggiungi un windows form e sei fuori e in esecuzione. Tuttavia, c'è un problema:

Problema: se esegui in modalità GUI, ti ritroverai con una finestra e una fastidiosa console in agguato sullo sfondo e non hai alcun modo per nasconderlo.

Quello che le persone sembrano desiderare è una vera applicazione anfibia che possa funzionare senza problemi in entrambe le modalità.

Se lo scomponi, in realtà ci sono quattro casi d'uso qui:

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.

Sto postando il codice per farlo, ma con un avvertimento.

In realtà penso che questo tipo di approccio ti metterà in molti più guai lungo la strada di quanto valga la pena. Ad esempio, dovrai avere due diverse interfacce utente: una per la GUI e una per il comando / shell. Dovrai costruire uno strano motore logico centrale che astrae dalla GUI rispetto alla riga di comando, e diventerà strano. Se fossi io, farei un passo indietro e penserei a come verrà utilizzato nella pratica e se questo tipo di cambio di modalità valga il lavoro. Quindi, a meno che non lo richieda un caso speciale, non userei questo codice da solo, perché non appena mi imbatto in situazioni in cui ho bisogno di chiamate API per fare qualcosa, tendo a fermarmi e chiedermi "sto complicando troppo le cose? ".

Tipo di output = Applicazione 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();
            }
        }
    }
}

13
Lo trovo ironico con Microsoft e con il modo in cui desidera creare interfacce C # per tutte le sue API, ma non esiste un modo C # per eseguire un'attività così semplice.
Ramon Zarazua B.

3
Piuttosto che a seconda della console è la finestra in primo piano, si potrebbe ottenere processo padre id del processo corrente utilizzando WinAPI: stackoverflow.com/a/3346055/855432
Ghord

2
Al momento in cui scrivo, una copia di backup dell'articolo è disponibile qui web.archive.org/web/20111227234507/http://www.rootsilver.com/…
Andrew Savinykh

2
Ciao! Ho scoperto che se ho eseguito questa soluzione da shell come Far, nc crea una nuova console. Se mi collego a Far Console come cmd funziona male. Consiglio di creare ConsoleApplication e se la GUI è necessaria, allora fai FreeConsole (); Ottimo articolo! Grazie!
Maxim Vasiliev

6
Consiglierei di chiamare AttachConsolecon -1(il valore della costante API ATTACH_PARENT_PROCESS) piuttosto che sperare che la finestra in primo piano sia la finestra di comando giusta in cui scrivere.
Jon Hanna

70

Questo è un po 'vecchio (OK, è MOLTO vecchio), ma sto facendo esattamente la stessa cosa adesso. Ecco una soluzione molto semplice che funziona per me:

[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);
}

5
se lo stai eseguendo in una finestra cmd, si aprirà un'altra finestra della console che non è desiderabile nel processo automatizzato che dovrà acquisire l'output della console.
AaA

Da parte mia, ho usato il codice fornito ma ho aggiunto una funzione condivisa InitConsole () per fare la parte AllocConsole () se handle è intptr.zero. Quando ho usato ShowConsoleWindow e stampato su di esso subito dopo, non ha funzionato. Allocare la console all'avvio dell'applicazione, quindi utilizzare ShowConsoleWindow ha funzionato. Oltre a questo, questo è perfetto per me. Grazie ..
Sage Pourpre

Console.WriteLinei log non vengono visualizzati nel caso avviato da cmd con args
Mohammad Ali

Non dimenticareusing System.Runtime.InteropServices;
Darren Griffith

19

Il modo più semplice è avviare un'applicazione WinForms, andare alle impostazioni e modificare il tipo in un'applicazione console.


1
Applicazione -> tipo di output: Applicazione console. L'ha fatto per me!
Lodewijk

Ha funzionato alla grande. Si spera che non rovini nient'altro. Grazie e +1.
deathismyfriend

1
Avviando l'applicazione facendo doppio clic su di essa si apre una finestra di cmd
Mohammad Ali

13

Disclaimer

C'è un modo per ottenere ciò che è abbastanza semplice, ma non suggerirei che sia un buon approccio per un'app che farai vedere ad altre persone. Ma se uno sviluppatore ha bisogno di mostrare la console e i moduli di Windows allo stesso tempo, può essere fatto abbastanza facilmente.

Questo metodo supporta anche la visualizzazione solo della finestra della console, ma non supporta la visualizzazione solo del Windows Form, ovvero la console verrà sempre visualizzata. Puoi interagire (cioè ricevere dati - Console.ReadLine(), Console.Read()) con la finestra della console solo se non mostri i moduli di Windows; output su Console - Console.WriteLine()- funziona in entrambe le modalità.

Viene fornito così com'è; nessuna garanzia che questo non farà qualcosa di orribile in seguito, ma funziona.

Fasi del progetto

Inizia da un'applicazione console standard .

Contrassegna il Mainmetodo come[STAThread]

Aggiungere un riferimento nel progetto a System.Windows.Forms

Aggiungi un Windows Form al tuo progetto.

Aggiungi il codice di avvio standard di Windows al tuo Mainmetodo:

Risultato finale

Avrai un'applicazione che mostra la Console e, facoltativamente, i moduli di Windows.

Codice d'esempio

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");
        }
    }
}

bel codice qui. ma come disabiliti la visualizzazione della console se vuoi vendere o distribuire o qualcos'altro. ???
r4ccoon

@ r4ccoon - non puoi. Ma puoi spostare facilmente tutto il tuo codice in una normale app di Windows.
Sam Meldrum

Bene, IMHO un modo più semplice per ottenere lo stesso effetto è creare un progetto Windows Form come al solito, quindi fare clic con il pulsante destro del mouse in Esplora soluzioni -> Proprietà e modificare il tipo di output in Applicazione console. (modifica: ora ho capito che è fondamentalmente la risposta di ICR)
kamilk

9

Resuscitare ancora una volta un thread molto vecchio, poiché nessuna delle risposte qui ha funzionato molto bene per me.

Ho trovato un modo semplice che sembra abbastanza robusto e semplice. Ha funzionato per me. L'idea:

  • Compila il tuo progetto come applicazione Windows. Potrebbe esserci una console genitore all'avvio del tuo eseguibile, ma forse no. L'obiettivo è riutilizzare la console esistente, se esiste, o crearne una nuova in caso contrario.
  • AttachConsole (-1) cercherà la console del processo genitore. Se ce n'è uno, si attacca ad esso e hai finito. (Ho provato questo e ha funzionato correttamente quando ho chiamato la mia applicazione da cmd)
  • Se AttachConsole ha restituito false, non esiste una console padre. Creane uno con AllocConsole.

Esempio:

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());
        }
    }
}

Un avvertimento: sembra che se provi a scrivere sulla console prima di collegare o allocare una console, questo approccio non funziona. La mia ipotesi è la prima volta che chiami Console.Write / WriteLine, se non c'è già una console, Windows crea automaticamente una console nascosta da qualche parte per te. (Quindi forse la risposta di Anthony ShowConsoleWindow è migliore dopo che hai già scritto sulla console, e la mia risposta è migliore se non hai ancora scritto sulla console). La cosa importante da notare è che questo non funziona:

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());
        }
    }

grazie per aver condiviso il codice di esempio. L'ho provato e ho scoperto che funziona ma con limitazioni. Non esiste un reindirizzamento dell'output della console (può essere risolto con codice sorgente aggiuntivo). Ma lo svantaggio principale è che restituisce immediatamente il controllo alla console. Ad esempio, quando digito \bin\Debug>shareCheck.exe /once e premo Invio, viene visualizzato il prompt dei comandi e quindi la console inizia a produrre: \bin\Debug>hello. It looks like you started me from an existing console.e quando il programma termina non c'è alcun prompt dei comandi, quindi l'ultima riga di output e lo schermo vuoto sono un po 'pazzi
oleksa

Grazie Kevin per la parola di cautela - Ho problemi con gli approcci suggeriti in questo post SO e mi sembra di ottenere la "console nascosta" anche se non ho alcun output della console in corso prima ... Si è scoperto che la finestra "Output" in Visual Studio è la console nascosta, se la tua app è in esecuzione con il debugger allegato! Vale la pena menzionare ... (quindi, il mio programma ha funzionato quando si passa all'esecuzione senza debugger, ovvero Ctrl-F5)
Per Lundberg

3

Quello che ha funzionato per me è stato scrivere un'app per console separatamente che facesse quello che volevo, compilarla in un exe e Process.Start("MyConsoleapp.exe","Arguments")


1
Questa è la versione rasoio di Occam. Non abbastanza impegnativo: P
Michael Hoffmann

3

Controlla questo codice sorgente. Tutto il codice commentato: utilizzato per creare una console in un'app di Windows. Senza commenti: per nascondere la console in un'app console. Da qui . (In precedenza qui .) Progetto 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
    }
}

1

In realtà AllocConsole con SetStdHandle in un'applicazione GUI potrebbe essere un approccio più sicuro. Il problema con il "dirottamento della console" già menzionato, è che la console potrebbe non essere affatto una finestra in primo piano, (specialmente considerando l'afflusso di nuovi gestori di finestre in Vista / Windows 7) tra le altre cose.


0

In wind32, le applicazioni in modalità console sono una bestia completamente diversa dalle solite applicazioni di ricezione delle code di messaggi. Sono dichiarati e si compilano in modo diverso. È possibile creare un'applicazione che abbia sia una parte console che una finestra normale e nascondere l'una o l'altra. Ma sospetta che troverai l'intera faccenda un po 'più di lavoro di quanto pensassi.


Wind32 suona come qualcosa che appartiene a un forum di meteorologia, ma se intendi Win32, tieni presente che possiamo creare loop di messaggi nei programmi della console, come in PostThreadMessage nell'applicazione console .
user34660

0

Come da citazione di Jeffrey Knight sopra, non appena mi imbatto in situazioni in cui ho bisogno di chiamate API per fare qualcosa, tendo a fermarmi e chiedermi "sto complicando troppo le cose?".

Se ciò che si desidera è disporre di un codice ed eseguirlo in modalità GUI di Windows o in modalità Console, valutare la possibilità di spostare il codice utilizzato in entrambe le modalità su una DLL della libreria di codici e quindi avere un'applicazione Windows Form che utilizza tale DLL e una console applicazione che utilizza quella DLL (ovvero, se in Visual Studio ora hai una soluzione a tre progetti: libreria con la maggior parte del codice, GUI con solo il codice Win Forms e Console con solo il codice della tua console.)


0

E ancora un'altra risposta tardiva. Non sono riuscito a ottenere alcun output sulla console creata AllocConsolesecondo i suggerimenti precedenti, quindi sto iniziando con l' applicazione Console . Quindi, se la console non è necessaria:

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

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

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

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

        public static bool HideConsole()
        {
            var hwnd = GetConsoleWindow();
            GetWindowThreadProcessId(hwnd, out var pid);

            if (pid != Process.GetCurrentProcess().Id) // It's not our console - don't mess with it.
                return false;

            ShowWindow(hwnd, SW_HIDE);

            return true;
        }
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.