Come faccio a mostrare un output / finestra della console in un'applicazione moduli?


131

Per rimanere bloccati subito, un esempio molto semplice:

using System;
using System.Windows.Forms;

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

Se lo compilo con le opzioni predefinite (usando csc dalla riga di comando), come previsto, verrà compilato in un'applicazione console. Inoltre, poiché ho importato System.Windows.Forms, mostrerà anche una finestra di messaggio.

Ora, se uso l'opzione /target:winexe, che penso sia la stessa scelta Windows Applicationall'interno delle opzioni del progetto, come previsto vedrò solo la finestra di messaggio e nessun output della console.

(In effetti, nel momento in cui viene lanciato dalla riga di comando, posso emettere il comando successivo prima ancora che l'applicazione sia stata completata).

Quindi, la mia domanda è: so che puoi avere "windows" / moduli di output da un'applicazione console, ma c'è comunque modo di mostrare la console da un'applicazione Windows?


2
quale vedi come differenza tra i due? Perché non compilare solo come console e mostrare un modulo.
Doggett,

7
@Doggett, semplice - Sto imparando e voglio capire perché / come farlo, anche se non finisco mai per usarlo in una vera applicazione .... Al momento, sto pensando a un'opzione che dà comandi extra / output come in VLC, comunque TBH, non ne ho bisogno - di nuovo, sto solo imparando e voglio capirlo!
Wil,

Ho realizzato questo usando questo tutorial: saezndaree.wordpress.com/2009/03/29/…
vivanov

Risposte:


153

questo dovrebbe funzionare.

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

8
Fantastica, questa domanda sembra essere stata posta molto, questa è l'unica vera risposta alla domanda che sono stato in grado di trovare, +1
RobJohnson

5
Problema principale: quando lo chiudi, tutte le applicazioni si chiudono.
Segna il

4
Ho provato su Windows 8 e Windows 10: - AttachConsole funziona da una scatola cmd - AllocConsole funziona da Visual Studio. Quando è necessario l'allocazione, AttachConsole restituisce false. Dovresti anche chiamare FreeConsole () prima di terminare l'applicazione in modalità console. Nel mio programma ho usato il codice di Matthew Strawbridge (vedi sotto), con la riga AttachConsole () modificata in: if (! AttachConsole (-1)) AllocConsole ();
Berend Engelbrecht,

Funzionerà con un controllo utente? Sto lavorando per creare un controllo SSH come componente winforms usando Granados (per esempio), ed è solo un componente in background. Vorrei aggiungere un simpatico wrapper per la visualizzazione e l'uso della console anche in un componente.
Kraang Prime,

2
Questo non è eccezionale, quando eseguito dalla riga di comando si apre una finestra della console separata e quando si esegue dalla riga di comando e si tenta di utilizzare >per reindirizzare l'output ottengo una finestra della console separata e zero output nel mio file.
uglycoyote

140

Forse questo è troppo semplicistico ...

Creare un progetto Windows Form ...

Quindi: Proprietà progetto -> Applicazione -> Tipo di output -> Applicazione console

Quindi possono avere console e moduli in esecuzione insieme, funziona per me


2
Sembra più semplice, risolto anche il mio problema.
dadude999,

2
Questa è sicuramente la soluzione migliore! Altri sono intelligenti ma complicati
LM.Croisez

3
Semplice e ha funzionato bene. Questa dovrebbe essere la risposta accettata.
madu,

7
Mentre, sì, tecnicamente questo può essere usato per consentire ciò che il poster sta chiedendo - non è un'ottima soluzione. In questo modo, se avvii la tua applicazione winforms con la GUI, otterrai anche una finestra della console aperta. In questo caso, avresti bisogno di qualcosa di più simile alla risposta di Mike de Klerk.
Justin Greywolf,

2
Questa è l'unica soluzione in cui sono stato in grado di ottenere la mia app Winforms per scrivere l'output sulla console quando eseguito dalla riga di comando o per scrivere su file quando reindirizzato sulla riga di comando con >. Tuttavia, speravo in una soluzione che spiegasse come eseguire come "Console Application" solo un po 'di tempo (ovvero abilitare a livello di programmazione qualunque cosa sia la modifica di questa misteriosa impostazione di Visual Studio). Qualcuno sa come funziona sotto il cofano?
uglycoyote

63

Se non ti preoccupi di aprire una console a comando, puoi andare nelle proprietà del tuo progetto e cambiarlo in Applicazione console

screenshot di modifica del tipo di progetto.

Questo mostrerà comunque il tuo modulo e aprirà una finestra della console. Non è possibile chiudere la finestra della console, ma funziona come un eccellente registratore temporaneo per il debug.

Ricorda solo di disattivarlo prima di distribuire il programma.


1
Bello. Questo risolve il problema che ho con la mia applicazione di moduli, che devo essere in grado di eseguire l'output su una finestra della console supportando il reindirizzamento dell'output su un file. E non ho bisogno di collegare alcuna console manualmente ...
Kai Hartmann,

2
@JasonHarrison Se si chiude la finestra della console, il programma si chiude. Inoltre, la finestra è sempre aperta durante l'esecuzione del programma.
gunr2171,

2
@ gun2171: Grazie. Gli svantaggi di questo approccio sono indicati nella risposta: la finestra della console apparirà se l'applicazione viene avviata con doppio clic, menu Start, ecc.
Jason Harrison

17

È possibile chiamare AttachConsoleutilizzando Pinvoke per ottenere una finestra della console collegata a un progetto WinForms: http://www.csharp411.com/console-output-from-winforms-application/

È inoltre possibile prendere in considerazione Log4net ( http://logging.apache.org/log4net/index.html ) per configurare l'output del registro in diverse configurazioni.


+1 - Wow, speravo in un console.show o simile! molto più complicato di quanto pensassi! Lascerò aperto per il momento nel caso in cui ci sia una risposta migliore / più facile.
Wil,

Questo ha funzionato per me, AllocConsole () non perché ha generato una nuova finestra della console (non ha approfondito ulteriormente AllocConsole, forse mi sono perso qualcosa lì).
derFunk

14

Questo ha funzionato per me, convogliare l'output in un file. Chiama la console con

cmd / c "C: \ path \ to \ your \ application.exe"> myfile.txt

Aggiungi questo codice alla tua applicazione.

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

Ho trovato questo codice qui: http://www.csharp411.com/console-output-from-winforms-application/ Ho pensato che fosse degno di pubblicarlo anche qui.


5
Funziona alla grande TRANNE che ora fallisce in Windows 8 e Windows 10. Per esito negativo, intendo dire che non c'è output se non un ulteriore prompt (se questo è un indizio). Qualcuno ha suggerito AllocConsole ma che ha appena mostrato una finestra cmd.
Simon Heffer,

Ho anche provato la risposta di Chaz sopra ma che fornisce una nuova console in Windows 7 (anche se non in 8 o 10). Ho solo bisogno dell'opzione per eseguire il reindirizzamento sulla riga di comando o eseguire come gui se non ci sono argomenti.
Simon Heffer,

Ho provato questo ma non ha funzionato. Con solo AttachConsole(ATTACH_PARENT_PROCESS)ottengo l'output della console ma reindirizzarlo dalla riga di comando con >non funziona. Quando provo questa risposta, non riesco a ottenere alcun output, né nella console né in un file.
uglycoyote

12

Ci sono sostanzialmente due cose che possono succedere qui.

Output della console È possibile che un programma winforms si colleghi alla finestra della console che lo ha creato (o a una finestra della console diversa o, se lo si desidera, a una nuova finestra della console). Una volta collegato alla finestra della console Console.WriteLine () ecc funziona come previsto. Un approccio a questo approccio è che il programma restituisce immediatamente il controllo alla finestra della console, quindi continua a scrivere su di esso, in modo che l'utente possa anche digitare nella finestra della console. Penso che si possa iniziare con il parametro / wait per gestire questo.

Collegamento per avviare la sintassi del comando

Output della console reindirizzato Questo è quando qualcuno indirizza l'output dal tuo programma da qualche altra parte, ad es.

yourapp> file.txt

Il collegamento a una finestra della console in questo caso ignora efficacemente le tubazioni. Per far funzionare tutto ciò, è possibile chiamare Console.OpenStandardOutput () per ottenere un handle per il flusso a cui deve essere inviato l'output. Funziona solo se l'output è reindirizzato, quindi se si desidera gestire entrambi gli scenari è necessario aprire l'output standard, scrivere su di esso e collegarsi alla finestra della console. Ciò significa che l'output viene inviato alla finestra della console e alla pipe, ma è la soluzione migliore che ho trovato. Sotto il codice che uso per fare questo.

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

Non sono riuscito a scrivere sulla console; collegare prima il processo genitore ha fatto il trucco. Grazie.
Pupper,

Sembrerebbe che questa risposta richieda di riscrivere tutte le chiamate per Console.WriteLinechiamare invece il nuovo WriteLinedefinito sopra. Anche se ho provato che non ero in grado con questo codice di ottenere qualcosa reindirizzato a un file quando eseguivo l'applicazione dalla riga di comando e reindirizzavo >a un file.
uglycoyote

@uglycoyote, assicurati di costruire GUIConsoleWriter il più presto possibile nella tua applicazione, altrimenti non funzionerà per misteriosi motivi del tipo di Windows. Direi che l'incapsulamento delle chiamate Console.WriteLineè solo una buona pratica, in quanto ti consente di testare e cambiare facilmente i luoghi in cui accedi (ad esempio, potresti voler avviare la registrazione su un servizio di registrazione basato su cloud come PaperTrail o qualsiasi altra cosa )
cedd

questo ha funzionato bene per me in Win10 senza nemmenoStreamWriter _stdOutWriter;
TS

Il piping è la risposta, ma invece di un file, basta usare ALTRO, come: yourapp | Di Più ; fare riferimento a stackoverflow.com/a/13010823/1845672
Roland

9

Creare un'applicazione Windows Form e modificare il tipo di output su Console.

Ciò comporterà sia una console che il modulo da aprire.

inserisci qui la descrizione dell'immagine


Questo è esattamente quello che sto cercando. Semplice e non utilizzando WINAPI.
Michael Coxon,

Ho provato molti esempi, ma nessuno di questi ha prodotto risultati che hanno soddisfatto le mie aspettative. Questa soluzione tuttavia è esattamente quello che volevo e di gran lunga la soluzione più semplice.
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;
        }
    }
}

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

1
Funziona bene da un prompt dei comandi, ma non da Start> Esegui o in Visual Studio. Per farlo funzionare in tutti i casi, sostituire la linea AttachConsole con: if (! AttachConsole (-1)) AllocConsole (); Se viene chiamato AllocConsole (), dovrebbe essere chiamato anche FreeConsole (), altrimenti l'host della console continua a funzionare dopo aver terminato il programma.
Berend Engelbrecht,

2
Qual è l'uso previsto di initialiseOut e initialiseError, perché non vengono utilizzati?
Edwin,

StandardHandle : uintè sbagliato qui ... dovrebbe essere IntPtr per funzionare sia su x86 che x64
Dmitry Gusarov

1

Puoi in qualsiasi momento passare dal tipo di applicazioni alla console o a Windows. Quindi, non scriverai una logica speciale per vedere lo stdout. Inoltre, quando si esegue l'applicazione nel debugger, vedrai tutto lo stdout nella finestra di output. Potresti anche solo aggiungere un punto di interruzione e nelle proprietà del punto di interruzione cambiare "Quando Hit ...", puoi generare qualsiasi messaggio e variabile. Inoltre puoi selezionare / deselezionare "Continua esecuzione" e il tuo punto di interruzione diventerà quadrato. Quindi, i messaggi di breakpoint senza cambiare nulla nell'applicazione nella finestra di output del debug.


0

Perché non lasciarlo semplicemente come un'app di Windows Form e creare un semplice modulo per imitare la console. Il modulo può apparire come la console schermata nera e farlo rispondere direttamente alla pressione del tasto. Quindi, nel file program.cs, decidi se devi eseguire il modulo principale o ConsoleForm. Ad esempio, utilizzo questo approccio per acquisire gli argomenti della riga di comando nel file program.cs. Creo ConsoleForm, inizialmente lo nascondo, quindi passo le stringhe della riga di comando a una funzione AddCommand, che visualizza i comandi consentiti. Infine, se l'utente ha dato il -h o -? comando, chiamo il .Show sul ConsoleForm e quando l'utente preme un tasto qualsiasi su di esso, chiudo il programma. Se l'utente non fornisce il -? comando, chiudo il ConsoleForm nascosto ed eseguo il modulo principale.


2
Ciao e benvenuto su StackOverflow, evita di pubblicare domande come risposte, usa la sezione commenti.
Pedro Rodrigues,
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.