c# - Mostrar la consola en la aplicación de Windows?
winforms console (9)
Renuncia
Hay una manera de lograr esto que es bastante simple, pero no sugeriría que sea un buen enfoque para una aplicación que va a dejar que otras personas vean. Pero si tuviera algún desarrollador que necesita mostrar los formularios de la consola y de Windows al mismo tiempo, puede hacerlo con bastante facilidad.
Este método también admite mostrar solo la ventana de la consola, pero no admite mostrar solo el formulario de Windows, es decir, siempre se mostrará la consola. Solo puede interactuar (es decir, recibir datos - Console.ReadLine()
, Console.Read()
) con la ventana de la consola si no muestra los formularios de Windows; salida a la consola - Console.WriteLine()
- funciona en ambos modos.
Esto se proporciona tal cual; no hay garantías de que esto no haga algo horrible más adelante, pero funciona.
Pasos del proyecto
Comience desde una aplicación de consola estándar.
Marque el método Main
como [STAThread]
Agregue una referencia en su proyecto a System.Windows.Forms
Agregue un Windows Form a su proyecto.
Agregue el código de inicio estándar de Windows a su método Main
:
Resultado final
Tendrá una aplicación que muestra la consola y, opcionalmente, los formularios de Windows.
Código de muestra
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");
}
}
}
¿Hay alguna manera de mostrar la consola en una aplicación de Windows?
Quiero hacer algo como esto:
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());
}
}
}
Compruebe este código fuente. Código comentado: se usa para crear una consola en una aplicación de Windows. Sin comentar: para ocultar la consola en una aplicación de consola. Desde here . (Anteriormente here .) Proyecto 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
}
}
En realidad, AllocConsole con SetStdHandle en una aplicación GUI podría ser un enfoque más seguro. El problema con el "secuestro de consola" ya mencionado, es que la consola podría no ser una ventana en primer plano, especialmente teniendo en cuenta la afluencia de nuevos administradores de ventanas en Vista / Windows 7, entre otras cosas.
En wind32, las aplicaciones en modo consola son una bestia completamente diferente de las aplicaciones habituales de recepción de cola de mensajes. Se declaran y compilan de manera diferente. Puede crear una aplicación que tenga tanto una parte de consola como una ventana normal y oculte una u otra. Pero sospecha que encontrarás todo un poco más de trabajo de lo que creías.
Esto es un poco viejo (OK, es MUY viejo), pero estoy haciendo exactamente lo mismo ahora. Aquí hay una solución muy simple que funciona para mí:
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;
La manera más fácil es iniciar una aplicación de WinForms, ir a la configuración y cambiar el tipo a una aplicación de consola.
Lo que funcionó para mí fue escribir una aplicación de consola por separado que hiciera lo que yo quería que hiciera, compilarla en un exe y luego hacer Process.Start("MyConsoleapp.exe","Arguments")
Lo que quieres hacer no es posible de una manera sensata. Hubo una pregunta similar, así que mira las respuestas .
Luego, también hay un enfoque insano (sitio inactivo: copia de seguridad disponible aquí ) escrito por Jeffrey Knight :
Pregunta: ¿Cómo creo una aplicación que se puede ejecutar en el modo de GUI (Windows) o en el modo de línea de comando / consola?
En la superficie, parece fácil: creas una aplicación de consola, le agregas un formulario de Windows y estás listo y en ejecución. Sin embargo, hay un problema:
Problema: si se ejecuta en modo GUI, termina con una ventana y una molesta consola al acecho en el fondo, y no tiene forma de ocultarla.
Lo que la gente parece querer es una verdadera aplicación de anfibios que se puede ejecutar sin problemas en cualquier modo.
Si lo desglosas, en realidad hay cuatro casos de uso aquí:
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.
Estoy publicando el código para hacer esto, pero con una advertencia.
De hecho, creo que este tipo de enfoque te llevará a muchos más problemas en el camino de lo que vale. Por ejemplo, tendrá que tener dos IU diferentes, una para la GUI y otra para el comando / shell. Tendrás que crear un extraño motor de lógica central que abstraiga de la interfaz gráfica de usuario frente a la línea de comandos, y se volverá extraño. Si fuera yo, daría un paso atrás y pensaría cómo se usará esto en la práctica, y si este tipo de cambio de modo vale la pena el trabajo. Por lo tanto, a menos que algún caso especial lo requiera, no usaría este código yo mismo, porque tan pronto como me encuentro con situaciones en las que necesito llamadas API para hacer algo, tiendo a parar y preguntarme "¿estoy complicando demasiado las cosas? ".
Tipo de salida = aplicación de 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(); } } } }
Resucitando un hilo muy viejo una vez más, ya que ninguna de las respuestas aquí funcionó muy bien para mí.
Encontré una manera simple que parece bastante robusta y simple. Funcionó para mí La idea:
- Compila tu proyecto como una aplicación de Windows. Puede haber una consola padre cuando se inicia el ejecutable, pero tal vez no. El objetivo es reutilizar la consola existente si existe, o crear una nueva si no es así.
- AttachConsole (-1) buscará la consola del proceso principal. Si hay uno, se adhiere a él y ya ha terminado. (Intenté esto y funcionó correctamente al llamar a mi aplicación desde cmd)
- Si AttachConsole devolvió false, no hay una consola padre. Crea uno con AllocConsole.
Ejemplo:
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());
}
}
}
Una advertencia: parece que si intenta escribir en la consola antes de adjuntar o asignar una consola, este enfoque no funciona. Supongo que es la primera vez que llamas a Console.Write / WriteLine, si todavía no hay una consola, Windows crea automáticamente una consola oculta para ti. (Entonces, quizás la respuesta de ShowConsoleWindow de Anthony sea mejor después de que ya hayas escrito en la consola, y mi respuesta es mejor si aún no has escrito en la consola). Lo importante a tener en cuenta es que esto no funciona:
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());
}
}