Không có đầu ra cho bảng điều khiển từ một ứng dụng WPF?


112

Tôi đang sử dụng Console.WriteLine () từ một ứng dụng thử nghiệm WPF rất đơn giản, nhưng khi tôi thực thi ứng dụng từ dòng lệnh, tôi không thấy gì được ghi vào bảng điều khiển. Có ai biết điều gì có thể xảy ra ở đây không?

Tôi có thể tái tạo nó bằng cách tạo một ứng dụng WPF trong VS 2008 và chỉ cần thêm Console.WriteLine ("text") vào bất cứ đâu được thực thi. Có ý kiến ​​gì không?

Tất cả những gì tôi cần ngay bây giờ là một cái gì đó đơn giản như Console.WriteLine (). Tôi nhận ra rằng tôi có thể sử dụng log4net hoặc một số giải pháp ghi nhật ký khác, nhưng tôi thực sự không cần nhiều chức năng như vậy cho ứng dụng này.

Chỉnh sửa: Tôi nên nhớ rằng Console.WriteLine () dành cho các ứng dụng bảng điều khiển. Ồ, không có câu hỏi ngu ngốc, phải không? :-) Bây giờ tôi sẽ chỉ sử dụng System.Diagnostics.Trace.WriteLine () và DebugView.


Các bản sao có thể xảy ra ở đâyở đây (mới hơn, nhưng với một số câu trả lời thú vị bằng cách sử dụng AttachConsole từ Kernel32.dll )
Tối đa

1
@Max, những câu hỏi đó có thể là bản sao của câu hỏi này . Câu hỏi này đã được hỏi 2-4 năm trước khi một trong những câu hỏi bạn đăng.
Rob

Câu trả lời:


90

Bạn sẽ phải tạo một cửa sổ Console theo cách thủ công trước khi thực sự gọi bất kỳ phương thức Console.Write nào. Điều đó sẽ kích hoạt Bảng điều khiển hoạt động bình thường mà không cần thay đổi loại dự án (đối với ứng dụng WPF sẽ không hoạt động).

Dưới đây là một ví dụ mã nguồn hoàn chỉnh, về cách lớp ConsoleManager có thể trông như thế nào và cách nó có thể được sử dụng để bật / tắt Bảng điều khiển, độc lập với loại dự án.

Với lớp sau, bạn chỉ cần viết vào ConsoleManager.Show()đâu đó trước bất kỳ cuộc gọi nào tới 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
Có thể thử gọi AttachConsole (-1) trước và kiểm tra giá trị trả về của nó để đính kèm vào bảng điều khiển của tiến trình mẹ; nếu nó trả về false, hãy gọi AllocConsole. Tuy nhiên, ứng dụng vẫn 'trả về' đầu tiên và chỉ sau đó xuất ra bảng điều khiển, tôi sẽ đăng thêm nếu tôi tìm thấy giải pháp. Ngoài ra, nếu bạn đặt loại ứng dụng WPF thành Ứng dụng bảng điều khiển, sự cố sẽ biến mất nhưng bạn không thể tách bảng điều khiển mà nó không hiển thị trên màn hình trong thời gian ngắn khi chương trình được khởi động, vì vậy nó trông hơi khó xử (nhưng nếu bạn có thể sống với nó , nó hoạt động tốt).
Alex Paven

2
Eh, thực ra là không, tôi không nghĩ có thể có cả hai cách; một ứng dụng Console được đánh dấu là CUI trong tiêu đề PE của nó và do đó tự động hợp tác tốt với CMD. Mặt khác, một ứng dụng GUI trả lại quyền kiểm soát cho CMD ngay lập tức và ngay cả khi nó có thể gắn lại vào bảng điều khiển, việc đọc và ghi sẽ được xen kẽ với các đầu ra tiếp theo trong đường dẫn, điều này rõ ràng là rất tệ. Mặt khác, nếu bạn đánh dấu ứng dụng là ứng dụng Console, bạn chỉ phải sống với CMD hiển thị ngắn gọn khi khởi động ứng dụng; sau đó bạn có thể sử dụng FreeConsole tách và Đính kèm / Alloc sau vv
Alex Paven

1
Tại sao phải làm điều này khi câu trả lời từ Brian hoạt động tốt và dễ dàng hơn nhiều.
Wouter Janssens

2
Có thể rõ ràng, nhưng tôi thấy rằng Console.WriteLine vẫn không hoạt động bằng cách sử dụng kỹ thuật này khi trình gỡ lỗi Visual Studio được đính kèm. Khi tôi chạy ứng dụng bên ngoài VS, nó hoạt động rất hiệu quả. Cảm ơn.
boardy021

2
@Mark Vâng, nhưng nó không hoạt động ... Có một SetConsoleCtrlHandlerchức năng cho phép được thông báo khi CTRL_CLOSE_EVENTsự kiện xảy ra nhưng bạn không thể làm gì với nó, không có gì cho phép ứng dụng của bạn tiếp tục. Bạn sẽ ngừng hoạt động. Nếu bạn cảm thấy muốn hack, bạn có thể hoán đổi trình xử lý thông báo windows cho quy trình bảng điều khiển và chỉ cần thả thông báo WM_CLOSE, tôi chưa bao giờ thử điều này nhưng nó có thể hoạt động. Nó chỉ là một cửa sổ khác nhưng với điều đó đã nói, trừ khi bạn muốn giải trí với ý tưởng này, bạn có lẽ tốt hơn nên dành công sức để làm việc khác.
John Leidegren

129

Nhấp chuột phải vào dự án, tab "Thuộc tính", "Ứng dụng", thay đổi "Loại đầu ra" thành "Ứng dụng bảng điều khiển", và sau đó nó cũng sẽ có một bảng điều khiển.


2
Vấn đề duy nhất với điều đó là bạn sẽ có một cmd mở trong nền, nhưng nó hoạt động :).
ykatchou

5
Tuyệt vời, nhưng cửa sổ dòng lệnh sẽ được tạo khi ứng dụng không được thực thi từ cmd.exe (hai cửa sổ được tạo cho một ứng dụng). Nhưng đối với điều này cũng có giải pháp: bạn có thể ẩn cửa sổ cmd bằng ShowWindow (hWnd, 0). stackoverflow.com/a/10416180/1457197 . Sử dụng giải pháp này, bạn sẽ chỉ thấy văn bản trong bảng điều khiển khi ứng dụng WPF được thực thi từ dòng lệnh.
CoperNick

Lưu ý rằng bạn sẽ phải chuyển nó trở lại "Ứng dụng cửa sổ" trong khi làm việc trong Blend, vì nó chỉ hiển thị XAML (không có quyền truy cập vào Chế độ xem thiết kế) cho các loại "Ứng dụng bảng điều khiển". (tính Blend 2013)

1
Không đúng ans. Ẩn Windows chính. Chỉ cần giao diện điều khiển xuất hiện.
Yash

129

Bạn có thể dùng

Trace.WriteLine("text");

Điều này sẽ xuất ra cửa sổ "Đầu ra" trong Visual Studio (khi gỡ lỗi).

đảm bảo có cụm Chẩn đoán bao gồm:

using System.Diagnostics;

9
đây là câu trả lời tốt nhất, nhưng không có sự đánh giá cao nhất
kiltek

Tôi đồng ý - đây chính xác là những gì op đang yêu cầu. Giải pháp thay thế tuyệt vời cho Console.WriteLine () - giải pháp được đánh dấu là câu trả lời là một bài tập gọn gàng nhưng không hợp lý để đưa vào ứng dụng sản xuất.
nocarrier

4
Các ứng dụng PS dành cho Windows Store (Windows Runtime) tương đương với Trace.WriteLine là Debug.WriteLine ()
nocarrier

Đây là một giải pháp đơn giản, sạch sẽ, tuy nhiên không hiệu quả với tôi. Không hoạt động trong phương thức hạt giống của khung thực thể trong quá trình cập nhật cơ sở dữ liệu. Nếu không, hoạt động ở mọi nơi khác!
Charles W

Đây là giải pháp tốt nhất. Sẽ tốt hơn nếu câu trả lời cũng giải thích rằng Console.WriteLinekhông dành cho các ứng dụng WPF và nó chỉ dành cho các ứng dụng dòng lệnh.
Andrew Koster

12

Mặc dù John Leidegren tiếp tục bác bỏ ý tưởng này, nhưng Brian đã chính xác. Tôi vừa có nó hoạt động trong Visual Studio.

Để rõ ràng, ứng dụng WPF không tạo cửa sổ Console theo mặc định.

Bạn phải tạo một Ứng dụng WPF và sau đó thay đổi OutputType thành "Ứng dụng bảng điều khiển". Khi bạn chạy dự án, bạn sẽ thấy một cửa sổ bảng điều khiển với cửa sổ WPF của bạn ở phía trước nó.

Nó trông không đẹp lắm, nhưng tôi thấy nó hữu ích vì tôi muốn ứng dụng của mình được chạy từ dòng lệnh với phản hồi trong đó, và sau đó đối với một số tùy chọn lệnh nhất định, tôi sẽ hiển thị cửa sổ WPF.


1
Hoàn hảo. Làm công việc.
frostymarvelous

10

Có thể thấy đầu ra dành cho bảng điều khiển bằng cách sử dụng chuyển hướng dòng lệnh .

Ví dụ:

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

sẽ ghi tất cả nội dung vào output.txttệp.


Câu trả lời hay nhất vì nó đơn giản và không yêu cầu thay đổi nguồn
buckley

9

Bài cũ, nhưng tôi đã gặp phải vấn đề này, vì vậy nếu bạn đang cố gắng xuất một thứ gì đó ra Output trong một dự án WPF trong Visual Studio, thì phương pháp hiện đại là:

Bao gồm cái này:

using System.Diagnostics;

Và sau đó:

Debug.WriteLine("something");

4

Tôi sử dụng Console.WriteLine () để sử dụng trong cửa sổ Đầu ra ...


4
Đó là một câu hỏi 4 năm tuổi đã được chỉnh sửa rất nhiều kể từ lần đầu tiên tôi nhìn thấy nó. Tất nhiên bây giờ câu hỏi đã được diễn đạt tốt hơn và câu trả lời của tôi được đưa ra là không liên quan.
erodewald

1

Tôi đã tạo ra một giải pháp, trộn thông tin của bài đăng varius.

Đây là một biểu mẫu, chứa một nhãn và một hộp văn bản. Đầu ra bảng điều khiển được chuyển hướng đến hộp văn bản.

Có quá một lớp gọi là ConsoleView triển khai ba phương thức public: Show (), Close () và Release (). Cuối cùng là để mở bảng điều khiển và kích hoạt nút Đóng để xem kết quả.

Các biểu mẫu được gọi là FrmConsole. Đây là XAML và mã c #.

Việc sử dụng rất đơn giản:

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

Để mở bảng điều khiển. Sử dụng:

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

Để xuất văn bản ra bảng điều khiển.

Sử dụng:

ConsoleView.Close();

Đối với Đóng bảng điều khiển.

ConsoleView.Release();

Để mở bảng điều khiển và bật nút Đóng

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>

Mã của Window:

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  
}

mã của lớp 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
}

Tôi hy vọng kết quả này hữu ích.



-17

Theo như tôi biết, Console.WriteLine () chỉ dành cho các ứng dụng console. Tôi nghĩ đây là vấn đề của bạn.


1
Tôi không biết về WPF, nhưng đây chắc chắn không phải là trường hợp của WinForms. Console.WriteLine hoạt động tốt ở đó, nhưng tất nhiên, bạn sẽ không nhìn thấy bảng điều khiển, bạn sẽ thấy nó trong cửa sổ đầu ra của Trình gỡ lỗi và nếu bạn nghe đầu ra chuẩn.
Jeff Yates

2
bạn có thể đặt dự án thành ứng dụng Bảng điều khiển và nó sẽ vẫn chạy dưới dạng ứng dụng Windows nhưng nó cũng sẽ có bảng điều khiển hiển thị
Mark Cidade 8/10/08

Điều đó không chính xác, quá trình xây dựng của một ứng dụng không phải bảng điều khiển không đính kèm một bảng điều khiển bằng deffault. Bạn có thể thực hiện việc này theo cách thủ công bằng cách gọi hàm API AllocConsole () Win32 trước bất kỳ lệnh gọi nào đến Console.Write, lớp Console sau đó sẽ được khởi tạo để hoạt động với cửa sổ Console đó.
John Leidegren
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.