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

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?

Wil
источник
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();
wizzardz
источник
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

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

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

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

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

gunr2171
источник
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);
    }
}
cedd
источник
Я не мог писать на консоль; подключение к родительскому процессу сначала сделало свое дело. Спасибо.
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 ...", вы можете выводить любые сообщения и переменные. Также вы можете поставить / снять отметку с «Продолжить выполнение», и ваша точка останова станет квадратной. Таким образом, сообщения точки останова ничего не меняют в приложении в окне вывода отладки.

armagedescu
источник
0

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

gverge
источник
2
Здравствуйте и добро пожаловать в StackOverflow, не размещайте вопросы в качестве ответов, используйте раздел комментариев.
Педро Родригес
я не уверен, что это считается вопросом. Он дает полное пошаговое описание того, как делать то, что он предлагает, даже если реальный код был бы хорош.
Джон Лорд