Pokaż konsolę w aplikacji Windows?

Czy istnieje sposób na wyświetlenie konsoli w aplikacji Windows?

Chcę zrobić coś takiego:

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());
        }
    }
}
Author: Jeffrey Bosboom, 2009-01-23

9 answers

To, co chcesz zrobić, nie jest możliwe w rozsądny sposób. Było podobne pytanie więc spójrz na odpowiedzi .

Istnieje również insane approach (site down - backup dostępny tutaj.) napisany przez Jeffrey Knight :

Pytanie: Jak utworzyć aplikację, która może działać w obu GUI (windows) mode czy command line / console mode?

Z pozoru wydaje się to proste: tworzysz konsolę zastosowanie, Dodaj do niego formularz systemu windows i jesteś wyłączony. Jest jednak problem:

Problem: jeśli uruchamiasz się w trybie GUI, kończy się zarówno oknem, jak i nieznośna konsola czai się w tle, a ty nie masz jak Ukryj to.

Wydaje się, że ludzie chcą prawdziwej aplikacji, która może działać płynnie w obu trybach.

Jeśli go złamać, są w rzeczywistości cztery przypadki użycia tutaj:

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.

Zamieszczam kod do to, ale z zastrzeżeniem.

Myślę, że takie podejście doprowadzi Cię do o wiele więcej kłopoty na drodze, niż są warte. Na przykład, będziesz musiał mają dwa różne interfejsy użytkownika - jeden dla GUI i jeden dla polecenia / shell. Będziesz musiał zbudować jakąś dziwną centralną logikę silnik, który pobiera z GUI vs. wiersz poleceń, i po prostu idzie żeby stać się dziwnym. Gdybym to był ja, cofnąłbym się i pomyślał o tym, jak to będą stosowane w praktyce, oraz czy tego rodzaju przełączanie trybu jest warte pracy. Tak więc, o ile nie wymagał tego jakiś szczególny przypadek, ja sam bym tego kodu nie używał, bo jak tylko wpadnę na sytuacje, w których potrzebuję wywołań API, aby coś zrobić, mam tendencję do zatrzymajcie się i zadajcie sobie pytanie: "czy jestem zbyt skomplikowany?".

Typ wyjścia = aplikacja 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
Author: Igal Serban,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:20

To jest trochę stare (OK, jest bardzo stare), ale robię dokładnie to samo teraz. Oto bardzo proste rozwiązanie, które działa dla mnie:

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

    [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;
 63
Author: Anthony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-26 00:18:29

Najprostszym sposobem jest uruchomienie aplikacji WinForms, przejście do ustawień i zmiana typu na aplikację konsolową.

 17
Author: ICR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-23 10:12:25

Disclaimer

Istnieje sposób, aby to osiągnąć, który jest dość prosty, ale nie sugerowałbym, że jest to dobre podejście do aplikacji, którą chcesz pokazać innym ludziom. Ale jeśli masz trochę programisty trzeba pokazać konsoli i formularzy windows w tym samym czasie, można to zrobić dość łatwo.

Ta metoda obsługuje również wyświetlanie tylko okna konsoli, ale nie obsługuje wyświetlania tylko formularza Windows - tzn. konsola będzie zawsze wyświetlana. Możesz tylko wchodzić w interakcje (tzn. otrzymywać DANE - Console.ReadLine(), Console.Read()) w oknie konsoli, jeśli nie pokazujesz formularzy windows; wyjście do konsoli - Console.WriteLine() - działa w obu trybach.

Jest to zapewnione tak, jak jest; nie ma gwarancji, że to nie zrobi później czegoś strasznego, ale to działa.

Kroki projektu

Zacznij od standardowej aplikacji konsolowej.

Oznacz metodę Main jako [STAThread]

Dodaj referencję w swoim projekcie do systemu .Okna.Formularze

Dodaj okno formularz do twojego projektu.

Dodaj standardowy kod startowy systemu Windows do metody Main:

Wynik Końcowy

Będziesz miał aplikację, która wyświetla konsolę i opcjonalnie formularze windows.

Przykładowy Kod

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
Author: Sam Meldrum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-23 09:30:58

Wskrzeszenie bardzo starego wątku po raz kolejny, ponieważ żadna z odpowiedzi tutaj nie działa bardzo dobrze dla mnie.

Znalazłem prosty sposób, który wydaje się dość solidny i prosty. U mnie zadziałało. Pomysł:

  • Skompiluj swój projekt jako aplikację Windows. Podczas uruchamiania programu może istnieć konsola nadrzędna, ale może nie. Celem jest ponowne użycie istniejącej konsoli, jeśli taka istnieje, lub utworzenie nowej, jeśli nie.
  • AttachConsole (-1) będzie szukać konsoli rodzica proces. Jeśli taki jest, to się do niego przywiązuje i jesteś skończony. (Próbowałem tego i działało poprawnie podczas wywoływania mojej aplikacji z cmd)
  • Jeśli AttachConsole zwróciło false, nie ma konsoli nadrzędnej. Utwórz go za pomocą AllocConsole.

Przykład:

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

Uwaga : wydaje się, że jeśli spróbujesz napisać do konsoli przed dołączeniem lub przydzieleniu konsoli, takie podejście nie zadziała. Zgaduję, że pierwszy raz dzwonisz do konsoli.Napisz/WriteLine, jeśli tam nie jest już konsolą, a System Windows automatycznie tworzy ukrytą konsolę gdzieś dla Ciebie. (Więc być może odpowiedź Anthony ' ego ShowConsoleWindow jest lepsza po tym, jak już napisałeś do konsoli, a moja odpowiedź jest lepsza, jeśli jeszcze nie napisałeś do konsoli). Ważne jest, aby pamiętać, że to nie działa:

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());
        }
    }
 6
Author: Kevin Holt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-10 19:04:56

Dla mnie to, co zadziałało, to napisać osobno aplikację konsolową, która zrobiła to, co chciałem, skompilować ją do exe, a następnie zrobić Process.Start("MyConsoleapp.exe","Arguments")

 3
Author: Ian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-23 23:45:53

Sprawdź ten kod źródłowy. Cały skomentowany kod-służy do tworzenia konsoli w aplikacji Windows. Niezatwierdzone-aby ukryć konsolę w aplikacji konsolowej. Z TUTAJ . (Poprzednio proszę..) Projekt 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
Author: abatishchev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-16 09:43:58

Właściwie AllocConsole z SetStdHandle w aplikacji GUI może być bezpieczniejszym podejściem. Problem z" console hijacking " już wspomniano, jest to, że konsola może nie być pierwszym oknie w ogóle, (esp. biorąc pod uwagę napływ nowych menedżerów okien w Vista/Windows 7) między innymi.

 1
Author: EFraim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-02 20:01:29

W wind32 aplikacje w trybie konsolowym są zupełnie inną bestią niż zwykłe aplikacje odbierające kolejki wiadomości. Są one deklarowane i kompilowane w różny sposób. Możesz utworzyć aplikację, która ma zarówno część konsoli, jak i normalne okno i ukryć jedno lub drugie. Ale podejrzewam, że będzie to trochę więcej pracy niż myślałeś.

 0
Author: Joe Soul-bringer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-23 08:50:49