Как показать консольный вывод / окно в приложении форм?


132

Чтобы сразу застрять, очень простой пример:

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?


2
в чем вы видите разницу между ними? Почему бы просто не скомпилировать как консоль и не показать форму.
Doggett

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

Я добился этого с помощью этого учебника: saezndaree.wordpress.com/2009/03/29/…
vivanov

Ответы:


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
Роб Джонсон 05

5
Основная проблема: при закрытии все приложение закрывается.
Марк

4
Я тестировал Windows 8 и Windows 10: - AttachConsole работает из окна cmd - AllocConsole работает из Visual Studio. Когда требуется выделение, AttachConsole возвращает false. Вы также должны вызвать FreeConsole () перед завершением приложения в режиме консоли. В своей программе я использовал код Мэтью Стробриджа (см. Ниже) с измененной строкой AttachConsole () на: if (! AttachConsole (-1)) AllocConsole ();
Беренд Энгельбрехт

Будет ли это работать в пользовательском элементе управления? Я работаю над созданием элемента управления SSH в качестве компонента winforms с использованием Granados (например), и это только фоновый компонент. Я хотел бы добавить красивую оболочку для отображения и использования консоли в компоненте.
Kraang Prime,

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

141

Возможно, это слишком упрощенно ...

Создать проект Windows Form ...

Затем: Свойства проекта -> Приложение -> Тип вывода -> Консольное приложение.

Затем можно запустить консоль и формы вместе, работает для меня


2
Кажется, самое простое, исправила и мою проблему.
dadude999

2
Это однозначно лучшее решение! Другие умны, но намного сложнее
LM.Croisez

3
Просто и нормально работало. Это должен быть принятый ответ.
Madu

7
Хотя да, технически это можно использовать, чтобы разрешить то, о чем просит плакат - это не лучшее решение. Таким образом, если вы затем запустите приложение winforms с графическим интерфейсом, вы также откроете окно консоли. В этом случае вам понадобится что-то вроде ответа Майка де Клерка.
Justin Greywolf

2
Это единственное решение, в котором мне удалось заставить мое приложение Winforms записывать вывод на консоль при запуске из командной строки или записывать в файл при перенаправлении в командной строке с помощью >. Однако я надеялся на решение, которое объяснило бы, как работать в качестве «Консольного приложения» только в некоторых случаях (то есть программно включить все, что происходит при изменении этого таинственного параметра Visual Studio). Кто-нибудь знает, как это работает под капотом?
uglycoyote

64

Если вы не беспокоитесь об открытии консоли по команде, вы можете войти в свойства своего проекта и изменить его на Консольное приложение.

скриншот изменения типа проекта,

Это по-прежнему покажет вашу форму, а также появится окно консоли. Вы не можете закрыть окно консоли, но оно работает как отличный временный регистратор для отладки.

Просто не забудьте выключить его перед развертыванием программы.


1
Ницца. Это решает проблему, с которой я сталкиваюсь с моим приложением форм: мне нужно иметь возможность вывода в окно консоли, поддерживая перенаправление вывода в файл. И мне не нужно прикреплять какую-либо консоль вручную ...
Кай Хартманн

3
@JasonHarrison Если закрыть окно консоли, программа закроется. Также окно всегда открыто во время работы программы.
gunr2171

2
@ gun2171: Спасибо. Минусы этого подхода отмечены в ответе: окно консоли появится, если приложение будет запущено двойным щелчком, меню «Пуск» и т. Д.
Джейсон Харрисон,

Есть ли способ отследить событие закрытия консоли?
Эльшан

17

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

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


+1 - Вау, я надеялся на console.show или подобное! намного сложнее, чем я думал! Я пока оставлю открытым на всякий случай, если есть лучший / простой ответ.
Wil

Это сработало для меня, AllocConsole () не сработало, потому что оно породило новое окно консоли (не копался дальше в AllocConsole, возможно, я что-то там пропустил).
derFunk

14

Это сработало для меня, чтобы передать вывод в файл. Вызовите консоль с помощью

cmd / c "C: \ path \ to \ your \ 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.
Саймон Хеффер,

Также попробовал ответ Чаза выше, но это дает новую консоль в Windows 7 (хотя не в 8 или 10). Мне просто нужна возможность запускать с перенаправлением в командной строке или запускать как графический интерфейс, если нет аргументов.
Саймон Хеффер,

Я пробовал это, но не сработало. Просто AttachConsole(ATTACH_PARENT_PROCESS)я получаю вывод консоли, но перенаправление его в командной строке с помощью >не работает. Когда я пробую этот ответ, я не могу получить никаких результатов ни в консоли, ни в файле.
uglycoyote

12

По сути, здесь могут произойти две вещи.

Вывод в консоль. Программа winforms может присоединиться к окну консоли, в котором она была создана (или к другому окну консоли, или даже к новому окну консоли, если необходимо). После подключения к окну консоли Console.WriteLine () и т.д. работает должным образом. Один из подводных камней этого подхода заключается в том, что программа немедленно возвращает управление окну консоли, а затем продолжает запись в него, поэтому пользователь может также вводить текст в окне консоли. Я думаю, вы можете использовать start с параметром / 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);
    }
}

Я не мог писать на консоль; подключение к родительскому процессу сначала сделало свое дело. Спасибо.
Pupper

Казалось бы, этот ответ требует, чтобы вы переписали все вызовы, Console.WriteLineчтобы вместо этого вызывать новый, WriteLineопределенный выше. Несмотря на то, что я пытался это сделать, я не смог с помощью этого кода перенаправить что-либо в файл при запуске приложения в командной строке и перенаправлении с >помощью в файл.
uglycoyote

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

это отлично работало для меня в Win10 без четногоStreamWriter _stdOutWriter;
TS

Ответом на этот вопрос является трубопровод, но вместо файла просто используйте БОЛЬШЕ, например: yourapp | Больше ; пожалуйста, обратитесь к stackoverflow.com/a/13010823/1845672
Роланд

9

Создайте приложение Windows Forms и измените тип вывода на консоль.

Это приведет к открытию и консоли, и формы .

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


Это именно то, что я ищу. Просто и без использования WINAPI.
Майкл Коксон

Я пробовал много примеров, но ни один из них не дал результатов, которые оправдали мои ожидания. Однако это решение - именно то, что я хотел, и это, безусловно, самое простое решение.
excitus

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
Каково предполагаемое использование initialiseOut и initialiseError, потому что они не используются?
Эдвин

StandardHandle : uintздесь не так ... должен быть IntPtr для работы как на x86, так и на x64
Дмитрий Гусаров

1

Вы можете в любой момент переключаться между типами приложений, консолью или окнами. Итак, вы не будете писать специальной логики для просмотра stdout. Кроме того, при запуске приложения в отладчике вы увидите весь стандартный вывод в окне вывода. Вы также можете просто добавить точку останова, а в свойствах точки останова изменить "When Hit ...", вы можете выводить любые сообщения и переменные. Также вы можете поставить / снять отметку с «Продолжить выполнение», и ваша точка останова станет квадратной. Таким образом, сообщения точки останова ничего не меняют в приложении в окне вывода отладки.


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.