Frage

Ich möchte ein C # Programm machen, die als CLI ausgeführt werden können oder GUI-Anwendung je nachdem, welche Flags hineingeben. Kann dies geschehen?

Ich habe diese Fragen gefunden, aber sie nicht genau meine Situation abdecken:

War es hilfreich?

Lösung

jdigital Antwort auf Raymond Chen Blog , was erklärt, warum man‘ t eine Anwendung, die sowohl ein Konsolenprogramm ist und ein nicht-console* Programm: das Betriebssystem muss wissen, , bevor das Programm beginnt zu laufen welche Subsystem zu verwenden. Sobald das Programm läuft gestartet, es ist zu spät, um den anderen Modus zurück zu gehen und fordern.

Cade Antwort auf einen Artikel rel="nofollow eine .NET WinForms-Anwendung mit einer Konsole über das Ausführen . Es nutzt die Technik des Aufrufs AttachConsole , nachdem das Programm zu laufen beginnt. Dies hat den Effekt, daß das Programm zurück in das Konsolenfenster der Eingabeaufforderung schreiben, die das Programm gestartet. Aber die Kommentare in diesem Artikel darauf hinweisen, was ich als ein fataler Fehler sein. Das Kind Prozess wirklich nicht die Konsole steuern Die Konsole weiterhin Eingang des übergeordneten Prozesses im Namen zu akzeptieren, und die Eltern Prozess ist nicht bewusst, dass es warten soll, für das Kind, bevor sie die Konsole für andere Dinge laufen zu beenden.

Chens Artikel verweist auf ein Artikel von Junfeng Zhang, der erklärt, ein paar andere Techniken .

Die erste ist, was devenv verwendet. Es funktioniert, indem tatsächlich zwei Programme mit. Eine davon ist devenv.exe , die die Haupt GUI-Programm ist, und das andere ist devenv.com , die Konsole-Modus Aufgaben behandelt, aber wenn es in einem nicht verwendet wird konsolenartig, leitet er seine Aufgaben devenv.exe und Ausfahrten. Die Technik beruht auf der Win32-Regel, dass com Dateien vor gewählt get exe Dateien, wenn Sie einen Befehl ohne die Dateierweiterung eingeben.

Es gibt eine einfachere Variante davon, dass das Windows Script Host tut. Es bietet zwei völlig getrennte Binärdateien, wscript.exe und cscript.exe . Ebenso Java bietet java.exe für Konsolen-Programme und javaw.exe für Nicht-Konsolen-Programme.

Junfeng zweite Technik ist das, was ildasm verwendet. Er zitiert den Prozess, dass ildasm 's Autor durchgemacht, wenn es in beiden Modi laufen zu machen. Letztlich ist hier, was die Funktion:

  1. Das Programm wird als Konsolenmodus binär markiert, so dass es beginnt immer mit einer Konsole aus. Dies ermöglicht die Eingabe und Ausgabe-Umleitung als normal zu arbeiten.
  2. Wenn das Programm hat keine Konsolenmodus Befehlszeilenparameter, es Relaunches selbst.

Es ist nicht genug, um einfach FreeConsole zu nennen, um die erste Instanz zu machen aufhören, ein Konsolenprogramm zu sein. Das ist, weil der Prozess, der das Programm gestartet, cmd.exe , „weiß“, dass es ein Konsole-Modus-Programm und wartet auf das Programm gestartet laufen zu stoppen. Der Aufruf FreeConsole würde ildasm stoppt die Konsole, aber es würde nicht den übergeordneten Prozess machen starten mit der Konsole.

So die erste Instanz startet sich neu (mit einem zusätzlichen Befehlszeilenparameter, nehme ich an). Wenn Sie CreateProcess nennen, gibt es zwei verschiedene Fahnen, um zu versuchen, DETACHED_PROCESS und CREATE_NEW_CONSOLE , wobei jede davon wird sichergestellt, dass die zweite Instanz nicht an die übergeordnete Konsole angebracht werden. Danach kann die erste Instanz beenden und die comman erlaubend auffordern Verarbeitungsbefehlen wieder aufzunehmen.

Der Nebeneffekt dieser Technik besteht darin, dass, wenn Sie das Programm von einer GUI-Schnittstelle zu starten, wird es noch eine Konsole sein. Es wird kurz auf dem Bildschirm blinkt und dann verschwinden.

Der Teil Artikel in Junfeng ist über die Verwendung von EDITBIN das Programm der Konsole-Modus-Flag ändern ein roter Hering ist, glaube ich. Ihr Compiler oder Entwicklungsumgebung sollte eine Einstellung oder Option bieten zu steuern, welche Art von binären es schafft. Es sollte keine Notwendigkeit, etwas zu ändern danach.

Unter dem Strich ist es also, dass Sie können entweder zwei Binärdateien haben, oder Sie können eine momentane Flackern von einem Konsolenfenster haben . Sobald Sie entscheiden, welche das kleinere Übel ist, haben Sie die Wahl von Implementierungen.

* Ich sage nicht-Konsole statt GUI , weil sonst ist es eine falsche Dichotomie. Nur weil ein Programm keine Konsole hat, bedeutet nicht, dass es eine GUI hat. Eine Service-Anwendung ist ein gutes Beispiel. Außerdem kann ein Programm eine Konsole hat und Fenster.

Andere Tipps

Schauen Sie sich Raymond Blog zu diesem Thema:

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

Sein erster Satz: „Sie können nicht, aber man kann zu fälschen es versuchen“

http://www.csharp411.com/console-output- von-WinForms-Anwendung /

Überprüfen Sie einfach die Befehlszeilenargumente vor dem WinForms Application. Sachen.

soll ich hinzufügen, dass in .NET es lächerlich einfach ist, einfach eine Konsole und GUI-Projekte in der gleichen Lösung zu machen, die außer Haupt all ihre Versammlungen teilen. Und in diesem Fall könnten Sie die Kommandozeile machen Version einfach die GUI-Version starten, wenn es ohne Parameter gestartet wird. Sie würden eine blinkende Konsole erhalten.

Es gibt eine einfache Art und Weise zu tun, was Sie wollen. Ich verwende es immer dann, wenn Anwendungen zu schreiben, die sowohl eine CLI und eine GUI haben sollten. Sie haben Ihre „Outputtype“ auf „Consoleapplication“, um für diese zu arbeiten.

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

Ich denke, die bevorzugte Technik ist das, was Rob die genannt devenv Technik der Verwendung von zwei ausführbaren Dateien: eine Trägerrakete „.com“ und die original „.exe“. Das ist nicht so schwierig zu verwenden, wenn Sie den Standardcode für die Arbeit mit (siehe Link unten).

Die Technik verwendet Tricks zu haben, dass „.com“ sein ein Proxy für den stdin / stdout / stderr und starten Sie die gleichen Namens EXE-Datei. Diese geben das Verhalten das Programm ermöglicht in einem Kommandozeilen-Modus, um die Vorform, wenn das Formular eine Konsole genannt (möglicherweise nur, wenn bestimmte Befehlszeilenargumente erkannt werden), während nach wie vor als eine GUI-Anwendung frei von einer Konsole starten zu können.

gehosteten ich ein Projekt namens dualsubsystem auf Google Code , dass Updates eine alte MS-Office-Lösung dieser Technik und bietet den Quellcode und Arbeits Beispiel Binärdateien.

Hier ist, was ich glaube, die einfache .NET C # Lösung für das Problem zu sein. Nur das Problem neu zu formulieren, wenn Sie die Konsole „Version“ der App über eine Befehlszeile mit einem Schalter ausgeführt wird, hält die Konsole wartet (es kehrt nicht in die Eingabeaufforderung und der Prozess läuft weiter), auch wenn Sie eine am Ende des Codes Environment.Exit(0). Um dies zu beheben, kurz vor Environment.Exit(0) anrufen, wählen Sie diese:

SendKeys.SendWait("{ENTER}");

Dann wird die Konsole die endgültige Enter-Taste an der Eingabeaufforderung zurückgeben muss und der Prozess endet. . Hinweis: Nicht SendKeys.Send() nennen, oder die App abstürzt

Es ist immer noch notwendig AttachConsole() zu nennen, wie in vielen Stellen erwähnt, aber mit diesem ich keine Befehlsfenstern Flackern erhalten, wenn die WinForm Version der App zu starten.

Hier ist der gesamte Code in einer Beispielanwendung I (ohne WinForms-Code) erstellt:

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

Hoffe, dass es jemand aus auch zu verbringen Tage auf dieses Problem hilft. Danke für den Hinweis gehen @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);
    }

Ich habe einen alternativen Ansatz geschrieben, die die Konsole Blitz vermeidet. Siehe wie ein Windows-Programm erstellen, die sowohl als GUI und Konsolenanwendung funktioniert .

Ausführen AllocConsole () in einem statischen Konstruktor funktioniert für mich

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top