ไม่มีเอาต์พุตไปยังคอนโซลจากแอปพลิเคชัน WPF?


114

ฉันใช้ Console.WriteLine () จากแอปพลิเคชันทดสอบ WPF ที่เรียบง่าย แต่เมื่อฉันเรียกใช้แอปพลิเคชันจากบรรทัดคำสั่งฉันไม่เห็นสิ่งใดถูกเขียนลงในคอนโซล ไม่มีใครรู้ว่าเกิดอะไรขึ้นที่นี่?

ฉันสามารถสร้างซ้ำได้โดยสร้างแอปพลิเคชัน WPF ใน VS 2008 และเพิ่ม Console.WriteLine ("text") ที่ใดก็ได้ที่ดำเนินการ ความคิดใด ๆ ?

สิ่งที่ฉันต้องการในตอนนี้มีเพียงแค่ Console.WriteLine () ฉันรู้ว่าฉันสามารถใช้ log4net หรือโซลูชันการบันทึกอื่น ๆ ได้ แต่ฉันไม่ต้องการฟังก์ชันมากขนาดนั้นสำหรับแอปพลิเคชันนี้

แก้ไข:ฉันควรจำไว้ว่า Console.WriteLine () ใช้สำหรับแอปพลิเคชันคอนโซล ดีไม่มีคำถามโง่ ๆ ใช่มั้ย? :-) ฉันจะใช้ System.Diagnostics.Trace.WriteLine () และ DebugView ในตอนนี้


อาจซ้ำกันได้ที่นี่และที่นี่ (ใหม่กว่า แต่มีคำตอบที่น่าสนใจโดยใช้AttachConsole จาก Kernel32.dll )
— สูงสุด

1
@ แม็กซ์คำถามเหล่านั้นอาจซ้ำกับคำถามนี้ได้ คำถามนี้ถูกถาม 2-4 ปีก่อนที่คุณจะโพสต์คำถามใด ๆ
— Rob

คำตอบ:


91

คุณจะต้องสร้างหน้าต่างคอนโซลด้วยตนเองก่อนจึงจะเรียกใช้ Console ได้จริงเขียนวิธีการ ซึ่งจะทำให้คอนโซลทำงานได้อย่างถูกต้องโดยไม่ต้องเปลี่ยนประเภทโครงการ (ซึ่งสำหรับแอปพลิเคชัน WPF จะไม่ทำงาน)

นี่คือตัวอย่างซอร์สโค้ดที่สมบูรณ์เกี่ยวกับลักษณะของคลาส ConsoleManager และวิธีการใช้เพื่อเปิด / ปิดคอนโซลโดยไม่ขึ้นอยู่กับประเภทโครงการ

ในชั้นเรียนต่อไปนี้คุณต้องเขียนConsoleManager.Show()ที่ไหนสักแห่งก่อนที่จะโทรไป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);
    }
} 

5
เป็นไปได้ที่จะลองเรียก AttachConsole (-1) ก่อนและตรวจสอบค่าที่ส่งคืนเพื่อแนบไปกับคอนโซลของกระบวนการหลัก ถ้าส่งคืนเท็จให้เรียก AllocConsole อย่างไรก็ตามแอปพลิเคชันยังคง 'ส่งคืน' ก่อนแล้วจึงส่งออกไปยังคอนโซลฉันจะโพสต์เพิ่มเติมหากพบวิธีแก้ไข นอกจากนี้หากคุณตั้งค่าประเภทแอป WPF เป็นแอปพลิเคชันคอนโซลปัญหาจะหายไป แต่คุณไม่สามารถถอดคอนโซลได้โดยไม่แสดงบนหน้าจอสั้น ๆ เมื่อโปรแกรมเริ่มทำงานดังนั้นมันจึงดูอึดอัด (แต่ถ้าคุณสามารถอยู่กับมันได้ มันใช้งานได้ดี)
— Alex Paven

2
เอ๊ะไม่จริงฉันไม่คิดว่ามันจะเป็นไปได้ทั้งสองทาง แอปพลิเคชันคอนโซลถูกทำเครื่องหมายเป็น CUI ในส่วนหัว PE ดังนั้นจึงทำงานร่วมกันได้ดีกับ CMD โดยอัตโนมัติ ในทางกลับกันแอปพลิเคชัน GUI จะส่งคืนการควบคุมไปยัง CMD ทันทีและแม้ว่าจะสามารถเชื่อมต่อกับคอนโซลได้อีกครั้ง แต่การอ่านและการเขียนจะถูกรวมเข้ากับเอาต์พุตถัดไปในไปป์ไลน์ซึ่งเห็นได้ชัดว่าแย่มาก หากในทางกลับกันคุณทำเครื่องหมายแอปพลิเคชันเป็นแอปพลิเคชันคอนโซลคุณจะต้องใช้งาน CMD แสดงสั้น ๆ เมื่อเริ่มต้นแอป จากนั้นคุณสามารถใช้ FreeConsole เพื่อถอดและแนบ / Alloc ในภายหลังเป็นต้น
— Alex Paven

1
ทำไมต้องทำเช่นนี้เมื่อคำตอบจาก Brian ทำงานได้ดีและง่ายกว่ามาก
— Wouter Janssens

2
อาจจะชัดเจน แต่ฉันพบว่า Console.WriteLine ยังไม่สามารถใช้งานได้โดยใช้เทคนิคนี้เมื่อแนบดีบักเกอร์ Visual Studio เมื่อฉันเรียกใช้แอปนอก VS มันทำงานได้ดี ขอบคุณ.
— aboy021

2
@Mark Yeah แต่มันใช้ไม่ได้ ... มีSetConsoleCtrlHandlerฟังก์ชั่นที่ช่วยให้รับการแจ้งเตือนเมื่อมีCTRL_CLOSE_EVENTเหตุการณ์เกิดขึ้น แต่คุณไม่สามารถทำอะไรได้เลยไม่มีสิ่งใดที่ทำให้แอปพลิเคชันของคุณทำงานต่อได้ คุณจะถูกปิด หากคุณรู้สึกว่าถูกแฮ็กคุณสามารถสลับตัวจัดการข้อความของ windows สำหรับกระบวนการคอนโซลได้และเพียงแค่วางข้อความ WM_CLOSE ฉันไม่เคยลอง แต่ก็สามารถใช้งานได้ เป็นเพียงหน้าต่างอีกบานหนึ่ง แต่จากที่กล่าวมาเว้นแต่คุณต้องการสร้างความบันเทิงให้กับแนวคิดนี้คุณอาจใช้ความพยายามในการทำอย่างอื่นได้ดีกว่า
— John Leidegren

131

คุณสามารถใช้ได้

Trace.WriteLine("text");

สิ่งนี้จะส่งออกไปยังหน้าต่าง "เอาต์พุต" ใน Visual Studio (เมื่อทำการดีบัก)

ตรวจสอบให้แน่ใจว่ามีชุดประกอบการวินิจฉัยรวมอยู่ด้วย:

using System.Diagnostics;

9
นี่คือคำตอบที่ดีที่สุด แต่ยังไม่ได้คะแนนสูงสุด
— kiltek

ฉันเห็นด้วย - นี่คือสิ่งที่ op ขอ ทางเลือกที่ดีสำหรับ Console.WriteLine () - วิธีแก้ปัญหาที่ทำเครื่องหมายว่าเป็นคำตอบคือแบบฝึกหัดที่เรียบร้อย แต่ไม่มีเหตุผลที่จะรวมไว้ในแอปพลิเคชันการผลิต
— nocarrier

4
PS สำหรับแอป Windows Store (Windows Runtime) เทียบเท่ากับ Trace.WriteLine คือ Debug.WriteLine ()
— nocarrier

นี่เป็นวิธีแก้ปัญหาที่เรียบง่าย แต่ไม่ได้ผลสำหรับฉัน ไม่ทำงานในวิธีการ seed ของเอนทิตีเฟรมเวิร์กระหว่างการอัพเดตฐานข้อมูล มิฉะนั้นจะทำงานได้ทุกที่!
— Charles W.

นี่คือทางออกที่ดีที่สุด จะดีกว่าถ้าคำตอบอธิบายConsole.WriteLineว่าไม่ได้มีไว้สำหรับแอปพลิเคชัน WPF เลยและมีไว้สำหรับแอพบรรทัดคำสั่งเท่านั้น
— Andrew Koster

129

คลิกขวาที่โปรเจ็กต์แท็บ "Properties" "Application" เปลี่ยน "Output Type" เป็น "Console Application" จากนั้นก็จะมีคอนโซลด้วย


3
ปัญหาเดียวคือคุณจะมี cmd เปิดอยู่เบื้องหลัง แต่ใช้งานได้ :)
— ykatchou

5
เยี่ยมมาก แต่หน้าต่างบรรทัดคำสั่งจะถูกสร้างขึ้นเมื่อไม่ได้เรียกใช้แอปพลิเคชันจาก cmd.exe (สร้างสองหน้าต่างสำหรับแอปพลิเคชันเดียว) แต่สำหรับสิ่งนี้ยังมีวิธีแก้ไข: คุณสามารถซ่อนหน้าต่าง cmd โดย ShowWindow (hWnd, 0) stackoverflow.com/a/10416180/1457197 การใช้โซลูชันนี้คุณจะเห็นข้อความในคอนโซลเฉพาะเมื่อแอปพลิเคชัน WPF ทำงานจากบรรทัดคำสั่ง
— CoperNick

โปรดทราบว่าคุณจะต้องเปลี่ยนกลับเป็น "Window Application" ในขณะที่ทำงานใน Blend เนื่องจากจะแสดงเฉพาะ XAML (ไม่มีสิทธิ์เข้าถึง Design View) สำหรับประเภท "Console Application" (ณ Blend 2013)

2
ans ไม่ถูกต้อง ซ่อน Windows หลัก เพียงแค่คอนโซลขึ้นมา
— Yash

ไม่ได้ทำงานกับ WPF ไม่มีหน้าต่างหลัก
— Alp

12

แม้ว่า John Leidegren จะยังคงทำลายความคิด แต่ Brian ก็ถูกต้อง ฉันเพิ่งใช้งานได้ใน Visual Studio

เพื่อความชัดเจนแอปพลิเคชัน WPF ไม่ได้สร้างหน้าต่างคอนโซลตามค่าเริ่มต้น

คุณต้องสร้างแอปพลิเคชัน WPF จากนั้นเปลี่ยน OutputType เป็น "Console Application" เมื่อคุณเรียกใช้โปรเจ็กต์คุณจะเห็นหน้าต่างคอนโซลพร้อมหน้าต่าง WPF อยู่ข้างหน้า

มันดูไม่สวยมาก แต่ฉันพบว่ามันมีประโยชน์เพราะฉันต้องการให้แอพของฉันทำงานจากบรรทัดคำสั่งพร้อมคำติชมในนั้นจากนั้นสำหรับตัวเลือกคำสั่งบางอย่างฉันจะแสดงหน้าต่าง WPF


1
สมบูรณ์แบบ. ทำงาน
— หนาวจัด

10

เป็นไปได้ที่จะเห็นเอาต์พุตสำหรับคอนโซลโดยใช้การเปลี่ยนเส้นทางบรรทัดคำสั่งการเปลี่ยนเส้นทางบรรทัดคำสั่ง

ตัวอย่างเช่น:

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

จะเขียนเนื้อหาทั้งหมดลงในoutput.txtไฟล์


คำตอบที่ดีที่สุดเพราะง่ายและไม่ต้องการการเปลี่ยนแปลงที่มา
— buckley

9

โพสต์เก่า แต่ฉันพบสิ่งนี้ดังนั้นหากคุณพยายามส่งออกบางสิ่งไปยัง Output ในโครงการ WPF ใน Visual Studio วิธีการร่วมสมัยคือ:

รวมสิ่งนี้:

using System.Diagnostics;

แล้ว:

Debug.WriteLine("something");

4

ฉันใช้ Console.WriteLine () เพื่อใช้ในหน้าต่างผลลัพธ์ ...


4
เป็นคำถามอายุ 4 ปีที่ได้รับการแก้ไขอย่างหนักตั้งแต่ฉันเห็นครั้งแรก แน่นอนว่าคำถามนี้มีการใช้คำที่ดีขึ้นและคำตอบของฉันก็ไม่ตรงประเด็น
— erodewald

1

ฉันสร้างโซลูชันผสมข้อมูลของ varius post

เป็นแบบฟอร์มที่มีป้ายกำกับและกล่องข้อความหนึ่งช่อง เอาต์พุตคอนโซลถูกเปลี่ยนเส้นทางไปยังกล่องข้อความ

มีคลาสที่เรียกว่า ConsoleView มากเกินไปที่ใช้เมธอด publics สามวิธี ได้แก่ Show (), Close () และ Release () สุดท้ายคือการปล่อยให้เปิดคอนโซลและเปิดใช้งานปุ่มปิดเพื่อดูผลลัพธ์

แบบฟอร์มนี้เรียกว่า FrmConsole นี่คือ XAML และรหัส c #

การใช้งานนั้นง่ายมาก:

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

สำหรับเปิดคอนโซล ใช้:

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

สำหรับข้อความที่ส่งออกไปยังคอนโซล

ใช้:

ConsoleView.Close();

สำหรับปิดคอนโซล

ConsoleView.Release();

เปิดคอนโซลและเปิดใช้งานปุ่มปิด

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>

รหัสของหน้าต่าง:

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  
}

รหัสของคลาส 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
}

ฉันหวังว่าผลลัพธ์นี้จะเป็นประโยชน์



-17

เท่าที่ฉันรู้ Console.WriteLine () ใช้สำหรับแอปพลิเคชันคอนโซลเท่านั้น ฉันคิดว่านี่เป็นปัญหาของคุณ


1
ฉันไม่รู้เกี่ยวกับ WPF แต่นี่ไม่ใช่กรณีของ WinForms Console.WriteLine ทำงานได้ดีที่นั่น แต่แน่นอนว่าคุณจะไม่เห็นคอนโซลคุณจะเห็นในหน้าต่างเอาต์พุตดีบักเกอร์และหากคุณฟังเอาต์พุตมาตรฐาน
— Jeff Yates

2
คุณสามารถตั้งค่าโครงการเป็นแอปพลิเคชันคอนโซลและจะยังคงทำงานเป็นแอป Windows แต่จะมีคอนโซลที่มองเห็นได้ด้วย
— Mark Cidade

ไม่ถูกต้องกระบวนการสร้างของแอปพลิเคชันที่ไม่ใช่คอนโซลไม่ได้แนบคอนโซลโดย deffault คุณสามารถทำได้ด้วยตนเองโดยเรียกใช้ฟังก์ชัน AllocConsole () Win32 API ก่อนที่จะเรียกไปยัง Console.Write คลาส Console จะเริ่มต้นเพื่อทำงานกับหน้าต่างคอนโซลนั้น
— John Leidegren
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.