WPF应用程序没有输出到控制台?

164

我正在使用非常简单的WPF测试应用程序,其中包含Console.WriteLine(),但是当我从命令行执行该应用程序时,控制台上没有任何输出。有人知道这可能是怎么回事吗?

我可以通过在VS 2008中创建WPF应用程序,并在任何执行它的地方添加Console.WriteLine("text")来重现它。有什么想法吗?

现在我所需要的就是像Console.WriteLine()一样简单的东西。我意识到我可以使用log4net或其他日志记录解决方案,但是对于这个应用程序,我真的不需要那么多功能。

编辑:我应该记得Console.WriteLine()是用于控制台应用程序的。无论如何,没有愚蠢的问题,对吧?:-) 现在我将使用System.Diagnostics.Trace.WriteLine()和DebugView。


可能有重复的问题在这里在这里(较新,但使用了一些有趣的答案,使用了Kernel32.dll中的AttachConsole)。 - Max
3
@Max,那些问题可能是与问题重复。这个问题是在你发布那两个问题之前2-4年提出的。 - Rob
这绝不是一个愚蠢的问题,@Rob。多年来,我看到Debug和Console的角色发生了多次变化。 - Hey Mikey
9个回答

221
可以使用

Trace.WriteLine("text");

当在Visual Studio(调试时)时,这将输出到“输出”窗口。

请确保已包含诊断程序集:

using System.Diagnostics;

10
这是最佳答案,但评分不是最高的。 - kiltek
我同意 - 这正是 OP 所要求的。这是 Console.WriteLine() 的绝佳替代方案 - 被标记为答案的解决方案是一个不错的练习,但在生产应用中包含它是不合理的。 - Shawn J. Molloy
5
对于Windows Store应用(Windows Runtime),Trace.WriteLine的相当于是Debug.WriteLine()。 - Shawn J. Molloy
这是一个简单、干净的解决方案,但对我没有用。在更新数据库期间,实体框架的种子方法中没有起作用。否则,在其他地方都可以使用! - Charles W
1
这是最好的解决方案。如果答案还解释了 Console.WriteLine 根本不适用于 WPF 应用程序,只适用于命令行应用程序,那就更好了。 - Andrew Koster
显示剩余2条评论

155

右键点击项目,选择“属性”,切换到“应用程序”选项卡,将“输出类型”更改为“控制台应用程序”,这样它就也会有一个控制台,即使将应用程序的输出类型切换为“控制台应用程序”,WPF应用程序仍然按预期运行。


3
唯一的问题是您将在后台打开一个命令提示符,但它可以正常工作 :)。 - ykatchou
5
很好,但当应用程序不是从cmd.exe中执行时,将创建命令行窗口(一个应用程序会创建两个窗口)。但是针对这个问题也有解决方案:您可以通过ShowWindow(hWnd, 0)隐藏cmd窗口。https://dev59.com/xmkv5IYBdhLWcg3wsC3i#10416180。使用此解决方案,只有在从命令行执行WPF应用程序时才能在控制台中看到文本。 - CoperNick
请注意,在 Blend 中工作时,您必须将其切换回“Windows 应用程序”,因为对于“控制台应用程序”类型,它只显示 XAML(无法访问设计视图)。 (截至 Blend 2013) - user600838
3
答案不正确,隐藏了主窗口,只有控制台弹出。 - Yash
2
对于不使用Visual Studio的人,请在您的csproj文件中设置<OutputType>Exe</OutputType>而不是<OutputType>WinExe</OutputType>(请参见此处https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-properties?view=vs-2022)。 - sonrad10
显示剩余2条评论

102

在调用任何Console.Write方法之前,您需要手动创建一个控制台窗口。这将使控制台在不更改项目类型的情况下正常工作(对于WPF应用程序而言无效)。

以下是一个完整的源代码示例,演示了ConsoleManager类可能的外观,以及如何使用它来独立于项目类型启用/禁用控制台。

使用以下类,您只需要在任何调用Console.Write之前的某个地方写入ConsoleManager.Show()...

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

9
尝试先调用AttachConsole(-1)函数并检查其返回值以连接到父进程的控制台是可行的;如果返回false,则调用AllocConsole。但是,应用程序仍然会首先“返回”,然后才输出到控制台,如果我找到解决方案,我会发布更多信息。此外,如果将WPF应用程序类型设置为控制台应用程序,则问题消失了,但是不能在没有屏幕短暂显示控制台的情况下分离控制台,因此它看起来有点尴尬(但是如果您可以接受,它运作得很好)。 - Alex Paven
4
抱歉,实际上不可能两全其美;控制台应用程序在其PE头中标记为CUI,因此可以自动与CMD协作良好。另一方面,GUI应用程序会立即返回控制权给CMD,即使它可以重新连接到控制台,读写操作也会与管道中的下一个输出混合在一起,这显然非常糟糕。如果你将应用程序标记为控制台应用程序,则只需要忍受CMD在应用程序启动时短暂显示。之后,你可以使用FreeConsole进行分离,然后使用Attach/Alloc等方法进行连接。 - Alex Paven
1
为什么要这样做,当Brian的回答同样有效且更容易呢? - Wouter Janssens
2
可能很明显,但我发现当使用Visual Studio调试器时,Console.WriteLine仍然无法使用这种技术。当我在VS之外运行应用程序时,它非常有效。谢谢。 - aboy021
2
@Mark 是的,但它不起作用...有一个SetConsoleCtrlHandler函数可以在发生CTRL_CLOSE_EVENT事件时通知您,但您无法对其进行任何操作,没有任何东西允许您的应用程序继续运行。你将被关闭。如果你想黑客,你可能可以交换控制台进程的窗口消息处理程序并且只是删除WM_CLOSE消息,我从未尝试过这个,但它可能会起作用。它只是另一个窗口,但话虽如此,除非你想考虑这个想法,否则你的努力可能更好地花在其他事情上。 - John Leidegren
显示剩余10条评论

29

虽然这篇文章已经有些年头了,但如果你正在试图在Visual Studio的WPF项目中输出一些内容到“Output”,那么现代方法是:

请包含以下内容:

using System.Diagnostics;

然后:

Debug.WriteLine("something");

15

尽管John Leidegren一直否定这个想法,但Brian是正确的。我刚在Visual Studio中让它工作了。

明确一下,默认情况下,WPF应用程序不会创建控制台窗口。

您必须创建一个WPF应用程序,然后将OutputType更改为“控制台应用程序”。运行项目时,您将看到一个控制台窗口和您的WPF窗口在其前面。

它看起来不太漂亮,但我发现它很有帮助,因为我希望我的应用程序可以从命令行运行,并在其中提供反馈,然后对于某些命令选项,我会显示WPF窗口。


1
完美。完成工作。 - frostymarvelous

14

通过使用命令行重定向,可以查看输出到控制台的内容。

例如:

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

将所有内容写入到 output.txt 文件中。


最佳答案是它很简单,不需要更改源代码。 - buckley
1
当然,下一步是在Notepad++中打开文件并选择“查看->监视(tail -f)”,以实时观察其更新。 - mcalex

4

Brian的解决方案是在WPF应用程序启动时始终打开控制台。如果您想要动态地启用控制台输出(例如,仅在使用某些命令行参数启动时),请调用AttachConsole

[DllImport("kernel32.dll")]
static extern bool AttachConsole(uint dwProcessId);

const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;

接下来,当您想要开始向控制台编写内容时:

AttachConsole(ATTACH_PARENT_PROCESS);
Console.WriteLine("Hello world!");
Console.WriteLine("Writing to the hosting console!");

1
这个解决方案似乎可行,但如果你写类似于“yourapp.exe > result.txt”的内容,你会发现输出并没有重定向到文件中。 - Leo Li
我无法弄清楚在哪里放置这段代码。我尝试的每个地方都给了我一个错误。 - john k
@johnk 第一部分应该放在类内,不属于任何函数。第二部分应该在你想写入控制台的函数中。 - Bip901

4

我使用Console.WriteLine()方法将内容输出到控制台窗口...


5
这是一个四年前的问题,自从我第一次看到它以来已经被大量编辑过。现在,当然问题的措辞更好了,我的回答已经没用了。 - erodewald

2
我已经创建了一个解决方案,混合了各种帖子的信息。
这是一个表单,包含一个标签和一个文本框。控制台输出被重定向到文本框中。
还有一个叫做ConsoleView的类,实现了三个公共方法: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
}

我希望这个结果对你有帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接