Question

Je souhaite créer un programme C # pouvant être exécuté en tant que CLI. ou une application graphique en fonction des drapeaux qui y sont passés. Cela peut-il être fait?

J'ai trouvé ces questions, mais elles ne couvrent pas exactement ma situation:

Était-ce utile?

La solution

Réponse de Jdigital pointe vers le le blog de Raymond Chen , ce qui explique pourquoi vous ' t avoir une application qui est à la fois un programme console et un programme non-console: le système d’exploitation doit savoir avant que le programme ne commence à exécuter le sous-système à utiliser. Une fois le programme lancé, il est trop tard pour demander l’autre mode.

Réponse de Cade pointe vers un article sur l'exécution d'une application .Net WinForms avec une console . Il utilise la technique suivante: * après le démarrage du programme. . Cela a pour effet de permettre au programme d'écrire dans la fenêtre de la console de l'invite de commande qui a lancé le programme. Mais les commentaires de cet article soulignent ce que je considère être une faille fatale: Le processus enfant ne contrôle pas vraiment la console. La console continue à accepter les entrées de la part du processus parent et le processus parent process n'est pas conscient qu'il doit attendre que l'enfant ait fini de s'exécuter avant d'utiliser la console pour d'autres tâches.

L'article de Chen pointe vers un article de Junfeng Zhang qui explique quelques autres techniques .

Le premier est ce que devenv utilise. Cela fonctionne en ayant en fait deux programmes. L’un est devenv.exe , qui est le programme principal de l’interface graphique, et l’autre est devenv.com , qui gère les tâches en mode console, mais s’il est utilisé de manière non graphique. À la manière d'une console, il transmet ses tâches à devenv.exe et se ferme. La technique repose sur la règle Win32 selon laquelle les fichiers com sont choisis avant les fichiers exe lorsque vous tapez une commande sans l'extension de fichier.

Windows Script Host propose une variante plus simple à cet égard. Il fournit deux fichiers binaires complètement séparés, wscript.exe et cscript.exe . De même, Java fournit java.exe pour les programmes console et javaw.exe pour les programmes non-console.

La deuxième technique de

Junfeng est ce que ildasm utilise. Il cite le processus suivi par l'auteur de ildasm pour le faire fonctionner dans les deux modes. En fin de compte, voici ce que cela fait:

  1. Le programme est marqué comme un binaire en mode console, il commence donc toujours par une console. Cela permet à la redirection des entrées et des sorties de fonctionner normalement.
  2. Si le programme n'a pas de paramètre de ligne de commande en mode console, il se relance de lui-même.

Il ne suffit pas d'appeler AttachConsole pour que la première instance cesse d'être un programme console. En effet, le processus qui a lancé le programme, cmd.exe , & "Sait &"; qu'il a démarré un programme en mode console et attend son arrêt. Si vous appelez FreeConsole, ildasm cesserait d'utiliser la console, mais le processus parent ne démarrerait pas à l'aide de la console.

La première instance redémarre donc d'elle-même (avec un paramètre de ligne de commande supplémentaire, je suppose). Lorsque vous appelez CreateProcess, vous devez essayer deux indicateurs différents: DETACHED_PROCESS et CREATE_NEW_CONSOLE , l'un ou l'autre garantissant que la deuxième instance ne sera pas attachée à la console parent. Ensuite, la première instance peut se terminer et permettre à l'invite de commande de reprendre le traitement des commandes.

L’effet secondaire de cette technique est que lorsque vousdémarrez le programme à partir d'une interface graphique, il y aura toujours une console. Il clignotera brièvement sur l’écran puis disparaîtra.

La partie de l'article de Junfeng sur l'utilisation de editbin pour changer l'indicateur de mode console du programme est un fouillis rouge, je pense. Votre compilateur ou environnement de développement doit fournir un paramètre ou une option permettant de contrôler le type de binaire créé. Il ne devrait y avoir aucun besoin de modifier quoi que ce soit après.

La ligne du fond est donc que vous pouvez avoir deux fichiers binaires ou scintiller momentanément une fenêtre de la console . Une fois que vous avez décidé quel est le moindre mal, vous avez le choix entre plusieurs implémentations.

<=> Je dis non console au lieu de GUI car sinon, il s'agit d'une fausse dichotomie. Ce n’est pas parce qu’un programme n’a pas de console qu’il a une interface graphique. Une application de service est un excellent exemple. De plus, un programme peut avoir une console et Windows.

Autres conseils

Découvrez le blog de Raymond sur ce sujet:

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

Sa première phrase: & "Vous ne pouvez pas, mais vous pouvez essayer de la simuler". & ";

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

Il suffit de vérifier les arguments de la ligne de commande avant les éléments WinForms Application..

Je devrais ajouter que, dans .NET, il est extrêmement facile de créer simplement une console et des projets d'interface graphique dans la même solution, qui partagent tous leurs assemblys sauf principal. Et dans ce cas, vous pouvez créer la version en ligne de commande en lançant simplement la version graphique si elle est lancée sans paramètre. Vous obtiendrez une console clignotante.

Il existe un moyen facile de faire ce que vous voulez. Je l'utilise toujours lorsque j'écris des applications qui devraient avoir à la fois une interface de ligne de commande et une interface graphique. Vous devez définir votre & Quot; OutputType & Quot; à " ConsoleApplication " pour que cela fonctionne.

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

Je pense que la technique préférée est ce que Rob a appelé la technique devenv consistant à utiliser deux exécutables: un programme de lancement & ".com &"; et l'original " .exe " ;. Ce n’est pas si difficile à utiliser si vous avez le code standard sur lequel travailler (voir lien ci-dessous).

La technique utilise des astuces pour avoir ce " .com " être un proxy pour stdin / stdout / stderr et lancer le fichier .exe portant le même nom. Cela donne le comportement de permettre au programme de s’exécuter en mode ligne de commande lorsqu’il est appelé depuis une console (éventuellement uniquement lorsque certains arguments de ligne de commande sont détectés) tout en pouvant être lancé en tant qu’application graphique sans console.

J'ai hébergé un projet intitulé dualsubsystem sur Google Code qui met à jour une ancienne solution codeguru de cette technique et fournit le code source et des exemples de fichiers binaires.

Voici ce que je pense être la solution simple .NET C # au problème. Juste pour reformuler le problème, lorsque vous exécutez la console & Quot; version & Quot; Dans l’application depuis une ligne de commande avec un commutateur, la console continue d’attendre (elle ne revient pas à l’invite de commande et le processus continue de fonctionner) même si vous avez un Environment.Exit(0) à la fin de votre code. Pour résoudre ce problème, avant d'appeler SendKeys.Send(), appelez ceci:

SendKeys.SendWait("{ENTER}");

Ensuite, la console obtient la touche Entrée finale dont elle a besoin pour revenir à l'invite de commande et le processus se termine. Remarque: n'appelez pas AttachConsole(), sinon l'application va se bloquer.

Il est toujours nécessaire d'appeler <=> comme mentionné dans de nombreux articles, mais je ne vois aucun scintillement dans la fenêtre de commande lors du lancement de la version WinForm de l'application.

Voici l'intégralité du code d'un exemple d'application que j'ai créé (sans le code 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());
            }
        }
    }
}

J'espère que cela aidera quelqu'un qui passe aussi plusieurs jours sur ce problème. Merci pour le conseil aller à @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);
    }

J'ai écrit une approche alternative qui évite le flash de la console. Voir Comment créer un programme Windows fonctionnant à la fois comme interface graphique et comme application console .

Exécuter AllocConsole () dans un constructeur statique fonctionne pour moi

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top