Can a single executable be a console and graphical application?

I want to create a C # program that can be run as a CLI or GUI application depending on which flags are passed to it. It can be done?

I found these related questions, but they do not quite match my situation:

  • How to write a console in a GUI application
  • How to get console output in C ++ with a Windows program?
+72
user-interface c # command-line-interface
Jan 29 '09 at 21:32
source share
9 answers

Jdigital's answer points to Raymond Chen's blog , which explains why you cannot have an application that has a console and non-console * program: the OS needs to know which subsystem to use before starting the program. Once the program has started, it is too late to return and request another mode.

Cade's answer points to an article on launching a .Net WinForms application with a console . It uses the AttachConsole calling technique after starting the program. This allows the program to write back to the console window of the command line that launched the program. But the comments in this article point to what I consider a fatal flaw: the child process does not actually control the console. The console continues to accept input on behalf of the parent process, and the parent process does not know that it should wait for the child process to complete before using the console for other purposes.

Chen's article points to Junfeng Zhang's article, which explains several other techniques .

The first thing that uses devenv. This actually works two programs. One of them is devenv.exe, which is the main program with a graphical interface, and the other is devenv.com, which processes tasks in console mode, but if it is not used in a console mode, it redirects its tasks to devenv.exe and exits. The technique relies on the Win32 rule that com files are selected in front of exe files when entering a command without a file extension.

There is a simpler variation than the Windows Script Host. It provides two completely different binaries, wscript.exe and cscript.exe. Similarly, Java provides java.exe for console programs and javaw.exe for non-console programs.

The second Junfeng technique is that uses ildasm. He cites the process that ildasm went through when it started in both modes. Ultimately, this is what it does:

  1. The program is marked as binary in console mode, so it always starts from the console. This allows input and output redirection to work as usual.
  2. If the program does not have command-line options in console mode, it restarts itself.

It is not enough just to call FreeConsole so that the first instance FreeConsole to be a console program. This is because the process that launched the program, cmd.exe, β€œknows” that it launched the program in console mode and expects the program to stop. A call to FreeConsole would stop ildasm when using the console, but would not force the parent process to start working with the console.

Thus, the first instance restarts itself (with an additional command line option, I suppose). When you call CreateProcess , you need to try two different flags: DETACHED_PROCESS and CREATE_NEW_CONSOLE , each of which ensures that the second instance will not be connected to the parent console. After this, the first instance may complete and allow the command line to resume processing commands.

A side effect of this method is that when you start the program from the GUI, there will still be a console. It will flash on the screen for a moment, and then disappear.

I think part of the Junfeng article about using editbin to change the program console mode flag is red herring. Your compiler or development environment should provide a parameter or option to control the type of binary that it creates. There should not be a need to change anything later.

The bottom line is that you can have two binary files or a short-term flicker of the console window . Once you decide what lesser evil you have the choice of realization.

* I say not console instead of GUI, because otherwise it is a false dichotomy. The fact that the program does not have a console does not mean that it has a graphical interface. The service application is a prime example. The program may also have a console and windows.

+85
Jan 29 '09 at 23:54
source share
β€” -

Check out Raymond's blog on this topic:

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

His first sentence: "You cannot, but you can try to fake it."

+10
Jan 29 '09 at 21:40
source share

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

Just check the command line arguments before the WinForms Application. files Application. .

I must add that in .NET it is very easy to create console and graphic projects in the same solution that use all their assemblies except the main one. And in this case, you can make the command line version just run the GUI version if it is launched without parameters. You will receive a flashing console.

+6
Jan 29 '09 at 21:36
source share

There is an easy way to do what you want. I always use it when writing applications that should have both a CLI and a graphical interface. You must set your "OutputType" to "ConsoleApplication" for this to work.

 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 }); } } } 
+5
Mar 16 '13 at 11:45
source share

I think the preferred method is that Rob called the devenv method using two executables: the launcher ".com" and the original ".exe". It is not so difficult to use if you have the template code to work with (see the link below).

This technique uses tricks so that ".com" is a proxy for stdin / stdout / stderr and runs a file with the same name .exe. This gives behavior that allows the program to preform in command line mode when the form is called from the console (perhaps only when certain command line arguments are detected), while you can still run the GUI application without the console.

I had a project called dualsubsystem on Google Code that updates the old solution for this method and provides a source of code and executables.

+3
Jul 05 '09 at 17:40
source share

Here is what I consider a simple .NET C # solution. Just to repeat the problem, when you launch the console β€œversion” of the application from the command line using the switch, the console continues to wait (it does not return to the command line and the process continues to work), even if you have Environment.Exit(0) at the end of your code. To fix this, before calling Environment.Exit(0) call this:

 SendKeys.SendWait("{ENTER}"); 

Then the console receives the final Enter key, which must be returned to the command line, and the process ends. Note. Do not call SendKeys.Send() , or the application crashes.

It is still necessary to call AttachConsole() , as mentioned in many posts, but at the same time I do not see the flicker of the command window when starting the version of the WinForm application.

Here is all the code in the sample application that I created (without WinForms code):

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

Hope this helps someone and spend days on this issue. Thanks for the tip. @dantill.

+3
Sep 18 '15 at 8:52
source share
 /* ** 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 <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); } 
+2
Jan 27 '13 at 17:50
source share
+1
May 12 '13 at 20:54
source share

Running AllocConsole () in a static constructor works for me

0
Nov 15 '14 at
source share



All Articles