如何在表单应用程序中显示控制台输出/窗口?

直接陷进去,一个非常基本的例子:

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是相同的) ,正如预期的那样,我将只看到 Message Box,没有控制台输出。

(事实上,从命令行启动它的那一刻起,我就可以在应用程序完成之前发出下一个命令)。

因此,我的问题是——我知道你可以从一个控制台应用输出“ Windows”/form,但是有没有办法显示一个 Windows 应用程序的控制台呢?

418505 次浏览

可以使用 pcall 调用 AttachConsole,以获得附加到 WinForms 项目的控制台窗口: http://www.csharp411.com/console-output-from-winforms-application/

您可能还需要考虑 Log4net (http://logging.apache.org/log4net/index.html) ,以便在不同的配置中配置日志输出。

这个应该可以。

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();

如果您不担心按命令打开控制台,那么可以进入项目的属性并将其更改为控制台应用

screenshot of changing the project type.

这将仍然显示您的表单,并弹出一个控制台窗口。您无法关闭控制台窗口,但它可以作为一个优秀的临时日志记录器进行调试。

只要记住在部署程序之前将其关闭即可。

基本上有两种情况可能发生。

控制台输出 Winforms 程序可以将自己附加到创建它的控制台窗口(或者附加到不同的控制台窗口,或者如果需要的话,确实附加到新的控制台窗口)。一旦连接到控制台窗口控制台。WriteLine ()等工作正常。这种方法的一个问题是,程序立即将控制返回到控制台窗口,然后继续写入控制台窗口,因此用户也可以在控制台窗口中输入。我认为可以使用 start 和/wait 参数来处理这个问题。

链接启动 Command 语法

重定向控制台输出 这是当某人从您的程序输出到其他地方的时候,例如。

Yourapp > file.txt

在这种情况下,附加到控制台窗口有效地忽略了管道。为了使这个工作,你可以呼叫控制台。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);
}
}

对我来说,将输出通过管道传输到一个文件很有用。 调用控制台

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

我在这里找到了这段代码: < a href = “ http://www.cSharp 411.com/sole-output-from-winforms-application/”rel = “ nofollow norefrer”> http://www.csharp411.com/console-output-from-winforms-application/ 我觉得也值得放在这里。

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

也许这过于简单化了。

创建 Windows 窗体项目..。

然后: 项目属性-> 应用-> 输出类型-> 控制台应用

然后可以有控制台和窗体一起运行,为我工作

您可以随时在应用程序类型、控制台或窗口之间切换。因此,您不需要编写特殊的逻辑来查看标准输出。此外,在调试器中运行应用程序时,您将在输出窗口中看到所有的标准输出。您也可以只添加一个断点,并且在断点属性中更改“ When Hit...”,您可以输出任何消息和变量。还可以选中/取消选中“继续执行”,断点将变成方形。因此,不更改调试输出窗口中应用程序中任何内容的断点消息。

//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;
}
}
}

为什么不把它作为一个 Window Forms 应用程序,创建一个简单的表单来模仿 Console 呢。可以使表单看起来像黑屏控制台,并让它直接响应按键。 然后,在 Program.cs 文件中,决定是否需要运行主窗体或 Console Form。例如,我使用这种方法捕获 Program.cs 文件中的命令行参数。我创建 Console Form,最初隐藏它,然后将命令行字符串传递给其中的 AddCommand 函数,该函数显示允许的命令。最后,如果用户给出-h 或-?命令,我调用。在控制台上显示,当用户点击任何键时,我关闭程序。如果用户不给-?命令,则关闭隐藏的 Console Form 并运行主窗体。

创建 Windows 窗体应用程序,并将输出类型更改为 Console。

它将导致 控制台和窗体打开。

enter image description here

以查兹的回答为基础。NET 5中有 一个突破性的改变,所以在项目文件中需要两个修改,即更改 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>

在项目属性中将输出类型设置为 控制台将会给你一个控制台应用以及你创建的表单。

如果你想要的是简单的调试输出以下工作为我。我在 C # 中使用 VS 2022编程

添加“使用 System.Diagnostics”

那么

  Debug.WriteLine("*****");
Debug.WriteLine(...);
Debug.WriteLine("");

输出显示在 VS2022的调试控制台中。有很多东西,所以我使用调试。WriteLine (“ * * * * *”)和 Debug。WriteLine (“”)帮助我查找输出。您还可以在启动后清除调试输出。

我还在工作,但在 VS 下运行时没有输出,运行两个调试