Як відобразити консольний вихід / вікно у програмі форм?


131

Щоб застрягти прямо, дуже базовий приклад:

using System;
using System.Windows.Forms;

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

Якщо я компілюю це з параметрами за замовчуванням (використовуючи csc в командному рядку), як очікувалося, він буде компілюватися в консольний додаток. Крім того, оскільки я імпортував System.Windows.Forms, він також відображатиме вікно повідомлень.

Тепер, якщо я використовую параметр /target:winexe, який, на мою думку, є таким самим, як і вибір Windows Applicationу межах варіантів проекту, як і очікувалося, я побачу лише поле повідомлень і відсутність консольного виходу.

(Насправді, в момент його запуску з командного рядка я можу видавати наступну команду ще до того, як додаток навіть завершиться).

Отже, моє питання - я знаю, що ви можете мати "windows" / форми виводу з консольного додатка, але чи все-таки можна показати консоль із програми Windows?


2
в чому ви бачите різницю між ними? Чому б не просто скласти як консоль і показати форму.
Доггетт

7
@Doggett, просто - я навчаюсь і хочу зрозуміти, чому / як це зробити, навіть якщо я ніколи не закінчую його використанням у реальній програмі .... На даний момент я думаю про варіант, який дає додаткові команди / Вихід, такий як у VLC, проте TBH, мені це не потрібно - знову ж таки, я просто навчаюся і хочу це зрозуміти!
Віл

Відповіді:


153

це має працювати.

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
Дивовижно, це питання, здається, мені було задано багато, це єдина фактична відповідь на питання, яке мені вдалося знайти, +1
RobJohnson

5
Основна проблема: коли ви її закриєте, усі програми закриваються.
Марк

4
Я тестував на Windows 8 і Windows 10: - AttachConsole працює з cmd-вікна - AllocConsole працює від Visual Studio. Коли Alot потрібен, AttachConsole повертає значення false. Також слід зателефонувати FreeConsole () перед тим, як закрити програму в консольному режимі. У своїй програмі я використав код Меттью Страубриджа (див. Нижче), з рядком AttachConsole (), зміненим на: if (! AttachConsole (-1)) AllocConsole ();
Беренд Енгельбрехт

Чи буде це працювати в контролі користувача? Я працюю над створенням управління SSH як компонента winforms за допомогою Granados (наприклад), і це лише фоновий компонент. Я хотів би додати гарну обгортку для відображення та використання консолі в компоненті.
Прем'єр-міністр Краанг

2
Це не чудово, коли при запуску з командного рядка він відкриває окреме вікно консолі, а при запуску з командного рядка і намагаючись використовувати >для перенаправлення виводу, я отримую окреме вікно консолі та нульовий вихід у своєму файлі.
uglycoyote

139

Можливо, це занадто спрощено ...

Створення проекту Windows Form ...

Потім: Властивості проекту -> Застосування -> Тип виводу -> Приклад консолі

Тоді консолі та форми можуть працювати разом, працює для мене


2
Здається, найпростіше, вирішив і мою проблему.
dadude999

2
Це, безумовно, найкраще рішення! Інші
розумні,

3
Простий і чудово працював. Це має бути прийнятою відповіддю.
мад

7
Хоча, так, технічно це можна використовувати для того, щоб дозволити те, про що просить плакат - це не чудове рішення. Тим самим, якщо потім запустити програму winforms за допомогою GUI - ви також відкриєте вікно консолі. У цьому випадку вам знадобиться щось більше, як відповідь Майка де Клерка.
Джастін Грейвольф

2
Це єдине рішення, де мені вдалося отримати програму Winforms для запису виводу на консоль під час запуску з командного рядка або для запису у файл, коли перенаправлено на командний рядок >. Однак я сподівався на рішення, яке пояснить, як запускати як "консольний додаток" лише частину часу (тобто програматично включати все, що відбувається, що змінюється ця загадкова налаштування Visual Studio). Хтось знає, як це працює під кришкою?
uglycoyote

63

Якщо ви не турбуєтесь про відкриття консолі в команді, ви можете перейти до властивостей свого проекту та змінити його на консольну програму

скріншот зміни типу проекту.

Це все одно покаже вашу форму, а також спливе вікно консолі. Ви не можете закрити вікно консолі, але воно працює як відмінний тимчасовий реєстратор для налагодження.

Просто пам’ятайте, щоб вимкнути його перед тим, як розгорнути програму.


1
Приємно. Це вирішує проблему, що виникає з моїм додатком форм, що мені потрібно мати змогу виводити у вікно консолі, підтримуючи перенаправлення виводу у файл. І мені не потрібно прикріплювати будь-яку консоль вручну ...
Кай Хартманн

2
@JasonHarrison Якщо закрити вікно консолі, програма закриється. Також вікно завжди відкрите, поки програма працює.
gunr2171

2
@ gun2171: Дякую Мінуси цього підходу відзначаються у відповіді: вікно консолі з’явиться, якщо програма запускається подвійним клацанням, меню «Пуск» тощо.
Jason Harrison

17

Ви можете зателефонувати AttachConsoleза допомогою pinvoke, щоб отримати вікно консолі, приєднане до проекту WinForms: http://www.csharp411.com/console-output-from-winforms-application/

Ви також можете розглянути Log4net ( http://logging.apache.org/log4net/index.html ) для налаштування виводу журналу в різних конфігураціях.


+1 - Нічого собі, я сподівався на консоль.шоу чи подібне! набагато складніше, ніж я думав! Я залишаю відкритим на даний момент на всякий випадок, якщо є краща / легша відповідь.
Віл

Це працювало для мене, AllocConsole () зробив не тому, що він породив нове вікно консолі (не копався далі в AllocConsole, можливо, я щось там пропустив).
derFunk

14

Це працювало для мене, щоб передавати вихід у файл. Телефонуйте на консоль за допомогою

cmd / c "C: \ шлях \ до \ ваш \ application.exe"> myfile.txt

Додайте цей код у свою програму.

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

Я знайшов цей код тут: http://www.csharp411.com/console-output-from-winforms-application/ Я вважав, що це гідно, щоб також розмістити його тут.


5
Це чудово працює, крім того, що зараз він не працює в Windows 8 і Windows 10. Під невдачею я маю на увазі, що немає виходу, крім додаткового підказу (якщо це підказка). Хтось запропонував AllocConsole, але це просто спалахнуло вікно cmd.
Саймон Хеффер

Також спробував відповідь Chaz вище, але це дає нову консоль в Windows 7 (хоча це не в 8 або 10). Мені просто потрібна опція для запуску з перенаправленням в командному рядку або запуску як gui, якщо немає аргументів.
Саймон Хеффер

Я спробував це, але не вийшло. Щойно AttachConsole(ATTACH_PARENT_PROCESS)я отримую консольний вихід, але перенаправлення його в командному рядку >не працює. Коли я спробую цю відповідь, я не можу отримати жодного виводу ні в консолі, ні у файлі.
uglycoyote

12

Тут в основному можуть статися дві речі.

Вихід з консолі Програма winforms може приєднатись до вікна консолі, яке її створило (або до іншого вікна консолі, або, за бажанням, до нового вікна консолі). Після приєднання до вікна консолі Console.WriteLine () тощо працює так, як очікувалося. Одним із причин такого підходу є те, що програма негайно повертає контроль у вікно консолі, а потім продовжує писати до нього, щоб користувач також міг набрати у вікно консолі. Ви можете використовувати старт з параметром / wait, щоб впоратися з цим, я думаю.

Посилання для запуску синтаксису команд

Перенаправлений вихід консолі Це коли хтось передає вихід з вашої програми десь в іншому місці, наприклад.

yourapp> file.txt

Приєднання до вікна консолі в цьому випадку ефективно ігнорує трубопроводи. Щоб зробити цю роботу, ви можете зателефонувати на Console.OpenStandardOutput (), щоб отримати ручку до потоку, до якого повинен бути переданий вихід. Це працює лише в тому випадку, якщо вихід є конвеєрним, тому якщо ви хочете обробити обидва сценарії, вам потрібно відкрити стандартний вихід і записати його та приєднати до вікна консолі. Це означає, що вихід направляється у вікно консолі та на трубу, але найкраще рішення, яке я міг знайти. Нижче коду, який я використовую для цього.

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

Я не зміг записати на консоль; приєднання батьківського процесу спочатку зробило трюк. Дякую.
Щеня

Здається, ця відповідь вимагає від вас переписати всі дзвінки, Console.WriteLineщоб замість цього зателефонувати за новим, WriteLineвизначеним вище. Навіть незважаючи на те, що я не міг за допомогою цього коду отримати щось переспрямоване у файл під час запуску програми в командному рядку та переадресації >на файл.
uglycoyote

@uglycoyote, переконайтеся, що ви будуєте GUIConsoleWriter якомога раніше у вашій програмі, інакше він не працюватиме з таємничих причин типу Windows. Я б заперечував, що інкапсуляція дзвінків - Console.WriteLineце лише хороша практика, оскільки вона дозволяє вам протестувати та легко змінювати місця, в які ви входите (наприклад, ви можете запустити вхід у службу ведення журналів на основі хмар, як PaperTrail, або будь-що інше )
cedd

це добре спрацювало для мене в Win10 без парнихStreamWriter _stdOutWriter;
TS

Piping - це відповідь, але замість файлу просто використовуйте БІЛЬШЕ, наприклад: yourapp | більше; зверніться до stackoverflow.com/a/13010823/1845672
Roland

9

Створіть додаток Windows Forms та змініть тип виводу на Console.

Це призведе до відкриття консолі та форми .

введіть тут опис зображення


Це саме те, що я шукаю. Просто і не використовувати WINAPI.
Майкл Коксон

Я перепробував багато прикладів, але жоден з них не дав результатів, які виправдали мої сподівання. Однак це рішення є саме тим, що я хотів, і, безумовно, найпростішим рішенням.
невміло

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
Відмінно працює з командного рядка, але не з пуску> Виконати або у Visual Studio. Щоб змусити його працювати у всіх випадках, замініть рядок AttachConsole на: if (! AttachConsole (-1)) AllocConsole (); Якщо AllocConsole () викликається, FreeConsole () також слід викликати, інакше хост консолі продовжує працювати після завершення програми.
Беренд Енгельбрехт

2
Для чого призначене використання InitiiseOut та InitiiseError, оскільки вони не використовуються?
Едвін

StandardHandle : uintтут не так ... має бути IntPtr для роботи і на x86, і на x64
Дмитро Гусаров

1

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


0

Чому б просто не залишити його як додаток Window Forms, а створити просту форму для імітації консолі. Форма може бути такою, що виглядає як чорно екранована консоль, і відповісти їй безпосередньо на натискання клавіш. Потім у файлі program.cs ви вирішите, чи потрібно запускати основну форму чи ConsoleForm. Наприклад, я використовую такий підхід для збору аргументів командного рядка у файл program.cs. Я створюю ConsoleForm, спочатку приховую його, потім передаю рядки командного рядка функції AddCommand в ньому, яка відображає дозволені команди. Нарешті, якщо користувач дав -h або -? команду, я закликаю .Show на ConsoleForm і коли користувач натискає будь-яку клавішу на ньому, я вимикаю програму. Якщо користувач не дає -? команду, я закриваю прихований ConsoleForm і запускаю основну форму.


2
Привіт і ласкаво просимо до StackOverflow, уникайте розміщення питань у відповідях, скористайтеся розділом коментарів.
Педро Родрігес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.