Bagaimana cara menampilkan output konsol / jendela di aplikasi formulir?

131

Untuk langsung terjebak, contoh yang sangat mendasar:

using System;
using System.Windows.Forms;

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

Jika saya mengkompilasi ini dengan opsi default (menggunakan csc pada command line), seperti yang diharapkan, itu akan dikompilasi ke aplikasi konsol. Juga, karena saya mengimporSystem.Windows.Forms , itu juga akan menampilkan kotak pesan.

Sekarang, jika saya menggunakan opsi /target:winexe, yang saya pikir sama dengan memilihWindows Application dari dalam opsi proyek, seperti yang diharapkan saya hanya akan melihat Kotak Pesan dan tidak ada output konsol.

(Bahkan, saat diluncurkan dari baris perintah, saya dapat mengeluarkan perintah berikutnya bahkan sebelum aplikasi selesai).

Jadi, pertanyaan saya adalah - Saya tahu Anda dapat memiliki "windows" / membentuk output dari aplikasi konsol, tetapi apakah ada juga untuk menunjukkan konsol dari aplikasi Windows?

Wil
sumber
2
apa yang Anda lihat sebagai perbedaan antara keduanya? Mengapa tidak mengkompilasi sebagai konsol dan menunjukkan formulir.
Doggett
7
@Doggett, sederhana - Saya sedang belajar dan ingin memahami mengapa / bagaimana melakukannya, bahkan jika saya tidak pernah menggunakannya di aplikasi nyata .... Saat ini, saya memikirkan pilihan yang memberikan perintah tambahan / output seperti di VLC, namun TBH, saya tidak membutuhkannya - lagi, hanya belajar dan ingin memahaminya!
Wil
Saya menyelesaikannya dengan menggunakan tutorial ini: saezndaree.wordpress.com/2009/03/29/…
vivanov

Jawaban:

153

ini harus bekerja.

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
sumber
8
Luar biasa, pertanyaan ini tampaknya telah banyak ditanyakan, ini adalah satu-satunya jawaban aktual untuk pertanyaan yang saya dapat temukan, +1
RobJohnson
5
Masalah utama: ketika Anda menutupnya, semua aplikasi ditutup.
Markus
4
Saya menguji pada Windows 8 dan Windows 10: - AttachConsole bekerja dari kotak cmd - AllocConsole bekerja dari Visual Studio. Ketika alokasi dibutuhkan, AttachConsole mengembalikan false. Anda juga harus menghubungi FreeConsole () sebelum menghentikan aplikasi dalam mode konsol. Dalam program saya, saya menggunakan kode Matthew Strawbridge (lihat di bawah), dengan baris AttachConsole () dimodifikasi menjadi: if (! AttachConsole (-1)) AllocConsole ();
Berend Engelbrecht
Apakah ini akan berfungsi dalam kontrol pengguna? Saya sedang bekerja membuat kontrol SSH sebagai komponen winforms menggunakan Granados (misalnya), dan itu hanya komponen latar belakang. Saya ingin menambahkan pembungkus yang bagus untuk ditampilkan dan menggunakan konsol dalam komponen juga.
Kraang Prime
2
Ini tidak bagus, ketika dijalankan dari baris perintah, ia akan membuka jendela konsol terpisah , dan ketika menjalankan dari baris perintah dan mencoba menggunakan >untuk mengarahkan ulang output saya mendapatkan jendela konsol terpisah dan nol output dalam file saya.
uglycoyote
139

Mungkin ini terlalu sederhana ...

Buat proyek Windows Form ...

Kemudian: Properti Proyek -> Aplikasi -> Jenis Output -> Aplikasi Konsol

Kemudian dapat membuat Konsol dan Formulir berjalan bersama, berfungsi untuk saya

Chaz
sumber
2
Tampak sederhana, perbaiki masalah saya juga.
dadude999
2
Ini jelas merupakan solusi terbaik! Yang lain
pintar
3
Sederhana dan bekerja dengan baik. Ini harus menjadi jawaban yang diterima.
madu
7
Sementara, ya, secara teknis ini dapat digunakan untuk memungkinkan apa yang diminta poster - itu bukan solusi yang bagus. Dengan melakukan ini, jika Anda kemudian memulai aplikasi winforms Anda dengan GUI - Anda juga akan membuka jendela konsol. Dalam hal ini, Anda akan membutuhkan sesuatu yang lebih seperti jawaban Mike de Klerk.
Justin Greywolf
2
Ini adalah satu-satunya solusi di mana saya bisa mendapatkan aplikasi Winforms saya untuk menulis output ke konsol ketika dijalankan dari baris perintah, atau menulis ke file ketika dialihkan pada baris perintah dengan >. Namun, saya berharap untuk solusi yang akan menjelaskan bagaimana menjalankan sebagai "Aplikasi Konsol" hanya beberapa waktu (yaitu untuk secara terprogram mengaktifkan apa pun yang mengubah pengaturan Visual Studio misterius ini tidak). Adakah yang tahu cara kerjanya di bawah tenda?
uglycoyote
63

Jika Anda tidak khawatir tentang membuka konsol sesuai perintah, Anda dapat masuk ke properti untuk proyek Anda dan mengubahnya ke Aplikasi Konsol

tangkapan layar untuk mengubah jenis proyek.

Ini masih akan menunjukkan formulir Anda serta muncul jendela konsol. Anda tidak bisa menutup jendela konsol, tetapi berfungsi sebagai pencatat sementara yang bagus untuk debugging.

Ingatlah untuk mematikannya sebelum Anda menggunakan program ini.

gunr2171
sumber
1
Bagus. Ini menyelesaikan masalah yang saya miliki dengan formulir aplikasi saya, bahwa saya harus dapat menampilkan ke jendela konsol sambil mendukung pengalihan keluaran ke file. Dan saya tidak perlu memasang konsol apa pun secara manual ...
Kai Hartmann
2
@JasonHarrison Jika Anda menutup jendela konsol, program ditutup. Juga jendela selalu terbuka saat program berjalan.
gunr2171
2
@ gun2171: Terima kasih. Kelemahan dari pendekatan ini tercantum dalam jawaban: jendela konsol akan muncul jika aplikasi diluncurkan dengan klik dua kali, menu Mulai, dll.
Jason Harrison
17

Anda dapat menelepon AttachConsolemenggunakan pinvoke untuk mendapatkan jendela konsol terpasang ke proyek WinForms: http://www.csharp411.com/console-output-from-winforms-application/

Anda mungkin juga ingin mempertimbangkan Log4net ( http://logging.apache.org/log4net/index.html ) untuk mengkonfigurasi keluaran log dalam konfigurasi yang berbeda.

Adam Vandenberg
sumber
+1 - Wow, saya berharap untuk konsol.show atau serupa! jauh lebih rumit dari yang saya kira! Saya akan membiarkan terbuka untuk saat ini kalau-kalau ada jawaban yang lebih baik / lebih mudah.
Wil
Ini bekerja untuk saya, AllocConsole () tidak karena itu melahirkan jendela konsol baru (tidak menggali lebih jauh ke AllocConsole, mungkin saya melewatkan sesuatu di sana).
derFunk
14

Ini bekerja untuk saya, untuk menyalurkan output ke file. Panggil konsol dengan

cmd / c "C: \ path \ to \ your \ application.exe"> myfile.txt

Tambahkan kode ini ke aplikasi Anda.

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

Saya menemukan kode ini di sini: http://www.csharp411.com/console-output-from-winforms-application/ Saya pikir layak untuk mempostingnya di sini juga.

Mike de Klerk
sumber
5
Ini berfungsi KECUALI, sekarang gagal di Windows 8 dan Windows 10. Secara gagal, maksud saya tidak ada output kecuali dan prompt ekstra (jika itu petunjuk). Seseorang menyarankan AllocConsole tetapi hanya mem-flash jendela cmd.
Simon Heffer
Juga mencoba jawaban Chaz di atas tetapi itu memberikan konsol baru di Windows 7 (lathough tidak di 8 atau 10). Saya hanya perlu opsi untuk dijalankan dengan pengalihan pada baris perintah atau dijalankan sebagai gui jika tidak ada argumen.
Simon Heffer
Saya mencoba ini tetapi tidak berhasil. Dengan hanya AttachConsole(ATTACH_PARENT_PROCESS)saya mendapatkan output konsol tetapi mengarahkannya kembali pada baris perintah dengan >tidak bekerja. Ketika saya mencoba jawaban ini, saya tidak bisa mendapatkan output apa pun, baik di konsol atau di file.
uglycoyote
12

Pada dasarnya ada dua hal yang bisa terjadi di sini.

Output konsol Adalah mungkin bagi program winforms untuk menempelkan dirinya sendiri ke jendela konsol yang membuatnya (atau ke jendela konsol yang berbeda, atau memang ke jendela konsol baru jika diinginkan). Setelah terpasang ke konsol jendela Console.WriteLine () dll berfungsi seperti yang diharapkan. Salah satu yang perlu dari pendekatan ini adalah bahwa program mengembalikan kontrol ke jendela konsol dengan segera, dan kemudian melanjutkan menulis ke sana, sehingga pengguna juga dapat mengetik di jendela konsol. Anda dapat menggunakan mulai dengan parameter / tunggu untuk menangani ini saya pikir.

Tautan untuk memulai sintaks Perintah

Keluaran konsol yang dialihkan Ini adalah ketika seseorang mengirimkan output dari program Anda ke tempat lain, mis.

aplikasi Anda> file.txt

Melampirkan ke jendela konsol dalam hal ini secara efektif mengabaikan perpipaan. Untuk membuat ini bekerja, Anda dapat memanggil Console.OpenStandardOutput () untuk mendapatkan pegangan ke aliran yang harus dikirim melalui pipa. Ini hanya berfungsi jika output disalurkan, jadi jika Anda ingin menangani kedua skenario, Anda perlu membuka output standar dan menulis ke sana dan melampirkan ke jendela konsol. Ini berarti bahwa output dikirim ke jendela konsol dan ke pipa tetapi solusi terbaik yang bisa saya temukan. Di bawah kode yang saya gunakan untuk melakukan ini.

// 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
sumber
Saya tidak dapat menulis ke konsol; melampirkan proses induk terlebih dahulu melakukan trik. Terima kasih.
Pupper
Tampaknya jawaban ini mengharuskan Anda untuk menulis ulang semua panggilan Console.WriteLineuntuk bukannya memanggil yang baru WriteLinedidefinisikan di atas. Meskipun saya mencoba bahwa saya tidak dapat dengan kode ini untuk mendapatkan apa pun diarahkan ke file ketika menjalankan aplikasi pada baris perintah dan mengarahkan >ke file.
uglycoyote
@uglycoyote, pastikan Anda membuat GUIConsoleWriter sedini mungkin dalam aplikasi Anda, jika tidak, itu tidak akan berfungsi untuk alasan tipe jendela misterius. Saya berpendapat bahwa merangkum panggilan ke Console.WriteLineadalah praktik yang baik, karena memungkinkan Anda untuk menguji, dan dengan mudah mengubah tempat-tempat yang Anda masuki (misalnya, Anda mungkin ingin mulai masuk ke layanan logging berbasis cloud seperti PaperTrail, atau apa pun )
cedd
ini bekerja dengan baik untuk saya di Win10 bahkan tanpaStreamWriter _stdOutWriter;
TS
Perpipaan adalah jawabannya, tetapi alih-alih ke file, cukup gunakan MORE, seperti: yourapp | lebih banyak; silakan merujuk ke stackoverflow.com/a/13010823/1845672
Roland
9

Buat Aplikasi Formulir Windows, dan ubah tipe output ke Konsol.

Ini akan menghasilkan konsol dan formulir untuk dibuka.

masukkan deskripsi gambar di sini

Pedro Rodrigues
sumber
Inilah yang saya cari. Sederhana dan tidak menggunakan WINAPI.
Michael Coxon
Saya telah mencoba banyak contoh, tetapi tidak ada yang memberikan hasil yang memenuhi harapan saya. Namun solusi ini persis apa yang saya inginkan dan sejauh ini solusi termudah.
inexcitus
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;
        }
    }
}
Kamil Kh
sumber
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));
    }
}
lap
sumber
1
Bekerja dengan baik dari command prompt, tetapi tidak dari Start> Run atau di Visual Studio. Untuk membuatnya berfungsi dalam semua kasus, ganti baris AttachConsole dengan: if (! AttachConsole (-1)) AllocConsole (); Jika AllocConsole () dipanggil, FreeConsole () juga harus dipanggil, jika host konsol terus berjalan setelah menghentikan program.
Berend Engelbrecht
2
Apa tujuan penggunaan initialiseOut dan initialiseError, karena tidak digunakan?
Edwin
StandardHandle : uintsalah di sini ... seharusnya IntPtr dapat berfungsi pada x86 dan x64
Dmitry Gusarov
1

Anda dapat setiap saat beralih di antara jenis aplikasi, ke konsol atau windows. Jadi, Anda tidak akan menulis logika khusus untuk melihat stdout. Juga, ketika menjalankan aplikasi dalam debugger, Anda akan melihat semua stdout di jendela output. Anda mungkin juga menambahkan breakpoint, dan pada breakpoint properties ubah "When Hit ...", Anda bisa menampilkan pesan, dan variabel apa saja. Anda juga dapat memeriksa / menghapus centang "Lanjutkan eksekusi", dan breakpoint Anda akan menjadi berbentuk persegi. Jadi, pesan breakpoint tanpa mengubah apa pun dalam aplikasi di jendela output debug.

armagedescu
sumber
0

Mengapa tidak tinggalkan saja sebagai aplikasi Window Forms, dan buat formulir sederhana untuk meniru Konsol. Bentuknya dapat dibuat agar terlihat seperti Konsol layar-hitam, dan langsung merespons ke tombol tekan. Kemudian, dalam file program.cs, Anda memutuskan apakah Anda perlu menjalankan formulir utama atau ConsoleForm. Sebagai contoh, saya menggunakan pendekatan ini untuk menangkap argumen baris perintah di file program.cs. Saya membuat ConsoleForm, awalnya menyembunyikannya, lalu meneruskan string baris perintah ke fungsi AddCommand di dalamnya, yang menampilkan perintah yang diizinkan. Akhirnya, jika pengguna memberi -h atau -? perintah, saya memanggil .Show on the ConsoleForm dan ketika pengguna menekan salah satu tombol di atasnya, saya mematikan program. Jika pengguna tidak memberikan -? perintah, saya menutup ConsoleForm tersembunyi dan Jalankan formulir utama.

Gverge
sumber
2
Halo dan selamat datang di StackOverflow, hindari memposting pertanyaan sebagai jawaban, gunakan bagian komentar.
Pedro Rodrigues