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

Wil*_*Wil 114 c# compilation console-application winforms

要立即陷入困境,这是一个非常基本的例子:

using System;
using System.Windows.Forms;

class test
{ 
    static void Main()
    { 
        Console.WriteLine("test");
        MessageBox.Show("test");
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我使用默认选项(在命令行使用csc)编译它,正如预期的那样,它将编译到控制台应用程序.另外,因为我导入了System.Windows.Forms,它还会显示一个消息框.

现在,如果我使用该选项/target:winexe,我认为Windows Application与在项目选项中选择相同,正如预期的那样,我只会看到消息框而没有控制台输出.

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

所以,我的问题是 - 我知道你可以从控制台应用程序获得"windows"/表单输出,但是无论如何从Windows应用程序显示控制台?

wiz*_*rdz 137

这一个应该工作.

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();
Run Code Online (Sandbox Code Playgroud)

  • 真棒,这个问题似乎已被问到很多,这是我能找到的问题的唯一实际答案,+ 1 (7认同)
  • 如何在此控制台中打印消息?Console.Writeline() 不在控制台中打印任何内容。 (6认同)
  • 我在Windows 8和Windows 10上进行了测试: - AttachConsole在cmd框中工作 - AllocConsole在Visual Studio中运行.当需要alloc时,AttachConsole返回false.您还应该在以控制台模式终止应用程序之前调用FreeConsole().在我的程序中,我使用了Matthew Strawbridge的代码(见下文),将AttachConsole()行修改为:if(!AttachConsole(-1))AllocConsole(); (4认同)
  • 这不是很好,当从命令行运行时,它会弹出一个 _separate_ 控制台窗口,当从命令行运行并尝试使用 `>` 重定向输出时,我在我的文件中得到一个单独的控制台窗口和零输出。 (3认同)
  • 主要问题:当你关闭它时,所有应用程序都关闭. (2认同)

小智 123

也许这过于简单......

创建Windows窗体项目...

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

然后可以让Console和Forms一起运行,对我有用

  • 虽然,是的,从技术上讲,这可以用来容纳海报要求的内容 - 这不是一个很好的解决方案.通过这样做,如果您随后使用GUI启动winforms应用程序 - 您还将打开一个控制台窗口.在这种情况下,您需要更像Mike de Klerk的答案. (5认同)
  • 简单且工作正常。这应该是公认的答案。 (3认同)
  • 似乎最简单,也解决了我的问题。 (2认同)
  • 这绝对是最好的解决方案!其他人很聪明,但方式复杂 (2认同)
  • 这是我能够让我的 Winforms 应用程序在从命令行运行时将输出写入控制台的唯一解决方案,或者在使用 `>` 在命令行上重定向时写入文件的解决方案。但是,我希望有一个解决方案可以解释如何仅在某些时候作为“控制台应用程序”运行(即以编程方式启用更改这个神秘的 Visual Studio 设置所做的任何事情)。有谁知道这是如何在引擎盖下工作的? (2认同)

gun*_*171 56

如果您不担心打开控制台on-command,可以进入项目的属性并将其更改为Console Application

更改项目类型的屏幕截图.

这仍然会显示您的表单以及弹出控制台窗口.您无法关闭控制台窗口,但它可以作为调试的优秀临时记录器.

只需记住在部署程序之前将其关闭.

  • 比接受的答案更简单,更优雅的解决方案。 (2认同)
  • @JasonHarrison如果关闭控制台窗口,程序将关闭.程序运行时窗口也始终打开. (2认同)
  • @ gun2171:谢谢.答案中指出了这种方法的缺点:如果通过双击,开始菜单等启动应用程序,将出现控制台窗口. (2认同)
  • 这不适用于 .Net Core 5。我只测试了 .Net Core 的单一版本。除了不显示控制台窗口之外,它甚至导致 Visual Studio 2019 在我保存并尝试打开“表单设计”选项卡后一度完全冻结。 (2认同)

Ada*_*erg 16

您可以AttachConsole使用pinvoke 调用以获得连接到WinForms项目的控制台窗口:http://www.csharp411.com/console-output-from-winforms-application/

您可能还需要考虑使用Log4net(http://logging.apache.org/log4net/index.html)来配置不同配置的日志输出.


Ped*_*ues 13

创建一个 Windows 窗体应用程序,并将输出类型更改为控制台。

这将导致控制台和表单都打开。

在此处输入图片说明

  • 我尝试了很多例子,但没有一个产生符合我期望的结果。然而,这个解决方案正是我想要的,也是迄今为止最简单的解决方案。 (2认同)

ced*_*edd 11

这里基本上可以发生两件事.

控制台输出winforms程序可以将自身附加到创建它的控制台窗口(或者如果需要,可以附加到不同的控制台窗口,或者实际上附加到新的控制台窗口).一旦附加到控制台窗口,Console.WriteLine()等按预期工作.这种方法的一个问题是程序立即将控制权返回到控制台窗口,然后继续写入,因此用户也可以在控制台窗口中输入.你可以使用start和/ wait参数来处理这个问题.

链接以启动命令语法

重定向控制台输出当有人将程序的输出从其他地方传输出来时,例如.

yourapp> file.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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*erk 11

这对我有用,可以将输出传递给文件.用控制台调用

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;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我在这里找到了这个代码:http://www.csharp411.com/console-output-from-winforms-application/ 我认为这也值得在这里发布.

  • 这很好用除了它现在在Windows 8和Windows 10中失败.失败我的意思是没有输出除了和额外的提示(如果这是一个线索).有人建议AllocConsole但只是闪过一个cmd窗口. (4认同)

小智 7

根据 Chaz 的回答,.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>
Run Code Online (Sandbox Code Playgroud)


Kam*_* Kh 6

//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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)