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

有没有办法在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()); } } } 

你想做的事情是不可能的。 有一个类似的问题, 所以看看答案 。

然后,还有一个疯狂的方法 (网站下载备份在这里 )由杰弗里·奈特写的:

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

从表面上看,这看起来很简单:创build一个控制台应用程序,添加一个窗体,然后closures并运行。 但是,有一个问题:

问题:如果在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。 你将不得不build立一个奇怪的中央逻辑引擎,从GUI和命令行抽象出来,而且会变得怪异。 如果是我,我会退后一步,思考如何在实践中使用这种模式,这种模式切换是否值得工作。 因此,除非有一些特殊情况要求,否则我不会自己使用这些代码,因为一旦遇到需要API调用来完成某些事情的情况,我就会停下来问自己:“我是不是过于复杂? ”。

输出types= 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(); } } } } 

这是一个很老的(好吧,这是非常古老的),但我现在正在做同样的事情。 这是一个非常简单的解决scheme,为我工作:

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

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

放弃

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

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

这是按原样提供的; 没有保证,这将不会做可怕的事情,但它确实工作。

项目步骤

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

Main方法标记为[STAThread]

在项目中添加一个引用到System.Windows.Forms

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

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

最终结果

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

示例代码

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

再次复活一个非常古老的线程,因为这里没有任何答案对我来说工作得很好。

我发现了一个简单的方法,看起来非常健壮和简单。 它为我工作。 这个想法:

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

例:

 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自动为你创build一个隐藏的控制台。 (所以也许安东尼的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()); } } 

我的工作是分别编写一个控制台应用程序,做我想做的事情,把它编译成一个exe文件,然后执行Process.Start("MyConsoleapp.exe","Arguments")

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

检查这个源代码。 所有评论代码 – 用于在Windows应用程序中创build控制台。 取消注释 – 隐藏控制台应用程序中的控制台。 从这里 。 (以前在这里 。)项目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 } } 

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