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

时间:2009-01-23 08:45:28

标签: c# winforms console

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

我想做这样的事情:

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

12 个答案:

答案 0 :(得分:76)

你想做的事情不可能以理智的方式进行。有一个类似的问题so look at the answers

然后还有insane approach撰写的backup available here.(网站缩写 - Jeffrey Knight):

  

问题:如何创建可在任一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.
     

我发布了代码来执行此操作,但需要注意。

     

我实际上认为这种方法会让你更多   在路上遇到麻烦而不是它的价值。例如,你必须这样做   有两个不同的UI' - 一个用于GUI,一个用于命令/   贝壳。你将不得不构建一些奇怪的中心逻辑   从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();
            }
        }
    }
}

答案 1 :(得分:64)

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

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

答案 2 :(得分:17)

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

答案 3 :(得分:13)

声明

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

此方法还支持仅显示控制台窗口,但不支持仅显示Windows窗体 - 即始终显示控制台。如果您不显示窗体,则只能与控制台窗口进行交互(即接收数据 - Console.ReadLine()Console.Read());输出到控制台 - Console.WriteLine() - 适用于两种模式。

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

项目步骤

从标准的控制台应用开始。

Main方法标记为[STAThread]

将项目中的引用添加到 System.Windows.Forms

将Windows 表单添加到项目中。

将标准Windows开始代码添加到Main方法:

结束结果

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

示例代码

Program.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的

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

答案 4 :(得分:6)

再次复活一个非常老的线程,因为这里的答案都不适合我。

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

  • 将您的项目编译为Windows应用程序。可执行文件启动时可能有父控制台,但可能没有。目标是重新使用现有控制台(如果存在),或者如果不存在则创建新控制台。
  • AttachConsole(-1)将查找父进程的控制台。如果有一个,它会附加到它,你就完成了。 (我试过这个,从cmd调用我的应用程序时它正常工作)
  • 如果AttachConsole返回false,则没有父控制台。使用AllocConsole创建一个。

示例:

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

提醒一句:如果您在连接或分配控制台之前尝试写入控制台,这种方法似乎不起作用。我的猜测是你第一次调用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());
        }
    }

答案 5 :(得分:3)

对我来说有用的是分别编写一个控制台应用程序来完成我想要的操作,将其编译为exe,然后执行Process.Start("MyConsoleapp.exe","Arguments")

答案 6 :(得分:2)

检查此源代码。所有注释代码 - 用于在Windows应用程序中创建控制台。取消注释 - 在控制台应用程序中隐藏控制台。来自here。 (以前 here 。)项目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
    }
}

答案 7 :(得分:1)

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

答案 8 :(得分:1)

创建 Windows 窗体应用程序。 在应用程序中设置项目属性以键入控制台应用程序。 该程序将打开一个控制台窗口并显示表格。

从表单或 Program.cs 调用 Console.Writeline() 以向控制台窗口发送消息。

您可以在 Program.cs 中发表评论

// Application.EnableVisualStyles();
// Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new Form1());

避免使用 Form1。

使用 C# 和 VS2019 进行测试。

答案 9 :(得分:0)

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

答案 10 :(得分:0)

根据上面的Jeffrey Knight引述,一旦我遇到需要API调用来完成某件事的情况,我就会停下来问自己“我是否使事情变得过于复杂了?”。

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

答案 11 :(得分:0)

还有另一个迟来的答案。 根据先前的建议,我无法获得由AllocConsole创建的控制台的任何输出,因此我从 Console应用程序开始。然后,如果不需要控制台:

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

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

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

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

        public static bool HideConsole()
        {
            var hwnd = GetConsoleWindow();
            GetWindowThreadProcessId(hwnd, out var pid);

            if (pid != Process.GetCurrentProcess().Id) // It's not our console - don't mess with it.
                return false;

            ShowWindow(hwnd, SW_HIDE);

            return true;
        }