Показати консоль у програмі Windows?


85

Чи є спосіб показати консоль у програмі Windows?

Я хочу зробити щось подібне:

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

Відповіді:


77

Те, що ви хочете зробити, неможливо осудним чином. Було подібне запитання, тож подивіться на відповіді .

Тоді також є шалений підхід (сайт не працює - тут можна створити резервну копію ), написаний Джеффрі Найтом :

Питання: Як створити програму, яка може працювати в режимі графічного інтерфейсу (Windows) або в режимі командного рядка / консолі?

На перший погляд, це здавалося б простим: ви створюєте консольний додаток, додаєте до нього віконну форму, і ви починаєте працювати. Однак є проблема:

Проблема. Якщо ви працюєте в режимі графічного інтерфейсу, у вас у вікні та прикрою консолі ховається у фоновому режимі, і у вас немає можливості приховати це.

Люди, здається, хочуть справжньої програми для земноводних, яка може безперебійно працювати в будь-якому режимі.

Якщо його розбити, насправді є чотири випадки використання тут:

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.

Я публікую код для цього, але з попередженням.

Я насправді думаю, що такий підхід призведе до набагато більших проблем, ніж це варте. Наприклад, вам доведеться мати два різних інтерфейси користувача - один для графічного інтерфейсу та інший для команди / оболонки. Вам доведеться створити якийсь дивний центральний логічний механізм, який абстрагується від графічного інтерфейсу проти командного рядка, і це просто стане дивним. Якби це був я, я відступив би і подумав про те, як це буде використано на практиці, і чи варто такого роду перемикання режимів варто працювати. Таким чином, якщо в цьому не вимагається якийсь особливий випадок, я б сам не використовував цей код, оскільки як тільки мені трапляються ситуації, коли мені потрібні виклики API, щоб щось зробити, я схиляюся зупинятись і запитувати себе: "Я надто ускладнюю речі?" ".

Тип виводу = Програма 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
Я вважаю це іронічним щодо Microsoft і про те, як вона хоче створити інтерфейси C # для всіх її API, але не існує способу C # виконати таке просте завдання.
Ramon Zarazua B.

3
Замість того , щоб в залежності від консолі є вікном переднього плану, ви можете отримати батьківський ідентифікатор процесу з поточною діяльністю з використанням WinAPI: stackoverflow.com/a/3346055/855432
ghord

2
На момент написання статті резервна копія статті доступна тут: web.archive.org/web/20111227234507/http://www.rootsilver.com/…
Ендрю Савіних,

2
Привіт! Я виявив, що якщо я запустив це рішення з оболонки, як Far, nc, воно створює нову консоль. Якщо я приєднуюсь до Far Console, як cmd, це працює неправильно. Я рекомендую створити ConsoleApplication, і якщо потрібен графічний інтерфейс, то виконайте FreeConsole (); Відмінна стаття! Дякую!
Максим Васильєв

6
Я рекомендував би зателефонувати за AttachConsoleдопомогою -1(значення константи API ATTACH_PARENT_PROCESS), а не сподіватися, що вікно переднього плану є правильним вікном команд для запису.
Джон Ханна,

70

Це трохи старе (добре, це ДУЖЕ старе), але я зараз роблю саме те саме. Ось дуже просте рішення, яке мені підходить:

[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
якщо ви запускаєте це у вікні cmd, це відкриє інше вікно консолі, що не є бажаним в автоматизованому процесі, який потребуватиме захоплення виводу консолі.
AaA

З мого кінця я використовував наданий код, але додав спільну функцію InitConsole (), щоб виконувати частину AllocConsole (), якщо дескриптор intptr.zero. Коли я використовував ShowConsoleWindow, а потім друкував на ньому одразу після цього, це не спрацювало. Виділення консолі під час запуску програми, а потім використання ShowConsoleWindow справді спрацювало. Крім цього, це ідеально для мене. Дякую ..
Sage Pourpre

Console.WriteLineжурнали не відображаються у справі, розпочатій із cmd з аргументами
Мохаммад Алі

Не забувайтеusing System.Runtime.InteropServices;
Даррен Гріффіт

19

Найпростіший спосіб - це запустити програму WinForms, перейти до налаштувань і змінити тип на консольний.


1
Додаток -> тип виводу: консольний додаток. Зробив це для мене!
Lodewijk

Це чудово працювало. Сподіваємось, це нічого іншого не зіпсує. Дякую та +1.
deathismyfriend

1
Запуск програми подвійним клацанням на ній відкриває вікно cmd
Mohammad Ali

13

Застереження

Існує спосіб досягти цього, який є досить простим, але я б не вважав, що це хороший підхід для програми, яку ви збираєтеся дозволити іншим людям побачити. Але якщо розробнику потрібно одночасно показати форми консолі та Windows, це можна зробити досить просто.

Цей метод також підтримує показ лише вікна консолі, але не підтримує показ лише форми Windows - тобто консоль буде завжди відображатися. Ви можете взаємодіяти (тобто отримувати дані - Console.ReadLine(), Console.Read()) з вікном консолі, лише якщо ви не показуєте форми Windows; вихід на консоль - Console.WriteLine()працює в обох режимах.

Це забезпечується як є; жодних гарантій, що це пізніше не зробить чогось жахливого, але це працює.

Крок проекту

Почніть із стандартної консольної програми .

Позначте Mainметод як[STAThread]

Додайте посилання у своєму проекті на System.Windows.Forms

Додавання Windows , форма для вашого проекту.

Додайте стандартний початковий код Windows до свого Mainметоду:

Кінцевий результат

У вас буде програма, яка відображає консоль та додатково форми Windows.

Зразок коду

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

хороший код тут. але як вимкнути показ консолі, якщо ви хочете продати або розгорнути чи щось інше. ???
r4ccoon

@ r4ccoon - ти не можеш. Але ви можете легко перенести весь свій код у звичайну програму Windows.
Sam Meldrum

Ну, IMHO простішим способом досягти того самого ефекту є створення Windows Forms Project, як зазвичай, потім клацніть його правою кнопкою миші в Провіднику рішень -> Властивості та змініть Тип виводу на Консольне додаток. (редагувати: тепер я зрозумів, що це в основному відповідь ICR)
Камілк

Приємна поетапна відповідь
AminM

9

Відновлення дуже старої теми ще раз, оскільки жодна з відповідей тут не працювала для мене дуже добре.

Я знайшов простий спосіб, який здається досить надійним і простим. У мене це спрацювало. Ідея:

  • Складіть свій проект як програму Windows. Під час запуску виконуваного файлу може існувати батьківська консоль, але, можливо, ні. Мета полягає в тому, щоб повторно використовувати існуючу консоль, якщо вона існує, або створити нову, якщо вона не існує.
  • AttachConsole (-1) буде шукати консоль батьківського процесу. Якщо він є, він прикріплюється до нього, і ви закінчили. (Я спробував це, і це спрацювало належним чином, викликаючи мою програму з cmd)
  • Якщо AttachConsole повернув false, батьківської консолі немає. Створіть його за допомогою AllocConsole.

Приклад:

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

Слово обережності: здається, що якщо ви спробуєте написати на консолі до приєднання або розміщення консолі, цей підхід не працює. Я думаю, ви вперше закликаєте Console.Write / WriteLine, якщо консолі ще немає, то Windows автоматично створює десь приховану консоль для вас. (Тож, можливо, відповідь Anthony ShowConsoleWindow краще після того, як ви вже написали на консоль, і моя відповідь буде кращою, якщо ви ще не писали на консолі). Важливо відзначити, що це не працює:

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

дякую, що поділилися зразком коду. Я спробував і виявив, що він працює, але з обмеженнями. Немає перенаправлення вихідних даних консолі (може бути виправлено додатковим вихідним кодом). Але головний недолік полягає в тому, що він негайно повертає управління на консоль. Наприклад, коли я набираю \bin\Debug>shareCheck.exe /once та натискаю Enter, з'являється командний рядок, а потім починає виводитися консоль: \bin\Debug>hello. It looks like you started me from an existing console.а коли програма закінчується, командного рядка немає, тому останній рядок виводу та порожній екран трохи божевільний
oleksa

Дякую Кевіну за слово обережності - у мене проблеми з підходами, запропонованими в цій публікації SO, і, схоже, я отримую "приховану консоль", хоча раніше у мене не було жодного виходу з консолі ... Виявилося, що вікно "Вивід" у Visual Studio - це прихована консоль, якщо ваша програма працює із налагодженим налагоджувачем! Варто згадати ... (отже, моя програма працювала під час перемикання на запуск без налагоджувача, тобто Ctrl-F5)
Per Lundberg

3

Мені вдалося написати окремо консольний додаток, який зробив те, що я хотів, зробити його компіляцією до exe, а потім зробити Process.Start("MyConsoleapp.exe","Arguments")


1
Це версія бритви Occam. Не досить складно: P
Michael Hoffmann

3

Перевірте цей вихідний код. Весь коментований код - використовується для створення консолі в програмі Windows. Без коментарів - щоб приховати консоль у програмі консолі. Від сюди . (Раніше тут .) Проект 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

Насправді AllocConsole з SetStdHandle у програмі графічного інтерфейсу може бути більш безпечним підходом. Проблема вже згаданого "викрадення консолі" полягає в тому, що консоль, можливо, взагалі не є вікном переднього плану (зокрема, враховуючи наплив нових менеджерів вікон у Vista / Windows 7), серед іншого.


0

У wind32 програми консольного режиму - це зовсім інший звір, ніж звичайні програми отримання черги повідомлень. Вони оголошуються та компілюються по-різному. Ви можете створити програму, яка має як консольну частину, так і звичайне вікно, і приховати те чи інше. Але підозрюєте, що ви знайдете для всього цього трохи більше роботи, ніж ви думали.


Wind32 звучить як щось, що належить на форумі метеорології, але якщо ви маєте на увазі Win32, то зверніть увагу, що ми можемо створювати цикли повідомлень у консольних програмах, як у PostThreadMessage to Console Application .
user34660

0

Згідно з цитатою Джеффрі Найта вище, як тільки я стикаюся з ситуаціями, коли мені потрібні виклики API, щоб щось зробити, я схиляюся до зупинки і запитую себе: "Я надто ускладнюю речі?".

Якщо бажано мати деякий код і запустити його в режимі графічного інтерфейсу користувача Windows або режимі консолі, розгляньте можливість переміщення коду, який використовується в обох режимах, до бібліотеки коду DLL, а потім мати програму Windows Forms, яка використовує цю бібліотеку DLL, і консоль додаток, який використовує цю бібліотеку DLL (тобто якщо у Visual Studio у вас тепер є рішення для трьох проектів: бібліотека з основною частиною коду, графічний інтерфейс лише з кодом Win Forms та консоль лише з вашим кодом консолі.)


0

І ще одна запізніла відповідь. Я не зміг отримати вихід на консоль, створену AllocConsoleза попередніми пропозиціями, тому замість цього я починаю з програми Console . Потім, якщо консоль не потрібна:

        [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;
        }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.