Lo que quieres hacer no es posible de una manera cuerda. Hubo una pregunta similar, así que mire las respuestas .
Luego también hay un enfoque loco (sitio caído - copia de seguridad disponible aquí ) escrito por Jeffrey Knight :
Pregunta: ¿Cómo creo una aplicación que pueda ejecutarse en modo GUI (Windows) o en modo de línea de comandos / consola?
A primera vista, esto parecería fácil: creas una aplicación de consola, le agregas un formulario de Windows y ya estás listo. Sin embargo, hay un problema:
Problema: si ejecuta en modo GUI, terminará con una ventana y una consola molesta acechando en segundo plano, y no tiene ninguna forma de ocultarla.
Lo que la gente parece querer es una verdadera aplicación anfibia que pueda funcionar sin problemas en cualquier modo.
Si lo desglosa, 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 traerá muchos más problemas en el futuro de lo que vale la pena. Por ejemplo, deberá tener dos interfaces de usuario diferentes: una para la GUI y otra para el comando / shell. Tendrás que construir un extraño motor lógico central que se abstraiga de la GUI frente a la línea de comandos, y se pondrá extraño. Si fuera yo, daría un paso atrás y pensaría en cómo se usará en la práctica, y si este tipo de cambio de modo vale la pena el trabajo. Por lo tanto, a menos que un 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 a la API para hacer algo, tiendo a detenerme 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
{
[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)
{
string mode = args.Length > 0 ? args[0] : "gui";
if (mode == "gui")
{
MessageBox.Show("Welcome to GUI mode");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
else if (mode == "console")
{
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
if (process.ProcessName == "cmd" )
{
AttachConsole(process.Id);
Console.WriteLine("hello. It looks like you started me from an existing console.");
}
else
{
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();
}
}
}
}
AttachConsole
con-1
(el valor de la constante de la APIATTACH_PARENT_PROCESS
) en lugar de esperar que la ventana de primer plano sea la ventana de comando correcta para escribir.Esto es un poco viejo (OK, es MUY antiguo), pero estoy haciendo exactamente lo mismo en este momento. Aquí hay una solución muy simple que me funciona:
[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); }
fuente
Console.WriteLine
los registros no se muestran en el caso que comenzó desde cmd con argsusing System.Runtime.InteropServices;
La forma más sencilla es iniciar una aplicación WinForms, ir a la configuración y cambiar el tipo a una aplicación de consola.
fuente
Descargo de responsabilidad
Hay una manera de lograr esto que es bastante simple, pero no sugeriría que sea un buen enfoque para una aplicación que vas a dejar que otras personas vean. Pero si algún desarrollador necesita mostrar la consola y los formularios de Windows al mismo tiempo, se puede hacer 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, la consola siempre se mostrará. Puede sólo interactúan (es decir, recibir datos -
Console.ReadLine()
,Console.Read()
) con la ventana de la consola si no se presenta las formas de las ventanas; salida a la consola -Console.WriteLine()
- funciona en ambos modos.Esto se proporciona tal cual; no hay garantías de que esto no hará algo horrible más adelante, pero funciona.
Pasos del proyecto
Comience desde una aplicación de consola estándar .
Marque el
Main
método como[STAThread]
Agregue una referencia en su proyecto a System.Windows.Forms
Agregue un formulario de Windows a su proyecto.
Agregue el código de inicio estándar de Windows a su
Main
método:Resultado final
Tendrá una aplicación que muestra la Consola y opcionalmente 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"); } } }
fuente
Resucitando un hilo muy antiguo una vez más, ya que ninguna de las respuestas aquí funcionó muy bien para mí.
Encontré una forma simple que parece bastante robusta y simple. Funcionó para mí. La idea:
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 llama a Console.Write / WriteLine, si aún no hay una consola, Windows crea automáticamente una consola oculta en algún lugar para usted. (Entonces, tal vez la respuesta ShowConsoleWindow de Anthony sea mejor después de que ya haya escrito en la consola, y mi respuesta es mejor si aún no ha 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()); } }
fuente
\bin\Debug>shareCheck.exe /once
\bin\Debug>hello. It looks like you started me from an existing console.
Lo que funcionó para mí fue escribir una aplicación de consola por separado que hiciera lo que quería que hiciera, compilarla en un exe y luego hacer
Process.Start("MyConsoleapp.exe","Arguments")
fuente
Consulte este código fuente. Todo el código comentado: se utiliza para crear una consola en una aplicación de Windows. Sin comentarios: para ocultar la consola en una aplicación de consola. De aqui . (Anteriormente
aquí.) Proyectoreg2run
.// 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 } }
fuente
En realidad, AllocConsole con SetStdHandle en una aplicación GUI podría ser un enfoque más seguro. El problema con el "secuestro de la consola" ya mencionado, es que la consola podría no ser una ventana en primer plano en absoluto, (especialmente considerando la afluencia de nuevos administradores de ventanas en Vista / Windows 7) entre otras cosas.
fuente
En wind32, las aplicaciones en modo consola son una bestia completamente diferente de las aplicaciones habituales de recepción de mensajes en cola. Se declaran y compilan de forma diferente. Puede crear una aplicación que tenga tanto una parte de consola como una ventana normal y ocultar una u otra. Pero sospeche que encontrará todo un poco más trabajo de lo que pensaba.
fuente
Según la cita anterior de Jeffrey Knight, tan pronto como me encuentro con situaciones en las que necesito llamadas a la API para hacer algo, tiendo a detenerme y preguntarme "¿estoy complicando demasiado las cosas?".
Si lo que se desea es tener algo de código y ejecutarlo en el modo GUI de Windows o en el modo Consola, considere mover el código utilizado en ambos modos a una biblioteca de códigos DLL y luego tener una aplicación de Windows Forms que use esa DLL y una consola aplicación que usa esa DLL (es decir, si en Visual Studio ahora tiene una solución de tres proyectos: biblioteca con la mayor parte del código, GUI con solo el código de Win Forms y Consola con solo el código de su consola).
fuente
Y otra respuesta tardía. No pude obtener ningún resultado en la consola creada con
AllocConsole
las sugerencias anteriores, así que en su lugar estoy comenzando con la aplicación Consola . Entonces, si la consola no es necesaria:[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; }
fuente