Tidak ada keluaran ke konsol dari aplikasi WPF?

113

Saya menggunakan Console.WriteLine () dari aplikasi uji WPF yang sangat sederhana, tetapi ketika saya menjalankan aplikasi dari baris perintah, saya tidak melihat ada yang ditulis ke konsol. Adakah yang tahu apa yang mungkin terjadi di sini?

Saya dapat mereproduksinya dengan membuat aplikasi WPF di VS 2008, dan hanya menambahkan Console.WriteLine ("teks") di mana saja yang dapat dieksekusi. Ada ide?

Yang saya butuhkan saat ini adalah sesuatu yang sederhana seperti Console.WriteLine (). Saya menyadari bahwa saya dapat menggunakan log4net atau solusi logging lainnya, tetapi saya benar-benar tidak membutuhkan banyak fungsi untuk aplikasi ini.

Sunting: Saya harus ingat bahwa Console.WriteLine () adalah untuk aplikasi konsol. Oh baiklah, tidak ada pertanyaan bodoh, bukan? :-) Saya hanya akan menggunakan System.Diagnostics.Trace.WriteLine () dan DebugView untuk saat ini.

rampok
sumber
Kemungkinan duplikat di sini dan di sini (lebih baru, tetapi dengan beberapa jawaban menarik menggunakan AttachConsole dari Kernel32.dll )
Max
1
@Max, pertanyaan-pertanyaan itu kemungkinan duplikat dari pertanyaan ini . Pertanyaan ini ditanyakan 2-4 tahun sebelum salah satu pertanyaan yang Anda posting.
Rob

Jawaban:

91

Anda harus membuat jendela Konsol secara manual sebelum Anda benar-benar memanggil metode Console.Write. Itu akan membuat Konsol berfungsi dengan baik tanpa mengubah jenis proyek (yang untuk aplikasi WPF tidak akan berfungsi).

Berikut adalah contoh kode sumber lengkap, tentang bagaimana kelas ConsoleManager akan terlihat, dan bagaimana kelas itu dapat digunakan untuk mengaktifkan / menonaktifkan Konsol, secara independen dari jenis proyek.

Dengan kelas berikut, Anda hanya perlu menulis di ConsoleManager.Show()suatu tempat sebelum panggilan ke Console.Write...

[SuppressUnmanagedCodeSecurity]
public static class ConsoleManager
{
    private const string Kernel32_DllName = "kernel32.dll";

    [DllImport(Kernel32_DllName)]
    private static extern bool AllocConsole();

    [DllImport(Kernel32_DllName)]
    private static extern bool FreeConsole();

    [DllImport(Kernel32_DllName)]
    private static extern IntPtr GetConsoleWindow();

    [DllImport(Kernel32_DllName)]
    private static extern int GetConsoleOutputCP();

    public static bool HasConsole
    {
        get { return GetConsoleWindow() != IntPtr.Zero; }
    }

    /// <summary>
    /// Creates a new console instance if the process is not attached to a console already.
    /// </summary>
    public static void Show()
    {
        //#if DEBUG
        if (!HasConsole)
        {
            AllocConsole();
            InvalidateOutAndError();
        }
        //#endif
    }

    /// <summary>
    /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown.
    /// </summary>
    public static void Hide()
    {
        //#if DEBUG
        if (HasConsole)
        {
            SetOutAndErrorNull();
            FreeConsole();
        }
        //#endif
    }

    public static void Toggle()
    {
        if (HasConsole)
        {
            Hide();
        }
        else
        {
            Show();
        }
    }

    static void InvalidateOutAndError()
    {
        Type type = typeof(System.Console);

        System.Reflection.FieldInfo _out = type.GetField("_out",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.FieldInfo _error = type.GetField("_error",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError",
            System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);

        Debug.Assert(_out != null);
        Debug.Assert(_error != null);

        Debug.Assert(_InitializeStdOutError != null);

        _out.SetValue(null, null);
        _error.SetValue(null, null);

        _InitializeStdOutError.Invoke(null, new object[] { true });
    }

    static void SetOutAndErrorNull()
    {
        Console.SetOut(TextWriter.Null);
        Console.SetError(TextWriter.Null);
    }
} 
John Leidegren
sumber
5
Dimungkinkan untuk mencoba memanggil AttachConsole (-1) terlebih dahulu dan memeriksa nilai kembaliannya untuk dilampirkan ke konsol proses induk; jika hasilnya salah, panggil AllocConsole. Namun, aplikasi masih 'mengembalikan' terlebih dahulu dan baru kemudian mengeluarkannya ke konsol, saya akan memposting lebih banyak jika saya menemukan solusi. Selain itu, jika Anda menyetel jenis aplikasi WPF ke Aplikasi Konsol, masalahnya akan hilang tetapi Anda tidak dapat melepaskan konsol tanpa menampilkannya di layar sebentar saat program dimulai, jadi agak terlihat aneh (tetapi jika Anda bisa menerimanya) , itu bekerja dengan baik).
Alex Paven
2
Eh, sebenarnya tidak, saya rasa tidak mungkin untuk memiliki keduanya; Aplikasi Konsol ditandai sebagai CUI di header PE-nya dan karenanya bekerja sama dengan baik dengan CMD secara otomatis. Sebaliknya, aplikasi GUI mengembalikan kontrol ke CMD dengan segera, dan bahkan jika dapat disambungkan kembali ke konsol, membaca dan menulis akan bercampur dengan keluaran berikutnya dalam pipeline, yang jelas sangat buruk. Jika di sisi lain Anda menandai aplikasi sebagai aplikasi Konsol, Anda hanya perlu melihat CMD yang muncul sebentar saat aplikasi dimulai; Anda kemudian dapat menggunakan FreeConsole untuk melepaskan dan Lampirkan / Alokasikan nanti dll
Alex Paven
1
Mengapa melakukan ini ketika jawaban dari Brian bekerja dengan baik dan jauh lebih mudah.
Wouter Jans
2
Mungkin jelas, tetapi saya menemukan bahwa Console.WriteLine masih tidak berfungsi menggunakan teknik ini ketika debugger Visual Studio dipasang. Ketika saya menjalankan aplikasi di luar VS, itu berhasil. Terima kasih.
aboy021
2
@Mark Ya, tetapi tidak berhasil ... Ada SetConsoleCtrlHandlerfungsi yang memungkinkan untuk diberi tahu ketika CTRL_CLOSE_EVENTperistiwa terjadi tetapi Anda tidak dapat melakukan apa pun dengannya, tidak ada yang memungkinkan aplikasi Anda untuk melanjutkan. Anda akan dimatikan. Jika Anda merasa ingin meretas, Anda mungkin dapat menukar penangan pesan windows untuk proses konsol dan hanya menjatuhkan pesan WM_CLOSE, saya belum pernah mencoba ini tetapi itu bisa berhasil. Ini hanya jendela lain tetapi dengan itu berkata, kecuali Anda ingin menghibur ide ini, usaha Anda mungkin lebih baik dihabiskan untuk melakukan sesuatu yang lain.
John Leidegren
129

Klik kanan pada proyek, "Properties", tab "Application", ubah "Output Type" menjadi "Console Application", dan kemudian itu juga akan memiliki konsol.

Brian
sumber
2
Satu-satunya masalah dengan itu adalah Anda akan memiliki cmd terbuka di latar belakang, tetapi berhasil :).
ykatchou
5
Bagus, tetapi jendela baris perintah akan dibuat ketika aplikasi tidak dijalankan dari cmd.exe (dua jendela dibuat untuk satu aplikasi). Tetapi untuk ini ada juga solusi: Anda dapat menyembunyikan jendela cmd dengan ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . Menggunakan solusi ini Anda akan melihat teks di konsol hanya ketika aplikasi WPF dijalankan dari baris perintah.
CoperNick
Perhatikan bahwa Anda harus mengalihkannya kembali ke "Aplikasi Jendela" saat bekerja di Blend, karena hanya menampilkan XAML (tanpa akses ke Tampilan Desain) untuk jenis "Aplikasi Konsol". (pada Blend 2013)
2
Tidak benar ans. Sembunyikan Windows utama. Hanya konsol yang muncul.
Yash
tidak bekerja dengan wpf, tidak ada jendela utama
alp
129

Kamu bisa memakai

Trace.WriteLine("text");

Ini akan menampilkan jendela "Output" di Visual Studio (saat debugging).

pastikan untuk memiliki rakitan Diagnostik yang disertakan:

using System.Diagnostics;
Phobis
sumber
9
ini adalah jawaban terbaik, tetapi tidak memiliki peringkat tertinggi
kiltek
Saya setuju - inilah yang diminta oleh op. Alternatif yang bagus untuk Console.WriteLine () - solusi yang ditandai sebagai jawaban adalah latihan yang rapi tetapi tidak masuk akal untuk disertakan dalam aplikasi produksi.
nocarrier
4
PS untuk aplikasi Windows Store (Windows Runtime) setara dengan Trace.WriteLine adalah Debug.WriteLine ()
nocarrier
Ini adalah solusi yang sederhana dan bersih, namun tidak berhasil untuk saya. Tidak berfungsi dalam metode seed framework entitas selama update-database. Jika tidak, bekerja di tempat lain!
Charles W
Ini solusi terbaik. Akan lebih baik jika jawabannya juga menjelaskan bahwa Console.WriteLineitu tidak dimaksudkan untuk aplikasi WPF sama sekali, dan itu dimaksudkan hanya untuk aplikasi baris perintah.
Andrew Koster
12

Meskipun John Leidegren terus menolak gagasan itu, Brian benar. Saya baru saja membuatnya berfungsi di Visual Studio.

Untuk memperjelas, aplikasi WPF tidak membuat jendela Konsol secara default.

Anda harus membuat Aplikasi WPF dan kemudian mengubah OutputType menjadi "Aplikasi Konsol". Saat Anda menjalankan proyek, Anda akan melihat jendela konsol dengan jendela WPF Anda di depannya.

Ini tidak terlihat sangat cantik, tetapi saya merasa terbantu karena saya ingin aplikasi saya dijalankan dari baris perintah dengan umpan balik di sana, dan kemudian untuk opsi perintah tertentu saya akan menampilkan jendela WPF.


sumber
1
Sempurna. Melakukan pekerjaan itu.
frostymarvelous
10

Mungkin untuk melihat keluaran yang ditujukan untuk konsol dengan menggunakan pengalihan baris perintah .

Sebagai contoh:

C:\src\bin\Debug\Example.exe > output.txt

akan menulis semua konten ke output.txtfile.

Lu55
sumber
Jawaban terbaik karena sederhana dan tidak memerlukan perubahan pada sumbernya
buckley
9

Posting lama, tetapi saya mengalami ini jadi jika Anda mencoba mengeluarkan sesuatu ke Output dalam proyek WPF di Visual Studio, metode kontemporernya adalah:

Sertakan ini:

using System.Diagnostics;

Lalu:

Debug.WriteLine("something");
Smitty
sumber
4

Saya menggunakan Console.WriteLine () untuk digunakan di jendela Output ...

erodewald
sumber
4
Ini adalah pertanyaan berusia 4 tahun yang telah banyak diedit sejak saya pertama kali melihatnya. Sekarang tentu saja pertanyaannya telah disusun dengan lebih baik dan tanggapan saya dibuat tidak relevan.
erodewald
1

Saya telah membuat solusi, mencampur informasi dari varius post.

Ini adalah formulir, yang berisi label dan satu kotak teks. Output konsol dialihkan ke kotak teks.

Ada terlalu kelas yang disebut ConsoleView yang mengimplementasikan tiga metode publik: Show (), Close (), dan Release (). Yang terakhir adalah membiarkan konsol terbuka dan mengaktifkan tombol Tutup untuk melihat hasil.

Bentuknya disebut FrmConsole. Berikut adalah kode XAML dan c #.

Penggunaannya sangat sederhana:

ConsoleView.Show("Title of the Console");

Untuk membuka konsol. Menggunakan:

System.Console.WriteLine("The debug message");

Untuk teks keluaran ke konsol.

Menggunakan:

ConsoleView.Close();

Untuk Tutup konsol.

ConsoleView.Release();

Membiarkan konsol terbuka dan mengaktifkan tombol Tutup

XAML

<Window x:Class="CustomControls.FrmConsole"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:CustomControls"
    mc:Ignorable="d"
    Height="500" Width="600" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Topmost="True" Icon="Images/icoConsole.png">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="40"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="40"/>
    </Grid.RowDefinitions>
    <Label Grid.Row="0" Name="lblTitulo" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" FontFamily="Arial" FontSize="14" FontWeight="Bold" Content="Titulo"/>
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="1" Name="txtInner" FontFamily="Arial" FontSize="10" ScrollViewer.CanContentScroll="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible" TextWrapping="Wrap"/>
    </Grid>
    <Button Name="btnCerrar" Grid.Row="2" Content="Cerrar" Width="100" Height="30" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"/>
</Grid>

Kode Jendela:

partial class FrmConsole : Window
{
    private class ControlWriter : TextWriter
    {
        private TextBox textbox;
        public ControlWriter(TextBox textbox)
        {
            this.textbox = textbox;
        }

        public override void WriteLine(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void WriteLine(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.AppendText(Environment.NewLine);
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(char value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value.ToString());
                textbox.ScrollToEnd();
            }));
        }

        public override void Write(string value)
        {
            textbox.Dispatcher.Invoke(new Action(() =>
            {
                textbox.AppendText(value);
                textbox.ScrollToEnd();
            }));
        }

        public override Encoding Encoding
        {
            get { return Encoding.UTF8; }

        }
    }

    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE

    #endregion


    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    public FrmConsole(string titulo)
    {
        InitializeComponent();
        lblTitulo.Content = titulo;
        Clear();
        btnCerrar.Click += new RoutedEventHandler(BtnCerrar_Click);
        Console.SetOut(new ControlWriter(txtInner));
        DesactivarCerrar();
    }

    #endregion


    //PROPIEDADES
    #region PROPIEDADES

    #endregion


    //DELEGADOS
    #region DELEGADOS

    private void BtnCerrar_Click(object sender, RoutedEventArgs e)
    {
        Close();
    }

    #endregion


    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public void ActivarCerrar()
    {
        btnCerrar.IsEnabled = true;
    }

    public void Clear()
    {
        txtInner.Clear();
    }

    public void DesactivarCerrar()
    {
        btnCerrar.IsEnabled = false;
    }

    #endregion  
}

kode kelas ConsoleView

static public class ConsoleView
{
    //DEFINICIONES DE LA CLASE
    #region DEFINICIONES DE LA CLASE
    static FrmConsole console;
    static Thread StatusThread;
    static bool isActive = false;
    #endregion

    //CONSTRUCTORES DE LA CLASE
    #region CONSTRUCTORES DE LA CLASE

    #endregion

    //PROPIEDADES
    #region PROPIEDADES

    #endregion

    //DELEGADOS
    #region DELEGADOS

    #endregion

    //METODOS Y FUNCIONES
    #region METODOS Y FUNCIONES

    public static void Show(string label)
    {
        if (isActive)
        {
            return;
        }

        isActive = true;
        //create the thread with its ThreadStart method
        StatusThread = new Thread(() =>
        {
            try
            {
                console = new FrmConsole(label);
                console.ShowDialog();
                //this call is needed so the thread remains open until the dispatcher is closed
                Dispatcher.Run();
            }
            catch (Exception)
            {
            }
        });

        //run the thread in STA mode to make it work correctly
        StatusThread.SetApartmentState(ApartmentState.STA);
        StatusThread.Priority = ThreadPriority.Normal;
        StatusThread.Start();

    }

    public static void Close()
    {
        isActive = false;
        if (console != null)
        {
            //need to use the dispatcher to call the Close method, because the window is created in another thread, and this method is called by the main thread
            console.Dispatcher.InvokeShutdown();
            console = null;
            StatusThread = null;
        }

        console = null;
    }

    public static void Release()
    {
        isActive = false;
        if (console != null)
        {
            console.Dispatcher.Invoke(console.ActivarCerrar);
        }

    }
    #endregion
}

Semoga hasil ini bermanfaat.

Emelias Alvarez
sumber
-17

Sejauh yang saya tahu, Console.WriteLine () hanya untuk aplikasi konsol. Saya pikir ini adalah masalah Anda.

GEOCHET
sumber
1
Saya tidak tahu tentang WPF, tetapi ini tentu saja tidak terjadi pada WinForms. Console.WriteLine berfungsi dengan baik di sana, tetapi, tentu saja, Anda tidak akan melihat konsol, Anda akan melihatnya di jendela keluaran Debugger dan jika Anda mendengarkan keluaran standar.
Jeff Yates
2
Anda dapat mengatur proyek ke aplikasi Konsol dan itu akan tetap berjalan sebagai aplikasi Windows tetapi juga akan memiliki konsol yang terlihat
Mark Cidade
Itu salah, proses pembuatan aplikasi non-konsol tidak memasang konsol dengan deffault. Anda dapat melakukan ini secara manual dengan memanggil fungsi AllocConsole () Win32 API sebelum panggilan apa pun ke Console.Write, kelas Konsol kemudian akan diinisialisasi untuk bekerja dengan jendela Konsol tersebut.
John Leidegren