Pregunta

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

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

¿Fue útil?

Solución

Respuesta de Jdigital apunta a blog de Raymond Chen , que explica por qué puede ' No tiene una aplicación que sea tanto un programa de consola como 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 y solicitar el otro modo.

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 a AttachConsole una vez 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 secundario realmente no controla la consola. La consola continúa aceptando aportes en nombre del proceso principal, y el principal proceso no es consciente de que debe esperar a que el niño 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 utiliza 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 en de manera similar a una consola, reenvía sus tareas a devenv.exe y sale. La técnica se basa en la regla Win32 de que los archivos com se eligen antes que los archivos exe cuando escribe un comando sin la extensión de archivo.

Hay una variación más simple en esto que el Windows Script Host. Proporciona dos archivos binarios completamente separados, wscript.exe y cscript.exe . Del mismo modo, 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 lo que ildasm usa. Cita el proceso que atravesó el autor de ildasm al hacer que se ejecutara en ambos modos. En definitiva, esto es lo que hace:

  1. El programa 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 normalmente.
  2. Si el programa no tiene parámetros de línea de comandos en modo consola, se reinicia solo.

No es suficiente simplemente llamar a 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 , & Quot; sabe & Quot; que inició un programa en modo consola y está esperando que el programa deje de ejecutarse. Llamar a CreateProcess haría que ildasm dejara de usar la consola, pero no haría que el proceso principal comenzara usando la consola.

Entonces, la primera instancia se reinicia (con un parámetro adicional de línea de comandos, supongo). Cuando llama a DETACHED_PROCESS, hay dos indicadores diferentes para probar, CREATE_NEW_CONSOLE y <=> , 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 cuandoinicie el programa desde una interfaz GUI, todavía habrá una consola. Parpadeará momentáneamente en la pantalla y luego desaparecerá.

La parte del artículo de Junfeng sobre el uso de editbin para cambiar la bandera del modo consola del programa es una pista falsa, creo. Su compilador 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 puedes tener dos binarios, o puedes 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 no consola en lugar de GUI porque de lo contrario es una falsa dicotomía. El hecho de que un programa no tenga una consola no significa que tenga una GUI. Una aplicación de servicio es un excelente ejemplo. Además, un programa puede tener una consola y ventanas.

Otros consejos

Echa un vistazo al blog de Raymond sobre este tema:

https://devblogs.microsoft.com/oldnewthing/20090101- 00 /? P = 19643

Su primera oración: " No puedes, pero puedes intentar fingirlo. "

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 RIDICAMENTE fácil simplemente hacer una consola y proyectos de GUI en la misma solución que comparten todos sus ensamblados, excepto main. Y en este caso, puede hacer que la versión de línea de comandos simplemente inicie la versión GUI si se inicia sin parámetros. Obtendría una consola intermitente.

Hay una manera fácil de hacer lo que quieres. Siempre lo uso cuando escribo aplicaciones que deberían tener una CLI y una GUI. Debe configurar su & Quot; OutputType & Quot; a " 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
        });
      }
    }
  }

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 difícil de usar si tiene el código repetitivo para trabajar (vea el enlace a continuación).

La técnica utiliza trucos para tener " .com " ser un proxy para stdin / stdout / stderr e iniciar el archivo .exe con el mismo nombre. Esto da el comportamiento de permitir que el programa se preforma 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 se puede iniciar como una aplicación GUI libre de una consola.

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

Esto es lo que creo que es la solución simple de .NET C # al problema. Solo para repetir el problema, cuando ejecuta la consola & Quot; version & Quot; de la aplicación desde una línea de comando con un interruptor, la consola sigue esperando (no vuelve al símbolo del sistema y el proceso continúa ejecutándose) incluso si tiene un Environment.Exit(0) al final de su código. Para solucionar esto, justo antes de llamar a SendKeys.Send(), 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 llame a AttachConsole(), o la aplicación se bloqueará.

Todavía es necesario llamar a <=> como se menciona en muchas publicaciones, pero con esto no aparece el parpadeo de la ventana de comandos 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 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 también 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), 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);
    }

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top