Um executável pode ser um console e um aplicativo GUI?

Eu quero fazer um programa c # que pode ser executado como um aplicativo CLI ou GUI dependendo de quais sinalizadores são passados ​​para ele. Isso pode ser feito?

Eu encontrei essas questões relacionadas, mas elas não cobrem exatamente a minha situação:

  • Como gravar no console em um aplicativo GUI
  • Como obtenho saída do console em C ++ com um programa do Windows?

A resposta de Jdigital aponta para o blog de Raymond Chen , o que explica por que você não pode ter um aplicativo que seja tanto um programa de console quanto um não-console * : O SO precisa saber antes que o programa comece a usar qual subsistema usar. Uma vez que o programa começou a correr, é tarde demais para voltar e solicitar o outro modo.

A resposta de Cade aponta para um artigo sobre a execução de um aplicativo .Net WinForms com um console . Ele usa a técnica de chamar AttachConsole depois que o programa começa a ser executado. Isso tem o efeito de permitir que o programa grave de volta na janela do console do prompt de comando que iniciou o programa. Mas os comentários nesse artigo apontam o que eu considero ser uma falha fatal: o processo filho realmente não controla o console. O console continua aceitando a input em nome do processo pai, e o processo pai não está ciente de que deve esperar que o filho termine a execução antes de usar o console para outras coisas.

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

O primeiro é o que o devenv usa. Ele funciona realmente com dois programas. Um é o devenv.exe , que é o principal programa GUI, e o outro é o devenv.com , que lida com tarefas no modo console, mas se for usado de maneira não-console, ele encaminha suas tarefas para devenv.exe e sai. A técnica depende da regra do Win32 com a qual os arquivos com são escolhidos antes dos arquivos exe quando você digita um comando sem a extensão do arquivo.

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

A segunda técnica de Junfeng é o que ildasm usa. Ele cita o processo pelo qual o autor do ildasm passou ao executá-lo nos dois modos. Finalmente, aqui está o que ela faz:

  1. O programa é marcado como um binário do modo de console, então ele sempre começa com um console. Isso permite que o redirecionamento de input e saída funcione normalmente.
  2. Se o programa não tiver parâmetros de linha de comando no modo de console, ele será reativado.

Não é suficiente simplesmente chamar o FreeConsole para fazer com que a primeira instância deixe de ser um programa de console. Isso porque o processo que iniciou o programa, o cmd.exe , “sabe” que ele iniciou um programa em modo de console e está aguardando a execução do programa. Chamar o FreeConsole faria com que o ildasm parasse de usar o console, mas isso não faria com que o processo pai começasse a usar o console.

Portanto, a primeira instância se reinicia (com um parâmetro extra de linha de comando, suponho). Quando você chama CreateProcess , há dois sinalizadores diferentes para tentar, CREATE_NEW_CONSOLE e CREATE_NEW_CONSOLE , qualquer um dos quais garantirá que a segunda instância não será anexada ao console pai. Depois disso, a primeira instância pode finalizar e permitir que o prompt de comando retome os comandos de processamento.

O efeito colateral dessa técnica é que, quando você inicia o programa a partir de uma interface GUI, ainda haverá um console. Ele piscará na canvas momentaneamente e depois desaparecerá.

A parte no artigo de Junfeng sobre o uso do editbin para alterar o flag do modo de console do programa é um arenque vermelho, eu acho. Seu compilador ou ambiente de desenvolvimento deve fornecer uma configuração ou opção para controlar o tipo de binário que ele cria. Não deve haver necessidade de modificar nada depois.

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

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

Confira o blog de Raymond sobre esse assunto:

http://blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx

Sua primeira frase: “Você não pode, mas você pode tentar fingir.”

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

Basta verificar os argumentos da linha de comando antes do Application. WinForms Application. coisa.

Devo acrescentar que no .NET é RIDICULAMENTE fácil simplesmente fazer um console e projetos de GUI na mesma solução que compartilham todos os seus assemblies, exceto o main. E nesse caso, você poderia fazer a versão da linha de comando simplesmente iniciar a versão da GUI se ela for lançada sem parâmetros. Você teria um console piscando.

Existe uma maneira fácil de fazer o que você quer. Estou sempre usando isso quando escrevo aplicativos que devem ter uma CLI e uma GUI. Você precisa definir seu “OutputType” como “ConsoleApplication” para que isso funcione.

 class Program { [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")] private static extern IntPtr _GetConsoleWindow(); ///  /// The main entry point for the application. ///  [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 chamou de técnica devenv de usar dois executáveis: um lançador “.com” e o “.exe” original. Isso não é complicado de usar se você tiver o código clichê para trabalhar (veja o link abaixo).

A técnica usa truques para que o “.com” seja um proxy para o stdin / stdout / stderr e inicie o mesmo arquivo .exe. Isso fornece o comportamento de permitir que o programa seja pré-formado em um modo de linha de comando quando chamado de formar um console (possivelmente apenas quando certos argumentos de linha de comando forem detectados) e ainda ser capaz de iniciar como um aplicativo GUI livre de um console.

Eu hospedei um projeto chamado dualsubsystem no Google Code que atualiza uma antiga solução codeguru dessa técnica e fornece o código-fonte e os binários de exemplo de trabalho.

Aqui está o que eu acredito ser a solução simples do .NET C # para o problema. Apenas para reafirmar o problema, quando você executa o console “versão” do aplicativo a partir de uma linha de comando com um comutador, o console continua aguardando (ele não retorna ao prompt de comando eo processo continua sendo executado) mesmo se você tiver um Environment.Exit(0) no final do seu código. Para corrigir isso, antes de chamar Environment.Exit(0) , chame isso:

 SendKeys.SendWait("{ENTER}"); 

Em seguida, o console obtém a chave Enter final necessária para retornar ao prompt de comando e o processo é encerrado. Nota: Não chame SendKeys.Send() , ou o aplicativo irá falhar.

Ainda é necessário chamar AttachConsole() como mencionado em muitos posts, mas com isso eu não recebo nenhuma janela de comando flicker ao iniciar a versão do WinForm do aplicativo.

Aqui está o código inteiro em um aplicativo de amostra 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 com este problema. Obrigado pela dica de 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), ie 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  #include  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 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 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 uma abordagem alternativa que evita o flash do console. Consulte Como criar um programa do Windows que funcione como uma GUI e um aplicativo de console .

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