如何在C#的Windows WPF应用程序中添加控制台输出

9
我希望能在我的WPF应用程序中添加一个额外的控制台窗口,用于记录实时信息。有什么想法吗?
答案: 在项目属性中使用控制台应用程序 对我来说很有效。谢谢。

“Additional”指的是您已经使用了内置控制台吗?Windows仅允许将进程附加到单个控制台,因此如果您需要多个控制台,则选项为(1)使用辅助进程或(2)创建一个外观类似于控制台的GUI窗口。 - Ben Voigt
1
如果某个答案对您有用,请不要编辑您的问题,而是将其标记为已接受的答案,并可能点赞 - yas4891
@yas4891 实际上没有人添加那个答案,那正是我自己在寻找的答案。他应该先发布答案,然后将其标记为答案。 - Daniel Armstrong
5个回答

5

不要这样做。

看看log4netNLog,可以将日志输出到文件。通过正确配置这些框架,您将获得更多的功能(不同的日志级别,自动时间戳,在每个记录行前自动添加类名)。

当您在进行此操作时,您可能还想实现自己的外观,以将使用的日志框架隐藏在代码的其余部分之后。这将使您能够轻松更改日志框架(如果需要的话)。


如果您想为程序同时提供控制台和GUI窗口,则可以通过将项目编译为控制台应用程序csc /target:exe)来实现此行为。但是请注意:这肯定会导致糟糕的可用性,因为没有用户会期望您的应用程序既有控制台又有GUI窗口。


1
你能不能至少礼貌地解释一下那个踩的原因? - yas4891
我们在工作中使用NLog,它非常有用,但根据OP的情况,可能不是最好的工具(显然我们无法推断)。 - Psytronic
1
将项目编译为控制台应用程序对我来说可行。非常感谢,兄弟们。 - Bayo Alen
.Net 似乎支持它。请看我的回答。 - Sam Hobbs
4
这样的回答太过愚蠢。问题中根本没有问如何记录到文件,这个答案与问题完全无关。你应该删除你的“不要这么做”的答案,别误解我,log4net 真的很棒(虽然我更喜欢 Serilog,因为它支持结构化日志记录),但它与这里的问题毫不相关。 - Daniel Armstrong

5
你可以调用 AttachConsole WIN API 函数,并使用 PInvoke 调用此函数:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);

const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;  // default value if not specifing a process ID

// Somewhere in main method
AttachConsole(ATTACH_PARENT_PROCESS);

我知道这不是针对这个问题的最佳答案,但当我试图使我的WPF应用程序从命令行和常规GUI中工作时,我偶然发现了这个。这就是我正在寻找的技巧。现在,当用户输入命令行开关时,我可以将信息输出到控制台窗口,并且当它从GUI运行时,它使用常规的Log4Net记录。谢谢! - Mike G

1
感谢以上的建议。以下是添加控制台窗口到WPF应用程序所需的所有步骤。我们修改了我们的WPF测试应用程序,以便在夜间测试过程中可以从命令行调用它。唯一的问题是,当应用程序从控制台运行时,在调用FreeConsole()后命令提示符不会立即写入控制台窗口,而我们的应用程序退出。FreeConsole()函数似乎缺少调用Flush()类似的功能来强制将命令提示符写入控制台窗口。我的推理是,控制台窗口的上/下箭头历史记录可用,并且控制台接受另一个命令,但是当下一个应用程序运行并写入控制台窗口时,缺失的命令提示符将与之一起被写入。
  1. In the project properties Application tab, leave the Output Type = Windows Application.
  2. Right click on App.xaml and choose Properties
  3. Set the Build Action = Page
  4. Open App.xaml.cs and modify the App class like below.

    public partial class App : Application
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AttachConsole(uint dwProcessId);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeConsole();
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetConsoleTitle(System.Text.StringBuilder sbTitle, int capacity);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern bool SetConsoleTitle(string sTitle);
    
        [STAThread]
        public static int Main(string[] args)
        {
            Boolean hasExceptionOccured = false;
    
            System.Text.StringBuilder sbTitle = new System.Text.StringBuilder();
    
            try
            {
                // If the user does not provide any parameters assume they want to run in GUI mode.
                if (0 == args.Length)
                {
                    var application = new App();
                    application.InitializeComponent();
                    application.Run();
                }
                else
                {
                    const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;  // Default value if not specifying a process ID.
    
                    // Attach to the console which launched this application.
                    AttachConsole(ATTACH_PARENT_PROCESS);
    
                    // Get the current title of the console window.
                    GetConsoleTitle(sbTitle, 64);
    
                    // Set the console title to the name and version of this application.
                    SetConsoleTitle(Global.thisProgramsName + " - v" + Global.thisProductVersion);
    
                    // Create a instance of your console class and call it’s Run() method.
                    var mainConsole = new ReportTester.MainConsole();
                    mainConsole.Run(args);                   
                }
            }
            catch (System.Exception ex)
            {
                System.Console.WriteLine(ex.Message);
                System.Console.WriteLine(ex.StackTrace);
                if (null != ex.InnerException)
                {
                    System.Console.WriteLine(ex.InnerException.Message);
                    System.Console.WriteLine(ex.InnerException.StackTrace);
                }
                hasExceptionOccured = true;
            }
            finally
            {
                // Since the console does not display the prompt when freed, we will provide one here.
                System.Console.Write(">");
    
                // Restore the console title.
                SetConsoleTitle(sbTitle.ToString());
    
                // Free the console.
                FreeConsole();
            }
    
            return (hasExceptionOccured ? 1 : 0);
        }
    }
    

我不明白这应该如何工作。ReportTester.MainConsole是什么样子?Run方法从哪里来? - dutop

1
如果你想让你的程序既有控制台窗口又有图形用户界面窗口,可以通过将项目编译为控制台应用程序来实现。只需转到项目属性并将输出类型更改为“控制台应用程序”。现在运行时,你将得到WPF窗口和一个控制台窗口。

1
要求不够明确。听起来似乎唯一的真正要求是能够重定向标准输出;似乎没有必要使用控制台窗口。
在一个空白(新的)WPF应用程序中,将以下内容添加到Loaded事件或其他事件中:
Stream StdoutStream = OpenStandardOutput();
StreamWriter Stdout = new StreamWriter(StdoutStream);
Stdout.WriteLine("Line one");
Stdout.WriteLine("Line two");
Stdout.WriteLine("Hello");
Stdout.WriteLine("Bye");
Stdout.Flush();
Stdout.Close();

然后在命令提示符中执行程序并将标准输出重定向到文件中。输出将在文件中。标准输入可以以相应的方式重定向。
这对于标准IO是我们无法控制的要求的情况非常有用。我们可以将GUI窗口与标准IO结合使用。

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