C# 在 Windows 应用程序中显示控制台?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/472282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 04:45:46  来源:igfitidea点击:

Show Console in Windows Application?

c#winformsconsole

提问by Ase

Is there a way to show the console in a Windows application?

有没有办法在 Windows 应用程序中显示控制台?

I want to do something like this:

我想做这样的事情:

static class Program
{
    [STAThread]
    static void Main(string[] args) {
        bool consoleMode = Boolean.Parse(args[0]);

        if (consoleMode) {
            Console.WriteLine("consolemode started");
            // ...
        } else {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

采纳答案by Igal Serban

What you want to do is not possible in a sane way. There was a similar question so look at the answers.

您想要做的事情不可能以理智的方式进行。有一个类似的问题,所以看看答案

Then there's also an insane approach(site down - backup available here.) written by Jeffrey Knight:

然后还有一个由Jeffrey Knight编写的疯狂方法(站点关闭 -此处提供备份。):

Question: How do I create an application that can run in either GUI (windows) mode or command line / console mode?

On the surface of it, this would seem easy: you create a Console application, add a windows form to it, and you're off and running. However, there's a problem:

Problem: If you run in GUI mode, you end up with both a window and a pesky console lurking in the background, and you don't have any way to hide it.

What people seem to want is a true amphibian application that can run smoothly in either mode.

If you break it down, there are actually four use cases here:

User starts application from existing cmd window, and runs in GUI mode
User double clicks to start application, and runs in GUI mode
User starts application from existing cmd window, and runs in command mode
User double clicks to start application, and runs in command mode.

I'm posting the code to do this, but with a caveat.

I actually think this sort of approach will run you into a lot more trouble down the road than it's worth. For example, you'll have to have two different UIs' -- one for the GUI and one for the command / shell. You're going to have to build some strange central logic engine that abstracts from GUI vs. command line, and it's just going to get weird. If it were me, I'd step back and think about how this will be used in practice, and whether this sort of mode-switching is worth the work. Thus, unless some special case called for it, I wouldn't use this code myself, because as soon as I run into situations where I need API calls to get something done, I tend to stop and ask myself "am I overcomplicating things?".

Output type=Windows Application

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;

namespace WindowsApplication
{
    static class Program
    {
        /*
    DEMO CODE ONLY: In general, this approach calls for re-thinking 
    your architecture!
    There are 4 possible ways this can run:
    1) User starts application from existing cmd window, and runs in GUI mode
    2) User double clicks to start application, and runs in GUI mode
    3) User starts applicaiton from existing cmd window, and runs in command mode
    4) User double clicks to start application, and runs in command mode.

    To run in console mode, start a cmd shell and enter:
        c:\path\to\Debug\dir\WindowsApplication.exe console
        To run in gui mode,  EITHER just double click the exe, OR start it from the cmd prompt with:
        c:\path\to\Debug\dir\WindowsApplication.exe (or pass the "gui" argument).
        To start in command mode from a double click, change the default below to "console".
    In practice, I'm not even sure how the console vs gui mode distinction would be made from a
    double click...
        string mode = args.Length > 0 ? args[0] : "console"; //default to console
    */

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeConsole();

        [DllImport("kernel32", SetLastError = true)]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [STAThread]
        static void Main(string[] args)
        {
            //TODO: better handling of command args, (handle help (--help /?) etc.)
            string mode = args.Length > 0 ? args[0] : "gui"; //default to gui

            if (mode == "gui")
            {
                MessageBox.Show("Welcome to GUI mode");

                Application.EnableVisualStyles();

                Application.SetCompatibleTextRenderingDefault(false);

                Application.Run(new Form1());
            }
            else if (mode == "console")
            {

                //Get a pointer to the forground window.  The idea here is that
                //IF the user is starting our application from an existing console
                //shell, that shell will be the uppermost window.  We'll get it
                //and attach to it
                IntPtr ptr = GetForegroundWindow();

                int  u;

                GetWindowThreadProcessId(ptr, out u);

                Process process = Process.GetProcessById(u);

                if (process.ProcessName == "cmd" )    //Is the uppermost window a cmd process?
                {
                    AttachConsole(process.Id);

                    //we have a console to attach to ..
                    Console.WriteLine("hello. It looks like you started me from an existing console.");
                }
                else
                {
                    //no console AND we're in console mode ... create a new console.

                    AllocConsole();

                    Console.WriteLine(@"hello. It looks like you double clicked me to start
                   AND you want console mode.  Here's a new console.");
                    Console.WriteLine("press any key to continue ...");
                    Console.ReadLine();       
                }

                FreeConsole();
            }
        }
    }
}

问题:如何创建可以在 GUI (windows) 模式或命令行/控制台模式下运行的应用程序?

从表面上看,这似乎很简单:您创建一个控制台应用程序,向其中添加一个 Windows 窗体,然后您就可以开始运行了。然而,有一个问题:

问题:如果您在 GUI 模式下运行,您最终会同时看到一个窗口和一个隐藏在后台的讨厌的控制台,而且您无法隐藏它。

人们似乎想要的是一个真正的两栖动物应用程序,可以在任何一种模式下流畅运行。

如果你把它分解一下,这里实际上有四个用例:

User starts application from existing cmd window, and runs in GUI mode
User double clicks to start application, and runs in GUI mode
User starts application from existing cmd window, and runs in command mode
User double clicks to start application, and runs in command mode.

我正在发布代码来做到这一点,但有一个警告。

我实际上认为这种方法会让你在路上遇到更多的麻烦而不是它的价值。例如,您必须有两种不同的用户界面——一种用于 GUI,另一种用于命令/shell。你将不得不构建一些奇怪的中央逻辑引擎,从 GUI 和命令行中抽象出来,这只会变得很奇怪。如果是我,我会退后一步思考如何在实践中使用它,以及这种模式切换是否值得进行这项工作。因此,除非某些特殊情况需要它,否则我不会自己使用此代码,因为一旦遇到需要调用 API 来完成某事的情况,我往往会停下来问自己“我是否将事情过于复杂? ”。

输出类型=Windows 应用程序

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;

namespace WindowsApplication
{
    static class Program
    {
        /*
    DEMO CODE ONLY: In general, this approach calls for re-thinking 
    your architecture!
    There are 4 possible ways this can run:
    1) User starts application from existing cmd window, and runs in GUI mode
    2) User double clicks to start application, and runs in GUI mode
    3) User starts applicaiton from existing cmd window, and runs in command mode
    4) User double clicks to start application, and runs in command mode.

    To run in console mode, start a cmd shell and enter:
        c:\path\to\Debug\dir\WindowsApplication.exe console
        To run in gui mode,  EITHER just double click the exe, OR start it from the cmd prompt with:
        c:\path\to\Debug\dir\WindowsApplication.exe (or pass the "gui" argument).
        To start in command mode from a double click, change the default below to "console".
    In practice, I'm not even sure how the console vs gui mode distinction would be made from a
    double click...
        string mode = args.Length > 0 ? args[0] : "console"; //default to console
    */

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FreeConsole();

        [DllImport("kernel32", SetLastError = true)]
        static extern bool AttachConsole(int dwProcessId);

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [STAThread]
        static void Main(string[] args)
        {
            //TODO: better handling of command args, (handle help (--help /?) etc.)
            string mode = args.Length > 0 ? args[0] : "gui"; //default to gui

            if (mode == "gui")
            {
                MessageBox.Show("Welcome to GUI mode");

                Application.EnableVisualStyles();

                Application.SetCompatibleTextRenderingDefault(false);

                Application.Run(new Form1());
            }
            else if (mode == "console")
            {

                //Get a pointer to the forground window.  The idea here is that
                //IF the user is starting our application from an existing console
                //shell, that shell will be the uppermost window.  We'll get it
                //and attach to it
                IntPtr ptr = GetForegroundWindow();

                int  u;

                GetWindowThreadProcessId(ptr, out u);

                Process process = Process.GetProcessById(u);

                if (process.ProcessName == "cmd" )    //Is the uppermost window a cmd process?
                {
                    AttachConsole(process.Id);

                    //we have a console to attach to ..
                    Console.WriteLine("hello. It looks like you started me from an existing console.");
                }
                else
                {
                    //no console AND we're in console mode ... create a new console.

                    AllocConsole();

                    Console.WriteLine(@"hello. It looks like you double clicked me to start
                   AND you want console mode.  Here's a new console.");
                    Console.WriteLine("press any key to continue ...");
                    Console.ReadLine();       
                }

                FreeConsole();
            }
        }
    }
}

回答by Joe Soul-bringer

In wind32, console-mode applications are a completely different beast from the usual message-queue-receiving applications. They are declared and compile differently. You might create an application which has both a console part and normal window and hide one or the other. But suspect you will find the whole thing a bit more work than you thought.

在wind32 中,控制台模式应用程序与通常的消息队列接收应用程序完全不同。它们的声明和编译方式不同。您可以创建一个同时具有控制台部分和普通窗口的应用程序并隐藏其中一个。但是怀疑你会发现整个事情比你想象的要多一些。

回答by Sam Meldrum

Disclaimer

免责声明

There is a way to achieve this which is quite simple, but I wouldn't suggest it is a good approach for an app you are going to let other people see. But if you had some developer need to show the console and windows forms at the same time, it can be done quite easily.

有一种方法可以很简单地实现这一点,但我不建议它是您要让其他人看到的应用程序的好方法。但是,如果您有一些开发人员需要同时显示控制台和 Windows 窗体,则可以很容易地完成。

This method also supports showing only the Console window, but does not support showing only the Windows Form - i.e. the Console will always be shown. You can only interact (i.e. receive data - Console.ReadLine(), Console.Read()) with the console window if you do not show the windows forms; output to Console - Console.WriteLine()- works in both modes.

此方法也支持只显示控制台窗口,但不支持只显示 Windows 窗体 - 即控制台将始终显示。如果不显示窗体,则只能与控制台窗口交互(即接收数据 - Console.ReadLine(), Console.Read());输出到控制台 -Console.WriteLine()在两种模式下都可以工作。

This is provided as is; no guarantees this won't do something horrible later on, but it does work.

这是按原样提供的;不能保证这以后不会做一些可怕的事情,但它确实有效。

Project steps

项目步骤

Start from a standard Console Application.

从标准控制台应用程序开始

Mark the Mainmethod as [STAThread]

将该Main方法标记为[STAThread]

Add a reference in your project to System.Windows.Forms

在您的项目中添加对System.Windows.Forms的引用

Add a Windows Formto your project.

将 Windows窗体添加到您的项目。

Add the standard Windows start code to your Mainmethod:

将标准的 Windows 启动代码添加到您的Main方法中:

End Result

最终结果

You will have an application that shows the Console and optionally windows forms.

您将拥有一个显示控制台和可选窗口窗体的应用程序。

Sample Code

示例代码

Program.cs

程序.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    class Program {

        [STAThread]
        static void Main(string[] args) {

            if (args.Length > 0 && args[0] == "console") {
                Console.WriteLine("Hello world!");
                Console.ReadLine();
            }
            else {
                Application.EnableVisualStyles(); 
                Application.SetCompatibleTextRenderingDefault(false); 
                Application.Run(new Form1());
            }
        }
    }
}

Form1.cs

表格1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ConsoleApplication9 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void Form1_Click(object sender, EventArgs e) {
            Console.WriteLine("Clicked");
        }
    }
}

回答by ICR

Easiest way is to start a WinForms application, go to settings and change the type to a console application.

最简单的方法是启动 WinForms 应用程序,转到设置并将类型更改为控制台应用程序。

回答by abatishchev

Check this source code out. All commented code – used to create a console in a Windows app. Uncommented – to hide the console in a console app. From here. (Previously here.) Project reg2run.

检查此源代码。所有注释代码 - 用于在 Windows 应用程序中创建控制台。未注释 - 在控制台应用程序中隐藏控制台。从这里开始。(以前在这里。)项目reg2run

// Copyright (C) 2005-2015 Alexander Batishchev (abatishchev at gmail.com)

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Reg2Run
{
    static class ManualConsole
    {
        #region DllImport
        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool AllocConsole();
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)]string fileName, [MarshalAs(UnmanagedType.I4)]int desiredAccess, [MarshalAs(UnmanagedType.I4)]int shareMode, IntPtr securityAttributes, [MarshalAs(UnmanagedType.I4)]int creationDisposition, [MarshalAs(UnmanagedType.I4)]int flagsAndAttributes, IntPtr templateFile);
        */

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool FreeConsole();

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        private static extern IntPtr GetStdHandle([MarshalAs(UnmanagedType.I4)]int nStdHandle);

        /*
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetStdHandle(int nStdHandle, IntPtr handle);
        */
        #endregion

        #region Methods
        /*
        public static void Create()
        {
            var ptr = GetStdHandle(-11);
            if (!AllocConsole())
            {
                throw new Win32Exception("AllocConsole");
            }
            ptr = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
            if (!SetStdHandle(-11, ptr))
            {
                throw new Win32Exception("SetStdHandle");
            }
            var newOut = new StreamWriter(Console.OpenStandardOutput());
            newOut.AutoFlush = true;
            Console.SetOut(newOut);
            Console.SetError(newOut);
        }
        */

        public static void Hide()
        {
            var ptr = GetStdHandle(-11);
            if (!CloseHandle(ptr))
            {
                throw new Win32Exception();
            }
            ptr = IntPtr.Zero;
            if (!FreeConsole())
            {
                throw new Win32Exception();
            }
        }
        #endregion
    }
}

回答by EFraim

Actually AllocConsole with SetStdHandle in a GUI application might be a safer approach. The problem with the "console hiHymaning" already mentioned, is that the console might not be a foreground window at all, (esp. considering the influx of new window managers in Vista/Windows 7) among other things.

实际上,在 GUI 应用程序中使用 SetStdHandle 的 AllocConsole 可能是一种更安全的方法。已经提到的“控制台劫持”的问题在于,控制台可能根本不是前台窗口(尤其是考虑到 Vista/Windows 7 中新窗口管理器的涌入)等。

回答by Anthony

This is a tad old (OK, it's VERY old), but I'm doing the exact same thing right now. Here's a very simple solution that's working for me:

这有点旧(好吧,它很旧),但我现在正在做完全相同的事情。这是一个对我有用的非常简单的解决方案:

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();

[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

const int SW_HIDE = 0;
const int SW_SHOW = 5;

public static void ShowConsoleWindow()
{
    var handle = GetConsoleWindow();

    if (handle == IntPtr.Zero)
    {
        AllocConsole();
    }
    else
    {
        ShowWindow(handle, SW_SHOW);
    }
}

public static void HideConsoleWindow()
{
    var handle = GetConsoleWindow();
    ShowWindow(handle, SW_HIDE);
}

回答by Ian

What worked for me was to write a console app separately that did what I wanted it to do, compile it down to an exe, and then do Process.Start("MyConsoleapp.exe","Arguments")

对我有用的是单独编写一个控制台应用程序来完成我想要它做的事情,将其编译为一个 exe,然后执行 Process.Start("MyConsoleapp.exe","Arguments")

回答by Kevin Holt

Resurrecting a very old thread yet again, since none of the answers here worked very well for me.

再次复活一个非常旧的线程,因为这里没有一个答案对我有用。

I found a simple way that seems pretty robust and simple. It worked for me. The idea:

我找到了一种看起来非常健壮和简单的简单方法。它对我有用。想法:

  • Compile your project as a Windows Application. There might be a parent console when your executable starts, but maybe not. The goal is to re-use the existing console if one exists, or create a new one if not.
  • AttachConsole(-1) will look for the console of the parent process. If there is one, it attaches to it and you're finished. (I tried this and it worked properly when calling my application from cmd)
  • If AttachConsole returned false, there is no parent console. Create one with AllocConsole.
  • 将您的项目编译为 Windows 应用程序。可执行文件启动时可能有一个父控制台,但也可能没有。目标是重用现有控制台(如果存在),或者如果不存在则创建一个新控制台。
  • AttachConsole(-1) 将查找父进程的控制台。如果有一个,它会附加到它上面,你就完成了。(我试过了,从 cmd 调用我的应用程序时它工作正常)
  • 如果 AttachConsole 返回 false,则没有父控制台。使用 AllocConsole 创建一个。

Example:

例子:

static class Program
{
    [DllImport( "kernel32.dll", SetLastError = true )]
    static extern bool AllocConsole();

    [DllImport( "kernel32", SetLastError = true )]
    static extern bool AttachConsole( int dwProcessId );

    static void Main(string[] args)
    {
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }
}

A word of caution : it seems that if you try writing to the console prior to attaching or allocing a console, this approach doesn't work. My guess is the first time you call Console.Write/WriteLine, if there isn't already a console then Windows automatically creates a hidden console somewhere for you. (So perhaps Anthony's ShowConsoleWindow answer is better after you've already written to the console, and my answer is better if you've not yet written to the console). The important thing to note is that this doesn't work:

警告:似乎如果您在附加或分配控制台之前尝试写入控制台,则此方法不起作用。我的猜测是您第一次调用 Console.Write/WriteLine,如果还没有控制台,那么 Windows 会自动在某处为您创建一个隐藏的控制台。(所以也许 Anthony 的 ShowConsoleWindow 答案在您已经写入控制台之后会更好,如果您尚未写入控制台,我的答案会更好)。需要注意的重要一点是,这不起作用:

static void Main(string[] args)
    {
        Console.WriteLine("Welcome to the program");   //< this ruins everything
        bool consoleMode = Boolean.Parse(args[0]);
        if (consoleMode)
        {
           if (!AttachConsole(-1))
              AllocConsole();
           Console.WriteLine("consolemode started");   //< this doesn't get displayed on the parent console
           // ...
        } 
        else
        {
           Application.EnableVisualStyles();
           Application.SetCompatibleTextRenderingDefault(false);
           Application.Run(new Form1());
        }
    }

回答by Jinlye

As per Jeffrey Knight quote above, as soon as I run into situations where I need API calls to get something done, I tend to stop and ask myself "am I overcomplicating things?".

正如上面 Jeffrey Knight 引用的那样,一旦我遇到需要调用 API 来完成某事的情况,我往往会停下来问自己“我是不是把事情过于复杂了?”。

If what is wanted is to have some code and run it in Windows GUI mode or Console mode, consider moving the code used in both modes off to a code library DLL, and then having a Windows Forms application that uses that DLL, and a Console application that uses that DLL (i.e. if in Visual Studio you now have a three-project solution: library with the bulk of the code, GUI with just the Win Forms code, and Console with just your console code.)

如果需要一些代码并在 Windows GUI 模式或控制台模式下运行它,请考虑将在这两种模式下使用的代码移到代码库 DLL,然后拥有一个使用该 DLL 的 Windows 窗体应用程序和一个控制台使用该 DLL 的应用程序(即,如果在 Visual Studio 中,您现在有一个包含三个项目的解决方案:包含大量代码的库、仅包含 Win Forms 代码的 GUI,以及仅包含您的控制台代码的控制台。)