Domanda

Voglio creare un programma C # che può essere eseguito come CLI o applicazione GUI a seconda di quali flag vengono passati in essa. Questo può essere fatto?

Ho trovato queste domande correlate, ma non riguardano esattamente la mia situazione:

È stato utile?

Soluzione

Risposta di Jdigital punta a Blog di Raymond Chen , che spiega perché puoi " t ha un'applicazione che sia sia un programma console che un programma * non console: il sistema operativo deve conoscere prima che il programma inizi a funzionare quale sottosistema utilizzare. Una volta avviato il programma, è troppo tardi per tornare indietro e richiedere l'altra modalità.

Risposta di Cade punta a un articolo sull'esecuzione di un'applicazione .Net WinForms con una console . Utilizza la tecnica di chiamare AttachConsole dopo l'avvio del programma . Ciò ha l'effetto di consentire al programma di riscrivere nella finestra della console del prompt dei comandi che ha avviato il programma. Ma i commenti in quell'articolo sottolineano ciò che considero un difetto fatale: Il processo figlio in realtà non controlla la console. La console continua ad accettare input per conto del processo padre e il padre processo non è consapevole del fatto che dovrebbe attendere il completamento dell'esecuzione del figlio prima di utilizzare la console per altre cose.

L'articolo di Chen punta a un articolo di Junfeng Zhang che spiega un paio di altre tecniche .

Il primo è quello che usa devenv . Funziona in realtà con due programmi. Uno è devenv.exe , che è il principale programma della GUI, e l'altro è devenv.com , che gestisce le attività in modalità console, ma se utilizzato in un non- come una console, inoltra i suoi compiti a devenv.exe ed esce. La tecnica si basa sulla regola Win32 secondo cui i file com vengono scelti prima dei file exe quando si digita un comando senza l'estensione di file.

Esiste una variazione più semplice su ciò che fa Windows Script Host. Fornisce due binari completamente separati, wscript.exe e cscript.exe . Allo stesso modo, Java fornisce java.exe per i programmi console e javaw.exe per i programmi non console.

La seconda tecnica di Junfeng è quella che usa ildasm . Cita il processo che l'autore di ildasm ha attraversato durante l'esecuzione in entrambe le modalità. In definitiva, ecco cosa fa:

  1. Il programma è contrassegnato come binario in modalità console, quindi inizia sempre con una console. Ciò consente al reindirizzamento di input e output di funzionare normalmente.
  2. Se il programma non ha parametri della riga di comando in modalità console, si riavvia da solo.

Non è sufficiente chiamare FreeConsole per far cessare la prima istanza di essere un programma console. Questo perché il processo che ha avviato il programma, cmd.exe , & Quot; conosce & Quot; che ha avviato un programma in modalità console e attende che il programma venga interrotto. Chiamare CreateProcess farebbe smettere ildasm di usare la console, ma non farebbe in modo che il processo genitore inizi usando la console.

Quindi la prima istanza si riavvia da sola (suppongo che con un ulteriore parametro da riga di comando). Quando chiami DETACHED_PROCESS, ci sono due diversi flag da provare, CREATE_NEW_CONSOLE e <=> , entrambi i quali garantiranno che la seconda istanza non sia collegata alla console principale. Successivamente, la prima istanza può terminare e consentire al prompt dei comandi di riprendere l'elaborazione dei comandi.

L'effetto collaterale di questa tecnica è che quando tuavviare il programma da un'interfaccia GUI, ci sarà ancora una console. Lampeggerà momentaneamente sullo schermo e poi scomparirà.

La parte nell'articolo di Junfeng sull'uso di editbin per cambiare il flag di modalità console del programma è un'aringa rossa, credo. Il compilatore o l'ambiente di sviluppo dovrebbe fornire un'impostazione o un'opzione per controllare il tipo di binario che crea. Non dovrebbe essere necessario modificare nulla in seguito.

La linea di fondo, quindi, è che puoi avere due binari oppure puoi avere uno sfarfallio momentaneo di una finestra della console . Una volta deciso qual è il male minore, hai la tua scelta di implementazioni.

<=> Dico non console invece di GUI perché altrimenti è una falsa dicotomia. Solo perché un programma non ha una console non significa che ha una GUI. Un'applicazione di servizio è un ottimo esempio. Inoltre, un programma può avere una console e windows.

Altri suggerimenti

Dai un'occhiata al blog di Raymond su questo argomento:

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

La sua prima frase: " Non puoi, ma puoi provare a fingere. "

http://www.csharp411.com/console-output- da-WinForms-application /

Basta controllare gli argomenti della riga di comando prima delle cose di WinForms Application..

Dovrei aggiungere che in .NET è RIDICOLOSAMENTE facile realizzare semplicemente una console e progetti GUI nella stessa soluzione che condividono tutti i loro assemblaggi tranne quelli principali. E in questo caso, è possibile creare la versione da riga di comando semplicemente avviare la versione della GUI se viene avviata senza parametri. Avresti una console lampeggiante.

C'è un modo semplice per fare quello che vuoi. Lo uso sempre quando scrivo app che dovrebbero avere sia una CLI che una GUI. Devi impostare il tuo & Quot; OutputType & Quot; a " ConsoleApplication " affinché funzioni.

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
        });
      }
    }
  }

Penso che la tecnica preferita sia quella che Rob ha chiamato la tecnica devenv di usare due eseguibili: un launcher " .com " e l'originale " .exe " ;. Questo non è così difficile da usare se hai il codice boilerplate con cui lavorare (vedi link sotto).

La tecnica usa trucchi per avere quel " .com " essere un proxy per stdin / stdout / stderr e avviare lo stesso file .exe. Questo dà il comportamento di consentire al programma di preformarsi in una modalità a riga di comando quando viene chiamato da una console (potenzialmente solo quando vengono rilevati determinati argomenti della riga di comando) pur essendo ancora in grado di avviarsi come un'applicazione GUI libera da una console.

Ho ospitato un progetto chiamato dualsubsystem su Google Code che aggiorna una vecchia soluzione codeguru di questa tecnica e fornisce il codice sorgente e i binari di esempio funzionanti.

Ecco quello che credo sia la semplice soluzione .NET C # al problema. Giusto per riaffermare il problema, quando si esegue la console & Quot; versione & Quot; dell'app da una riga di comando con un'opzione, la console continua ad attendere (non torna al prompt dei comandi e il processo continua a funzionare) anche se si ha un Environment.Exit(0) alla fine del codice. Per risolvere questo problema, prima di chiamare SendKeys.Send(), chiama questo:

SendKeys.SendWait("{ENTER}");

Quindi la console ottiene il tasto Invio finale necessario per tornare al prompt dei comandi e il processo termina. Nota: non chiamare AttachConsole(), altrimenti l'app andrà in crash.

È ancora necessario chiamare <=> come indicato in molti post, ma con questo non ottengo sfarfallio della finestra di comando all'avvio della versione WinForm dell'app.

Ecco l'intero codice in un'app di esempio che ho creato (senza il codice 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());
            }
        }
    }
}

Spero che aiuti qualcuno a passare giorni su questo problema. Grazie per il suggerimento vai 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);
    }

Ho redatto un approccio alternativo che evita il flash della console. Vedi Come creare un programma Windows che funziona sia come GUI che come applicazione console .

Esegui AllocConsole () in un costruttore statico funziona per me

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top