Pergunta

Eu quero fazer um C # programa que pode ser executado como um CLI ou aplicação GUI, dependendo do que bandeiras são passados ??para ele. isso pode ser feito?

Eu encontrei estas perguntas relacionadas, mas eles não cobrem exatamente a minha situação:

Foi útil?

Solução

resposta de jdigital aponta para blog de Raymond Chen, que explica por que você pode' t ter um aplicativo que é tanto um programa de console e um programa não-console*: as necessidades do sistema operacional para saber antes que o programa começa a ser executado que subsistema de uso. Uma vez que o início do programa em execução, é tarde demais para voltar e solicitar o outro modo.

Cade resposta aponta para um artigo sobre a execução de um aplicativo .NET WinForms com um console . Ele usa a técnica de chamar AttachConsole após o programa começa a ser executado. Isso tem o efeito de permitir que o programa para escrever de volta para a janela do console do prompt de comando que iniciou o programa. Mas os comentários do ponto de artigo o que eu considero ser uma falha fatal:. O processo filho não realmente controlar o console O console continua a aceitar a entrada em nome do processo pai, eo pai processo não está ciente de que deve esperar para a criança para concluir a execução antes de usar o console para outras coisas.

Chen artigo aponta para um artigo de Junfeng Zhang que explica um par de outras técnicas .

O primeiro é o que devenv usos. Ela funciona por realmente ter dois programas. Um deles é devenv.exe , que é o programa GUI principal, eo outro é devenv.com , que as tarefas de modo consola alças, mas se for usado de forma não consola-como forma, ele encaminha suas tarefas a devenv.exe e sai. A técnica baseia-se na regra Win32 que com arquivos são escolhidos à frente de exe arquivos quando você digita um comando sem a extensão do arquivo.

Há uma variação mais simples desta que o Windows Script Host faz. Ele fornece dois binários completamente separadas, wscript.exe e cscript.exe . Da mesma forma, Java fornece java.exe para programas de console e javaw.exe para programas não-console.

segunda técnica de Junfeng é o que ildasm usos. Ele cita o processo que ildasm 's autor passou quando fazendo-a funcionar em ambos os modos. Em última análise, é aqui que o que ele faz:

  1. O programa é marcado como um binário de modo console, por isso sempre começa com um console. Isso permite que o redirecionamento de entrada e saída para o trabalho como normal.
  2. Se o programa não tem parâmetros de linha de comando de modo de console, ele re-lança-se.

Não é o suficiente para FreeConsole simplesmente chamada para fazer o primeiro cessar instância para ser um programa de console. Isso porque o processo que iniciou o programa, cmd.exe , "sabe" que ele começou um programa de modo console e está aguardando o programa para parar de correr. Chamando FreeConsole faria ildasm parar de usar o console, mas não faria o processo pai start usando o console.

Então, a primeira instância em si for reiniciado (com um parâmetro de linha de comando extra, suponho). Quando você chama CreateProcess, há duas bandeiras diferentes para tentar, DETACHED_PROCESS e CREATE_NEW_CONSOLE , cada um dos quais vai assegurar que o segundo exemplo, não irá ser ligado à consola de pai. Depois disso, o primeiro exemplo pode encerrar e permitir que o command pronto para retomar o processamento de comandos.

O efeito colateral dessa técnica é que, quando você iniciar o programa a partir de uma interface gráfica, haverá ainda um console. Ele irá piscar na tela momentaneamente e, em seguida, desaparecer.

A parte no artigo de Junfeng sobre o uso de Editbin para mudar a bandeira de modo console do programa é um arenque vermelho, eu acho. Seu compilador ou ambiente de desenvolvimento deve fornecer uma definição ou opção de controle que tipo de binário que ele cria. Não deve haver nenhuma necessidade de modificar alguma coisa depois.

A linha de fundo, então, é que você pode ter dois binários, ou você pode ter um lampejo momentâneo de uma janela do console . Depois de decidir qual é o mal menor, você tem a escolha de implementações.

* digo não-console em vez de GUI , pois caso contrário, é uma falsa dicotomia. Só porque um programa não tem um console não significa que ele tem um GUI. Um aplicativo de serviço é um excelente exemplo. Além disso, um programa pode ter um console e janelas.

Outras dicas

Confira o blog de Raymond sobre este tema:

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

A sua primeira frase: "Você não pode, mas você pode tentar fingir"

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

Basta verificar os argumentos de linha de comando antes de o material WinForms Application..

Devo acrescentar que em .NET é ridiculamente fácil de simplesmente fazer um console e projetos GUI na mesma solução que partilham todas as suas assembléias, exceto principal. E, neste caso, você poderia fazer a versão de linha de comando simplesmente lançar a versão GUI se ele for iniciado sem parâmetros. Você poderia obter um console piscar.

Há uma maneira fácil de fazer o que quiser. Estou sempre usá-lo ao escrever aplicativos que devem ter tanto um CLI e uma GUI. Você tem que definir o seu "OutputType" para "ConsoleApplication" Para que isso 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
        });
      }
    }
  }

Eu acho que a técnica preferida é o que Rob chamado devenv técnica de usar dois executáveis: um lançador de ".com" e ".exe" original. Esta não é tão complicado de usar se você tem o código clichê para trabalhar com (ver link abaixo).

Os usa a técnica de truques para ter que ".com" ser um proxy para o stdin / stdout / stderr e lançar o mesmo nome de arquivo .exe. Isto dá o comportamento de permitindo que o programa pré-forma em um modo de linha de comando quando chamado formam um console (potencialmente somente quando certos argumentos da linha de comando são detectados), enquanto continuam sendo capazes de lançamento como uma aplicação gráfica livre de um console.

I hospedado um projeto chamado dualsubsystem no Google Code que as atualizações uma solução CodeGuru velho desta técnica e fornece o código fonte e de trabalho exemplo binários.

Aqui está o que eu acredito ser a solução simples # .NET C para o problema. Só para reformular o problema, quando você executar o console "versão" do aplicativo a partir de uma linha de comando com um switch, o console continua esperando (não retornar ao prompt de comando e o processo continua correndo), mesmo se você tem um Environment.Exit(0) no final do seu código. Para corrigir isso, antes de chamar Environment.Exit(0), chamar este:

SendKeys.SendWait("{ENTER}");

Em seguida, o console recebe a final tecla Enter ele precisa para voltar ao prompt de comando e as extremidades do processo. Nota:. Não chame SendKeys.Send(), ou o aplicativo irá falhar

É ainda necessário chamar AttachConsole() como mencionado em muitos lugares, mas com este eu recebo nenhuma cintilação janela de comando ao lançar a versão WinForm do aplicativo.

Aqui está o código inteiro em um aplicativo de exemplo que eu criei (sem o 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 ajude alguém de também passar dias sobre este problema. Obrigado pela dica ir para @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);
    }

Eu escrevi-se uma abordagem alternativa que evita o flash console. Veja como criar um programa do Windows que funciona tanto como uma aplicação GUI e de console .

Executar AllocConsole () em um construtor estático funciona para mim

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top