¿Puede un ejecutable ser una consola y una aplicación GUI?

Quiero hacer un progtwig C # que se pueda ejecutar como una aplicación CLI o GUI, dependiendo de qué banderas se pasen. Se puede hacer esto?

He encontrado estas preguntas relacionadas, pero no cubren exactamente mi situación:

  • Cómo escribir en la consola en una aplicación GUI
  • ¿Cómo obtengo la salida de la consola en C ++ con un progtwig de Windows?

La respuesta de Jdigital apunta al blog de Raymond Chen , lo que explica por qué no se puede tener una aplicación que sea tanto un progtwig de consola como un progtwig que no sea de consola * : el sistema operativo necesita saber antes de ejecutar el progtwig qué subsistema usar. Una vez que el progtwig ha comenzado a ejecutarse, es demasiado tarde para volver y solicitar el otro modo.

La respuesta de Cade apunta a un artículo sobre ejecutar una aplicación .Net WinForms con una consola . Utiliza la técnica de llamar a AttachConsole vez que el progtwig comienza a ejecutarse. Esto tiene el efecto de permitir que el progtwig vuelva a escribir en la ventana de la consola del símbolo del sistema que inició el progtwig. Pero los comentarios en ese artículo señalan lo que considero una falla fatal: el proceso hijo realmente no controla la consola. La consola continúa aceptando la entrada en nombre del proceso principal, y el proceso principal no sabe que debe esperar a que el hijo termine de ejecutarse antes de usar la consola para otras cosas.

El artículo de Chen señala un artículo de Junfeng Zhang que explica un par de otras técnicas .

El primero es lo que devenv usa. Funciona teniendo realmente dos progtwigs. Uno es devenv.exe , que es el progtwig principal de GUI, y el otro es devenv.com , que maneja las tareas de modo consola, pero si se usa de una manera que no es de consola, reenvía sus tareas a devenv.exe y salidas. La técnica se basa en la regla de Win32 que los archivos com se eligen antes que los archivos exe cuando se escribe un comando sin la extensión del archivo.

Hay una variación más simple en esto que hace Windows Script Host. Proporciona dos binarios completamente separados, wscript.exe y cscript.exe . Del mismo modo, Java proporciona java.exe para progtwigs de consola y javaw.exe para progtwigs que no son de consola.

La segunda técnica de Junfeng es lo que ildasm usa. Él cita el proceso por el cual el autor de ildasm pasó al hacerlo funcionar en ambos modos. En definitiva, esto es lo que hace:

  1. El progtwig está marcado como un binario en modo consola, por lo que siempre comienza con una consola. Esto permite que la redirección de entrada y salida funcione de forma normal.
  2. Si el progtwig no tiene parámetros de línea de comandos en modo consola, se vuelve a lanzar.

No es suficiente simplemente llamar a FreeConsole para que la primera instancia deje de ser un progtwig de consola. Esto se debe a que el proceso que inició el progtwig, cmd.exe , “sabe” que inició un progtwig en modo consola y está esperando que el progtwig deje de ejecutarse. Llamar a FreeConsole haría que ildasm dejara de usar la consola, pero no haría que el proceso principal comenzara a usar la consola.

Así que la primera instancia se reinicia a sí misma (con un parámetro extra de línea de comandos, supongo). Cuando llame a CreateProcess , hay dos indicadores diferentes para probar, DETACHED_PROCESS y CREATE_NEW_CONSOLE , cualquiera de los cuales asegurará que la segunda instancia no se adjuntará a la consola principal. Después de eso, la primera instancia puede finalizar y permitir que el símbolo del sistema reanude los comandos de procesamiento.

El efecto secundario de esta técnica es que cuando inicia el progtwig desde una interfaz GUI, todavía habrá una consola. Se parpadeará en la pantalla momentáneamente y luego desaparecerá.

La parte en el artículo de Junfeng sobre el uso de editbin para cambiar la bandera del modo consola del progtwig es una pista falsa, creo. Su comstackdor o entorno de desarrollo debe proporcionar una configuración u opción para controlar qué tipo de binario crea. No debería haber necesidad de modificar nada después.

La conclusión, entonces, es que puede tener dos binarios, o puede tener un parpadeo momentáneo de una ventana de consola . Una vez que decide cuál es el mal menor, tiene su elección de implementaciones.

* Digo no consola en lugar de GUI porque de lo contrario es una dicotomía falsa. El hecho de que un progtwig no tenga una consola no significa que tenga una GUI. Una aplicación de servicio es un buen ejemplo. Además, un progtwig puede tener una consola y ventanas.

Mira el blog de Raymond sobre este tema:

http://blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx

Su primera frase: “No puedes, pero puedes intentar fingirlo”.

http://www.csharp411.com/console-output-from-winforms-application/

Simplemente verifique los argumentos de línea de comando antes de la Application. WinForms Application. cosas.

Debo añadir que en .NET es RIDÍCULOSAMENTE fácil hacer una consola y proyectos de GUI en la misma solución que comparten todos sus ensambles excepto main. Y en este caso, podría hacer que la versión de la línea de comando simplemente inicie la versión de la GUI si se lanza sin parámetros. Tendrás una consola destellante.

Hay una manera fácil de hacer lo que quieres. Siempre lo uso cuando escribo aplicaciones que deberían tener tanto una CLI como una GUI. Debe configurar su “OutputType” en “ConsoleApplication” para que esto funcione.

 class Program { [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")] private static extern IntPtr _GetConsoleWindow(); ///  /// The main entry point for the application. ///  [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); /* * This works as following: * First we look for command line parameters and if there are any of them present, we run the CLI version. * If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console. * If there is no console at all, we show the GUI. * We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part. * This way we're both a CLI and a GUI. */ if (args != null && args.Length > 0) { // execute CLI - at least this is what I call, passing the given args. // Change this call to match your program. CLI.ParseCommandLineArguments(args); } else { var consoleHandle = _GetConsoleWindow(); // run GUI if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost")) // we either have no console window or we're started from within visual studio // This is the form I usually run. Change it to match your code. Application.Run(new MainForm()); else { // we found a console attached to us, so restart ourselves without one Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) { CreateNoWindow = true, UseShellExecute = false }); } } } 

Creo que la técnica preferida es lo que Rob llamó la técnica devenv de usar dos ejecutables: un iniciador “.com” y el original “.exe”. Esto no es tan complicado de usar si tiene el código repetitivo para trabajar (ver el enlace a continuación).

La técnica utiliza trucos para que “.com” sea un proxy para stdin / stdout / stderr y ejecute el mismo archivo .exe. Esto da el comportamiento de permitir que el progtwig se preforma en un modo de línea de comandos cuando se llama desde una consola (potencialmente solo cuando se detectan ciertos argumentos de línea de comandos) mientras se puede iniciar como una aplicación GUI libre de una consola.

Organicé un proyecto llamado dualsubsystem en Google Code que actualiza una antigua solución codeguru de esta técnica y proporciona el código fuente y los binarios de ejemplo de trabajo.

Aquí está lo que creo que es la solución simple .NET C # para el problema. Solo para replantear el problema, cuando ejecuta la “versión” de la consola de la aplicación desde una línea de comando con un conmutador, la consola sigue esperando (no vuelve al símbolo del sistema y el proceso continúa ejecutándose) incluso si tiene una Environment.Exit(0) al final de tu código. Para solucionar esto, justo antes de llamar a Environment.Exit(0) , llama a esto:

 SendKeys.SendWait("{ENTER}"); 

Luego, la consola obtiene la clave Enter final que necesita para regresar al símbolo del sistema y el proceso finaliza. Nota: No llame a SendKeys.Send() , o la aplicación se bloqueará.

Todavía es necesario llamar a AttachConsole() como se menciona en muchas publicaciones, pero con esto no obtengo ningún parpadeo de la ventana de comandos al iniciar la versión de WinForm de la aplicación.

Aquí está el código completo en una aplicación de muestra que creé (sin el código de WinForms):

 using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace ConsoleWriter { static class Program { [DllImport("kernel32.dll")] private static extern bool AttachConsole(int dwProcessId); private const int ATTACH_PARENT_PROCESS = -1; [STAThread] static void Main(string[] args) { if(args.Length > 0 && args[0].ToUpperInvariant() == "/NOGUI") { AttachConsole(ATTACH_PARENT_PROCESS); Console.WriteLine(Environment.NewLine + "This line prints on console."); Console.WriteLine("Exiting..."); SendKeys.SendWait("{ENTER}"); Environment.Exit(0); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } } 

Espero que ayude a alguien a pasar días en este problema. Gracias por la pista, vaya a @dantill.

 /* ** dual.c Runs as both CONSOLE and GUI app in Windows. ** ** This solution is based on the "Momentary Flicker" solution that Robert Kennedy ** discusses in the highest-rated answer (as of Jan 2013), ie the one drawback ** is that the console window will briefly flash up when run as a GUI. If you ** want to avoid this, you can create a shortcut to the executable and tell the ** short cut to run minimized. That will minimize the console window (which then ** immediately quits), but not the GUI window. If you want the GUI window to ** also run minimized, you have to also put -minimized on the command line. ** ** Tested under MinGW: gcc -o dual.exe dual.c -lgdi32 ** */ #include  #include  static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow); static LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam); static int win_started_from_console(void); static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp); int main(int argc,char *argv[]) { HINSTANCE hinst; int i,gui,relaunch,minimized,started_from_console; /* ** If not run from command-line, or if run with "-gui" option, then GUI mode ** Otherwise, CONSOLE app. */ started_from_console = win_started_from_console(); gui = !started_from_console; relaunch=0; minimized=0; /* ** Check command options for forced GUI and/or re-launch */ for (i=1;i to exit.\n"); fgets(buf,15,stdin); } return(0); } /* GUI mode */ /* ** If started from CONSOLE, but want to run in GUI mode, need to re-launch ** application to completely separate it from the console that started it. ** ** Technically, we don't have to re-launch if we are not started from ** a console to begin with, but by re-launching we can avoid the flicker of ** the console window when we start if we start from a shortcut which tells ** us to run minimized. ** ** If the user puts "-minimized" on the command-line, then there's ** no point to re-launching when double-clicked. */ if (!relaunch && (started_from_console || !minimized)) { char exename[256]; char buf[512]; STARTUPINFO si; PROCESS_INFORMATION pi; GetStartupInfo(&si); GetModuleFileNameA(NULL,exename,255); sprintf(buf,"\"%s\" -relaunch",exename); for (i=1;i 511) break; sprintf(&buf[strlen(buf)]," \"%s\"",argv[i]); } memset(&pi,0,sizeof(PROCESS_INFORMATION)); memset(&si,0,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwX = 0; /* Ignored unless si.dwFlags |= STARTF_USEPOSITION */ si.dwY = 0; si.dwXSize = 0; /* Ignored unless si.dwFlags |= STARTF_USESIZE */ si.dwYSize = 0; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; /* ** Note that launching ourselves from a console will NOT create new console. */ CreateProcess(exename,buf,0,0,1,DETACHED_PROCESS,0,NULL,&si,&pi); return(10); /* Re-launched return code */ } /* ** GUI code starts here */ hinst=GetModuleHandle(NULL); /* Free the console that we started with */ FreeConsole(); /* GUI call with functionality of WinMain */ return(my_win_main(hinst,argc,argv,minimized ? SW_MINIMIZE : SW_SHOWNORMAL)); } static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow) { HWND hwnd; MSG msg; WNDCLASSEX wndclass; static char *wintitle="GUI Window"; wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = NULL; wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = wintitle; wndclass.hIconSm = NULL; RegisterClassEx (&wndclass) ; hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wintitle,0, WS_VISIBLE|WS_OVERLAPPEDWINDOW, 100,100,400,200,NULL,NULL,hInstance,NULL); SetWindowText(hwnd,wintitle); ShowWindow(hwnd,iCmdShow); while (GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(msg.wParam); } static LRESULT CALLBACK WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam) { if (iMsg==WM_DESTROY) { PostQuitMessage(0); return(0); } return(DefWindowProc(hwnd,iMsg,wParam,lParam)); } static int fwbp_pid; static int fwbp_count; static int win_started_from_console(void) { fwbp_pid=GetCurrentProcessId(); if (fwbp_pid==0) return(0); fwbp_count=0; EnumWindows((WNDENUMPROC)find_win_by_procid,0L); return(fwbp_count==0); } static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp) { int pid; GetWindowThreadProcessId(hwnd,(LPDWORD)&pid); if (pid==fwbp_pid) fwbp_count++; return(TRUE); } 

He escrito un enfoque alternativo que evita el flash de la consola. Consulte Cómo crear un progtwig de Windows que funcione como una GUI y una aplicación de consola .

Ejecutar AllocConsole () en un constructor estático funciona para mí