Quiero crear un programa en C # que se pueda ejecutar como una aplicación CLI o GUI dependiendo de las banderas que se le pasen. Se puede hacer esto?
Encontré estas preguntas relacionadas, pero no cubren exactamente mi situación:
Quiero crear un programa en C # que se pueda ejecutar como una aplicación CLI o GUI dependiendo de las banderas que se le pasen. Se puede hacer esto?
Encontré estas preguntas relacionadas, pero no cubren exactamente mi situación:
Respuestas:
La respuesta de Jdigital apunta al blog de Raymond Chen , que explica por qué no puede tener una aplicación que sea a la vez un programa de consola y un programa que no sea de consola
*
: el sistema operativo necesita saber antes de que el programa comience a ejecutarse qué subsistema usar. Una vez que el programa ha comenzado a ejecutarse, es demasiado tarde para volver atrás y solicitar el otro modo.La respuesta de Cade apunta a un artículo sobre cómo ejecutar una aplicación .Net WinForms con una consola . Utiliza la técnica de llamar
AttachConsole
después de que el programa comienza a ejecutarse. Esto tiene el efecto de permitir que el programa vuelva a escribir en la ventana de la consola del símbolo del sistema que inició el programa. Pero los comentarios en ese artículo señalan lo que considero un defecto fatal: el proceso hijo no controla realmente la consola. La consola continúa aceptando entradas en nombre del proceso principal, y el proceso principal no es consciente de que debe esperar a que el hijo termine de ejecutarse antes de usar la consola para otras cosas.El artículo de Chen apunta a un artículo de Junfeng Zhang que explica un par de otras técnicas .
El primero es lo que usa Devenv . Funciona al tener dos programas. Uno es devenv.exe , que es el programa GUI principal, y el otro es devenv.com , que maneja tareas en modo consola, pero si se usa de una manera no similar a una consola, reenvía sus tareas a devenv.exe y salidas. La técnica se basa en la regla Win32 de 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 de esto que hace Windows Script Host. Proporciona dos binarios completamente separados, wscript.exe y cscript.exe . Asimismo, Java proporciona java.exe para programas de consola y javaw.exe para programas que no son de consola.
La segunda técnica de Junfeng es la que usa ildasm . Cita el proceso por el que pasó el autor de ildasm al ejecutarlo en ambos modos. En última instancia, esto es lo que hace:
No es suficiente simplemente llamar
FreeConsole
para que la primera instancia deje de ser un programa de consola. Esto se debe a que el proceso que inició el programa, cmd.exe , "sabe" que inició un programa en modo consola y está esperando que el programa deje de ejecutarse. LlamarFreeConsole
haría que ildasm dejara de usar la consola, pero no haría que el proceso principal comenzara a usar la consola.Entonces, la primera instancia se reinicia sola (con un parámetro de línea de comando adicional, supongo). Cuando llame
CreateProcess
, hay dos banderas diferentes para probar,DETACHED_PROCESS
yCREATE_NEW_CONSOLE
cualquiera de las cuales garantizará que la segunda instancia no se adjunte a la consola principal. Después de eso, la primera instancia puede terminar y permitir que el símbolo del sistema reanude el procesamiento de los comandos.El efecto secundario de esta técnica es que cuando inicia el programa desde una interfaz GUI, todavía habrá una consola. Destellará en la pantalla momentáneamente y luego desaparecerá.
Creo que la parte del artículo de Junfeng sobre el uso de editbin para cambiar el indicador del modo de consola del programa es una pista falsa. Su compilador o entorno de desarrollo debe proporcionar una configuración u opción para controlar qué tipo de binario crea. No debería ser necesario 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 decida cuál es el mal menor, puede elegir las implementaciones.
*
Digo sin consola en lugar de GUI porque de lo contrario es una dicotomía falsa. El hecho de que un programa no tenga una consola no significa que tenga una GUI. Una aplicación de servicio es un buen ejemplo. Además, un programa puede tener una consola y ventanas.fuente
WinMain
función con los parámetros apropiados (así que compile/SUBSYSTEM:WINDOWS
) y luego cambie el modo ex post facto para el cargador lanza un host de consola. Para obtener más comentarios, probé estoCREATE_NO_WINDOW
en CreateProcess yGetConsoleWindow() == NULL
como mi check-if-relanzado o no. Esto no corrige el parpadeo de la consola, pero significa que no tiene un argumento cmd especial.Consulte el blog de Raymond sobre este tema:
https://devblogs.microsoft.com/oldnewthing/20090101-00/?p=19643
Su primera frase: "No puedes, pero puedes intentar fingir".
fuente
http://www.csharp411.com/console-output-from-winforms-application/
Simplemente verifique los argumentos de la línea de comando antes de las
Application.
cosas de WinForms .Debo agregar que en .NET es RIDÍCULAMENTE fácil simplemente hacer una consola y proyectos GUI en la misma solución que comparten todos sus ensamblajes excepto main. Y en este caso, puede hacer que la versión de línea de comandos simplemente inicie la versión de GUI si se inicia sin parámetros. Obtendría una consola intermitente.
fuente
Hay una forma sencilla de hacer lo que quiera. Siempre lo uso al escribir aplicaciones que deberían tener una CLI y una GUI. Debe establecer su "OutputType" en "ConsoleApplication" para que esto funcione.
class Program { [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")] private static extern IntPtr _GetConsoleWindow(); /// <summary> /// The main entry point for the application. /// </summary> [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 }); } } }
fuente
Creo que la técnica preferida es lo que Rob llamó la técnica devenv de usar dos ejecutables: un lanzador ".com" y el original ".exe". Esto no es tan complicado de usar si tiene el código estándar para trabajar (vea el enlace a continuación).
La técnica utiliza trucos para que ".com" sea un proxy para stdin / stdout / stderr y ejecute el archivo .exe del mismo nombre. Esto da el comportamiento de permitir que el programa se ejecute en un modo de línea de comandos cuando se llama desde una consola (potencialmente solo cuando se detectan ciertos argumentos de la línea de comandos) mientras aún se puede iniciar como una aplicación GUI sin una consola.
Presenté un proyecto llamado dualsubsystem en Google Code que actualiza una antigua solución de codeguru de esta técnica y proporciona el código fuente y binarios de ejemplo de trabajo.
fuente
Esto es lo que creo que es la solución simple de .NET C # al problema. Solo para reafirmar el problema, cuando ejecuta la "versión" de la consola de la aplicación desde una línea de comando con un interruptor, la consola sigue esperando (no regresa al símbolo del sistema y el proceso sigue ejecutándose) incluso si tiene un
Environment.Exit(0)
al final de su código. Para solucionar esto, justo antes de llamarEnvironment.Exit(0)
, llame a esto:SendKeys.SendWait("{ENTER}");
Luego, la consola obtiene la tecla Enter final que necesita para regresar al símbolo del sistema y el proceso finaliza. Nota: No llames
SendKeys.Send()
o la aplicación se bloqueará.Todavía es necesario llamar
AttachConsole()
como se mencionó en muchas publicaciones, pero con esto no obtengo ningún parpadeo en la ventana de comando al iniciar la versión 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.
fuente
Console.WriteLine
no avanza el cursor de texto de la consola (principal). Entonces, cuando la aplicación se cierra, la posición del cursor está en el lugar equivocado y debe presionar Intro varias veces solo para que vuelva a un mensaje "limpio"./* ** 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), i.e. 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 <windows.h> #include <stdio.h> 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<argc;i++) { if (!strcmp(argv[i],"-minimized")) minimized=1; if (!strcmp(argv[i],"-gui")) gui=1; if (!strcmp(argv[i],"-gui-")) gui=0; if (!strcmp(argv[i],"-relaunch")) relaunch=1; } if (!gui && !relaunch) { /* RUN AS CONSOLE APP */ printf("Console app only.\n"); printf("Usage: dual [-gui[-]] [-minimized].\n\n"); if (!started_from_console) { char buf[16]; printf("Press <Enter> 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<argc;i++) { if (strlen(argv[i])+3+strlen(buf) > 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); }
fuente
He escrito un enfoque alternativo que evita el flash de la consola. Consulte Cómo crear un programa de Windows que funcione como aplicación de consola y GUI .
fuente
Ejecutar AllocConsole () en un constructor estático funciona para mí
fuente