ไม่มีเอาต์พุตไปยังคอนโซลจากแอปพลิเคชัน 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.