如何在表单应用程序中显示控制台输出/窗口?
Posted
技术标签:
【中文标题】如何在表单应用程序中显示控制台输出/窗口?【英文标题】:How do I show a console output/window in a forms application? 【发布时间】:2011-05-20 17:06:14 【问题描述】:为了直接陷入困境,一个非常基本的例子:
using System;
using System.Windows.Forms;
class test
static void Main()
Console.WriteLine("test");
MessageBox.Show("test");
如果我使用默认选项(在命令行使用 csc)编译它,如预期的那样,它将编译为控制台应用程序。另外,因为我导入了System.Windows.Forms
,所以也会显示一个消息框。
现在,如果我使用选项 /target:winexe
,我认为这与从项目选项中选择 Windows Application
相同,正如预期的那样,我只会看到消息框而没有控制台输出。
(事实上,从命令行启动它的那一刻,我可以在应用程序完成之前发出下一个命令。
所以,我的问题是 - 我知道您可以从控制台应用程序中获得“windows”/forms 输出,但是无论如何可以从 Windows 应用程序中显示控制台?
【问题讨论】:
您认为两者之间的区别是什么?为什么不直接编译为控制台并显示一个表单。 @Doggett,简单 - 我正在学习并想了解为什么/如何去做,即使我最终没有在实际应用程序中使用它......目前,我在想提供额外命令/输出的选项,例如在 VLC 中,但是 TBH,我不需要它 - 再次,只是学习并想理解它! 我使用本教程完成了这一点:saezndaree.wordpress.com/2009/03/29/… 【参考方案1】:这个应该可以的。
using System.Runtime.InteropServices;
private void Form1_Load(object sender, EventArgs e)
AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
【讨论】:
太棒了,这个问题似乎被问了很多,这是我能找到的问题的唯一实际答案,+1 主要问题:当你关闭它时,所有应用程序都会关闭。 我在 Windows 8 和 Windows 10 上进行了测试: - AttachConsole 在 cmd 框中工作 - AllocConsole 在 Visual Studio 中工作。当需要 alloc 时,AttachConsole 返回 false。您还应该在以控制台模式终止应用程序之前调用 FreeConsole()。在我的程序中,我使用了 Matthew Strawbridge 的代码(见下文),将 AttachConsole() 行修改为: if (!AttachConsole(-1)) AllocConsole(); 这不是很好,当从命令行运行时它会弹出一个 separate 控制台窗口,当从命令行运行并尝试使用>
重定向时输出我得到一个单独的控制台窗口和我的文件中的零输出。
如何在这个控制台打印消息? Console.Writeline() 不在控制台中打印任何内容。【参考方案2】:
也许这太简单了……
创建一个 Windows 窗体项目...
然后:项目属性 -> 应用程序 -> 输出类型 -> 控制台应用程序
然后可以让控制台和表单一起运行,对我有用
【讨论】:
看起来最简单,也解决了我的问题。 这绝对是最好的解决方案!其他人很聪明,但方式很复杂 简单且工作正常。这应该是公认的答案。 虽然,是的,从技术上讲,这可用于允许发布者要求的内容 - 这不是一个很好的解决方案。通过这样做,如果您随后使用 GUI 启动您的 winforms 应用程序 - 您还将打开一个控制台窗口。在这种情况下,您需要更像 Mike de Klerk 的答案。 这是我能够让我的 Winforms 应用程序在从命令行运行时将输出写入控制台,或者在使用>
在命令行上重定向时写入文件的唯一解决方案.但是,我希望有一个解决方案可以解释如何仅在某些时候作为“控制台应用程序”运行(即以编程方式启用更改这个神秘的 Visual Studio 设置所做的任何事情)。有谁知道这在幕后是如何工作的?【参考方案3】:
如果您不担心打开控制台命令,您可以进入项目的属性并将其更改为控制台应用程序
。
这仍然会显示您的表单以及弹出一个控制台窗口。您无法关闭控制台窗口,但它可以作为一个出色的调试临时记录器。
只需记住在部署程序之前将其关闭即可。
【讨论】:
不错。这解决了我的表单应用程序的问题,即我需要能够输出到控制台窗口,同时支持将输出重定向到文件。而且我不需要手动附加任何控制台... @JasonHarrison 如果关闭控制台窗口,程序将关闭。此外,程序运行时窗口始终处于打开状态。 @gun2171:谢谢。答案中指出了这种方法的缺点:如果通过双击、开始菜单等启动应用程序,则会出现控制台窗口。 有什么方法可以跟踪控制台关闭事件? 这不适用于 .Net Core 5。我只测试了 .Net Core 的单一版本。除了不显示控制台窗口之外,它甚至导致 Visual Studio 2019 在我保存它并尝试打开表单设计选项卡后完全冻结。【参考方案4】:您可以使用 pinvoke 调用 AttachConsole
以获取附加到 WinForms 项目的控制台窗口:http://www.csharp411.com/console-output-from-winforms-application/
您可能还需要考虑使用 Log4net (http://logging.apache.org/log4net/index.html) 在不同配置中配置日志输出。
【讨论】:
+1 - 哇,我希望有一个 console.show 或类似的!比我想象的要复杂得多!我会暂时保持开放,以防万一有更好/更简单的答案。 这对我有用,AllocConsole() 没有,因为它产生了一个新的控制台窗口(没有深入研究 AllocConsole,也许我错过了那里的东西)。【参考方案5】:创建一个 Windows 窗体应用程序,并将输出类型更改为控制台。
这将导致控制台和表单都打开。
【讨论】:
这正是我想要的。简单且不使用 WINAPI。 我尝试了很多例子,但没有一个产生符合我期望的结果。然而,这个解决方案正是我想要的,也是迄今为止最简单的解决方案。【参考方案6】:这对我有用,可以将输出通过管道传输到文件。 使用
调用控制台cmd /c "C:\path\to\your\application.exe" > myfile.txt
将此代码添加到您的应用程序中。
[DllImport("kernel32.dll")]
static extern bool AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool GetFileInformationByHandle(
SafeFileHandle hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[DllImport("kernel32.dll")]
private static extern SafeFileHandle GetStdHandle(UInt32 nStdHandle);
[DllImport("kernel32.dll")]
private static extern bool SetStdHandle(UInt32 nStdHandle, SafeFileHandle hHandle);
[DllImport("kernel32.dll")]
private static extern bool DuplicateHandle(
IntPtr hSourceProcessHandle,
SafeFileHandle hSourceHandle,
IntPtr hTargetProcessHandle,
out SafeFileHandle lpTargetHandle,
UInt32 dwDesiredAccess,
Boolean bInheritHandle,
UInt32 dwOptions
);
private const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;
private const UInt32 STD_OUTPUT_HANDLE = 0xFFFFFFF5;
private const UInt32 STD_ERROR_HANDLE = 0xFFFFFFF4;
private const UInt32 DUPLICATE_SAME_ACCESS = 2;
struct BY_HANDLE_FILE_INFORMATION
public UInt32 FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public UInt32 VolumeSerialNumber;
public UInt32 FileSizeHigh;
public UInt32 FileSizeLow;
public UInt32 NumberOfLinks;
public UInt32 FileIndexHigh;
public UInt32 FileIndexLow;
static void InitConsoleHandles()
SafeFileHandle hStdOut, hStdErr, hStdOutDup, hStdErrDup;
BY_HANDLE_FILE_INFORMATION bhfi;
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
hStdErr = GetStdHandle(STD_ERROR_HANDLE);
// Get current process handle
IntPtr hProcess = Process.GetCurrentProcess().Handle;
// Duplicate Stdout handle to save initial value
DuplicateHandle(hProcess, hStdOut, hProcess, out hStdOutDup,
0, true, DUPLICATE_SAME_ACCESS);
// Duplicate Stderr handle to save initial value
DuplicateHandle(hProcess, hStdErr, hProcess, out hStdErrDup,
0, true, DUPLICATE_SAME_ACCESS);
// Attach to console window – this may modify the standard handles
AttachConsole(ATTACH_PARENT_PROCESS);
// Adjust the standard handles
if (GetFileInformationByHandle(GetStdHandle(STD_OUTPUT_HANDLE), out bhfi))
SetStdHandle(STD_OUTPUT_HANDLE, hStdOutDup);
else
SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
if (GetFileInformationByHandle(GetStdHandle(STD_ERROR_HANDLE), out bhfi))
SetStdHandle(STD_ERROR_HANDLE, hStdErrDup);
else
SetStdHandle(STD_ERROR_HANDLE, hStdErr);
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
// initialize console handles
InitConsoleHandles();
if (args.Length != 0)
if (args[0].Equals("waitfordebugger"))
MessageBox.Show("Attach the debugger now");
if (args[0].Equals("version"))
#if DEBUG
String typeOfBuild = "d";
#else
String typeOfBuild = "r";
#endif
String output = typeOfBuild + Assembly.GetExecutingAssembly()
.GetName().Version.ToString();
//Just for the fun of it
Console.Write(output);
Console.Beep(4000, 100);
Console.Beep(2000, 100);
Console.Beep(1000, 100);
Console.Beep(8000, 100);
return;
我在这里找到了这段代码:http://www.csharp411.com/console-output-from-winforms-application/ 我认为也值得在这里发布。
【讨论】:
这很好用,除了它现在在 Windows 8 和 Windows 10 中失败。失败的意思是除了和额外的提示之外没有输出(如果这是一个线索)。有人建议 AllocConsole 但只是闪现了一个 cmd 窗口. 还尝试了上面的 Chaz 的答案,但这在 Windows 7 中提供了一个新的控制台(尽管不在 8 或 10 中)。如果没有参数,我只需要在命令行上使用重定向运行或作为 gui 运行的选项。 我试过这个但没有用。仅使用AttachConsole(ATTACH_PARENT_PROCESS)
我得到了控制台输出,但在命令行上使用>
将其重定向不起作用。当我尝试这个答案时,我无法在控制台或文件中获得任何输出。【参考方案7】:
这里基本上可以发生两件事。
控制台输出 winforms 程序可以将自身附加到创建它的控制台窗口(或附加到不同的控制台窗口,或者如果需要,甚至附加到新的控制台窗口)。一旦附加到控制台窗口 Console.WriteLine() 等按预期工作。这种方法的一个问题是程序立即将控制权返回给控制台窗口,然后继续对其进行写入,因此用户也可以在控制台窗口中键入。我认为您可以使用带有 /wait 参数的 start 来处理这个问题。
Link to start Command syntax
重定向控制台输出 这是当有人通过管道将您的程序的输出输出到其他地方时,例如。
你的应用程序 > 文件.txt
在这种情况下附加到控制台窗口会有效地忽略管道。要完成这项工作,您可以调用 Console.OpenStandardOutput() 来获取输出应通过管道传输到的流的句柄。这仅在输出是管道的情况下才有效,因此如果您想处理这两种情况,您需要打开标准输出并将其写入并附加到控制台窗口。这确实意味着输出被发送到控制台窗口和管道,但它是我能找到的最佳解决方案。在我用来执行此操作的代码下方。
// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
StreamWriter _stdOutWriter;
// this must be called early in the program
public GUIConsoleWriter()
// this needs to happen before attachconsole.
// If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
// I guess it probably does write somewhere, but nowhere I can find out about
var stdout = Console.OpenStandardOutput();
_stdOutWriter = new StreamWriter(stdout);
_stdOutWriter.AutoFlush = true;
AttachConsole(ATTACH_PARENT_PROCESS);
public void WriteLine(string line)
_stdOutWriter.WriteLine(line);
Console.WriteLine(line);
【讨论】:
我无法写入控制台;首先附加父进程就可以了。谢谢。 看来这个答案需要你重写所有对Console.WriteLine
的调用,而不是调用上面定义的新WriteLine
。即使我尝试在命令行上运行应用程序并使用 >
重定向到文件时,我无法使用此代码将任何内容重定向到文件。
@uglycoyote,请确保您在应用程序中尽早构建 GUIConsoleWriter,否则由于神秘的 windows 类型原因,它将无法工作。我认为封装对Console.WriteLine
的调用只是一种好习惯,因为它允许您进行测试并轻松更改您登录的位置(例如,您可能希望开始登录到基于云的日志服务,如 PaperTrail ,或其他)
这对我来说在 Win10 中运行良好,甚至没有 StreamWriter _stdOutWriter;
管道是答案,但不是文件,而是使用更多,例如:yourapp |更多的 ;请参考***.com/a/13010823/1845672【参考方案8】:
//From your application set the Console to write to your RichTextkBox
//object:
Console.SetOut(new RichTextBoxWriter(yourRichTextBox));
//To ensure that your RichTextBox object is scrolled down when its text is
//changed add this event:
private void yourRichTextBox_TextChanged(object sender, EventArgs e)
yourRichTextBox.SelectionStart = yourRichTextBox.Text.Length;
yourRichTextBox.ScrollToCaret();
public delegate void StringArgReturningVoidDelegate(string text);
public class RichTextBoxWriter : TextWriter
private readonly RichTextBox _richTextBox;
public RichTextBoxWriter(RichTextBox richTexttbox)
_richTextBox = richTexttbox;
public override void Write(char value)
SetText(value.ToString());
public override void Write(string value)
SetText(value);
public override void WriteLine(char value)
SetText(value + Environment.NewLine);
public override void WriteLine(string value)
SetText(value + Environment.NewLine);
public override Encoding Encoding => Encoding.ASCII;
//Write to your UI object in thread safe way:
private void SetText(string text)
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (_richTextBox.InvokeRequired)
var d = new StringArgReturningVoidDelegate(SetText);
_richTextBox.Invoke(d, text);
else
_richTextBox.Text += text;
【讨论】:
非常酷的解决方案。正是我想要的!【参考方案9】:基于 Chaz 的回答,在 .NET 5 中有 a breaking change,因此需要在项目文件中进行两项修改,即更改 OutputType
并添加 DisableWinExeOutputInference
。示例:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows10.0.17763.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
【讨论】:
【参考方案10】:using System;
using System.Runtime.InteropServices;
namespace SomeProject
class GuiRedirect
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern FileType GetFileType(IntPtr handle);
private enum StandardHandle : uint
Input = unchecked((uint)-10),
Output = unchecked((uint)-11),
Error = unchecked((uint)-12)
private enum FileType : uint
Unknown = 0x0000,
Disk = 0x0001,
Char = 0x0002,
Pipe = 0x0003
private static bool IsRedirected(IntPtr handle)
FileType fileType = GetFileType(handle);
return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
public static void Redirect()
if (IsRedirected(GetStdHandle(StandardHandle.Output)))
var initialiseOut = Console.Out;
bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
if (errorRedirected)
var initialiseError = Console.Error;
AttachConsole(-1);
if (!errorRedirected)
SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
【讨论】:
在命令提示符下工作正常,但在“开始”>“运行”或在 Visual Studio 中不行。要使其在所有情况下都能正常工作,请将 AttachConsole 行替换为: if (!AttachConsole(-1)) AllocConsole();如果 AllocConsole() 被调用,FreeConsole() 也应该被调用,否则控制台主机在终止程序后继续运行。 initialiseOut 和 initialiseError 的预期用途是什么,因为它们没有被使用?StandardHandle : uint
在这里是错误的......应该是 IntPtr 在 x86 和 x64 上都可以工作【参考方案11】:
在项目属性中将输出类型设置为 Console 将为您提供一个控制台应用程序以及您创建的表单。
【讨论】:
【参考方案12】:为什么不将其保留为 Window Forms 应用程序,并创建一个简单的表单来模仿控制台。可以使表单看起来像黑屏控制台,并让它直接响应按键。 然后,在 program.cs 文件中,您决定是否需要运行主窗体或 ConsoleForm。例如,我使用这种方法来捕获 program.cs 文件中的命令行参数。我创建了 ConsoleForm,最初将其隐藏,然后将命令行字符串传递给其中的 AddCommand 函数,该函数显示允许的命令。最后,如果用户给了 -h 或 -?命令,我在 ConsoleForm 上调用 .Show ,当用户点击它上的任何键时,我关闭了程序。如果用户不给 -?命令,我关闭隐藏的 ConsoleForm 并运行主窗体。
【讨论】:
我不确定这算不算一个问题。他给出了如何按照他的建议做的完整的一步一步,即使实际的代码会很好。【参考方案13】:您可以随时在应用程序类型、控制台或窗口之间切换。因此,您不会编写特殊的逻辑来查看标准输出。此外,在调试器中运行应用程序时,您将在输出窗口中看到所有标准输出。您也可以只添加一个断点,并在断点属性更改“当命中...”时,您可以输出任何消息和变量。您也可以选中/取消选中“继续执行”,您的断点将变为方形。因此,断点消息不会在调试输出窗口中更改应用程序中的任何内容。
【讨论】:
以上是关于如何在表单应用程序中显示控制台输出/窗口?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 GUI 输出窗口而不是 Visual Studio 2015 中的 cmd 控制台上显示我的 C++ 程序输出?
项目输出类型未更改为控制台应用程序,控制台窗口未在 WinForms 中显示