How do I write a program that can be run either as a console or a GUI application?


You can't, but you can try to fake it.

Each PE application contains a field in its header that specifies which subsystem it was designed to run under. You can say IMAGE_SUBSYSTEM_WINDOWS_GUI to mark yourself as a Windows GUI application, or you can say IMAGE_SUBSYSTEM_WINDOWS_CUI to say that you are a console application. If you are GUI application, then the program will run without a console.

The subsystem determines how the kernel prepares the execution environment for the program. If the program is marked as running in the console subsystem, then the kernel will connect the program's console to the console of its parent, creating a new console if the parent doesn't have a console. (This is an incomplete description, but the details aren't relevant to the discussion.) On the other hand, if the program is marked as running as a GUI application, then the kernel will run the program without any console at all.

There are some people who want to write what I call an "opportunistic" console program. These are programs that will use the console of their parent if available, but do not want a console created for them if not. The kernel doesn't support this type of program, but that hasn't stopped some people from coming up with clever workarounds. Note that if such a program type were introduced, it would create problems with programs such as cmd.exe and Explorer which change their behavior depending on what subsystem a program belongs to. These programs would have to be modified to understand a new pseudo-subsystem called "both".

I've also seen requests for what I call a "dynamic" console program. These are programs that want to decide at run time whether they want a console or not. For example, a program might want to run with a console only if a special command line switch is passed. To do this the kernel would have to have psychic powers: It would somehow have to know whether to hook up a console to your program or not (which happens before the program begins executing) based on something that happens in the future (when your program actually runs and parses its command line and decides whether it wants to run as a console or a GUI program). Again, people have come up with workarounds (see earlier link).

Comments (29)
  1. John says:

    If you’re running XP/2003, there is the <a href="http://msdn.microsoft.com/en-us/library/ms681952(VS.85).aspx">AttachConsole</a> function.  Couldn’t this be combined with a method like <a href="http://support.microsoft.com/kb/105305">this</a&gt; or <a href="">http://www.halcyon.com/~ast/dload/guicon.htm</a&gt; to achieve the desired affect?  I’ve used the last example a few times, but it allocates a new console instead of using the parent’s console.

  2. Tom says:

    >>To do this the kernel would have to have psychic powers<<

    I don’t see why that is necessary.  Why can’t there be a third type – “dynamic” – where creating or attaching to the console and whether to release the CMD prompt to accept the next command is made by the application at run-time through API calls.  Many of the API calls are actually already available (AttachConsole, etc.).

    This type of usage is actually quite common.  Microsoft has a number of executables that emulate this behavior (e.g., Visual Studio), and if you search the ‘net, there are lots of people trying to do the same with their apps.

    [Before the first instruction in the process executes, kernel needs to set up the execution environment. It needs to decide whether to set the process’s stdin/stdout to NULL or to create a console and set the stdin/stdout to that console. But it has to wait until the process calls “IHaveDecidedWhetherIWantAConsole()”. Program execution requires execution environment. Execution environment includes setting stdin/stdout. Knowing what stdin/stdout should be requires program execution. Without psychic powers, you have a deadlock. That’s the entire point of my article, which I apparently did a bad job of explaining. -Raymond]
  3. qwertz says:

    These are programs that want to decide at run time whether they want a console or not.

    Isn’t that exactly what AllocConsole and AttachConsole are there for?

    http://msdn.microsoft.com/en-us/library/ms681944(VS.85).aspx

    But maybe the point is that there are actually programs that want decide during run time whether a console should have existed during startup?

  4. Me says:

    And yet Unix has no problem doing this.  All programs are effectively console programs, but they can choose not to use stdin and stdout.  In fact, there are ways to determine at runtime if a program is connected to a terminal and perform different actions.  So if a program is started from the command line, stdin would be connected to a tty and the program can take a different path than if stdin was connected to /dev/null, or if it was connected to a file, or whatever.

  5. Neil says:

    I like the devenv solution. The problem with the ildasm solution is that the application will never see WM_QUERYENDSESSION messages.

    ildasm may have been originally written with an older compiler/linker which didn’t automatically work with both (w)WinMain and (w)main entry points, which explains the use of editbin.

  6. laonianren says:

    Junfeng’s description of how devenv.com works is incorrect.

    devenv.com is a general purpose console-mode stub application.  When it runs it creates three pipes to redirect the console’s stdin, stdout and stderr.  It then finds its own name (usually devenv.com), replaces the ".com" with ".exe" and launches the new app (i.e. devenv.exe) using the read end of the stdin pipe and the write ends of the stdout and stderr pipes as the standard handles.  Then it just sits and waits for devenv.exe to exit and copies data between the console and the pipes.

    Thus even though devenv.exe is a gui app it can read and write the "parent" console using its standard handles.

    And you could use devenv.com yourself for myapp.exe by renaming it to myapp.com.  But you can’t in practise because it belongs to MS.

  7. Leo Davidson says:

    I’ve tackled this a little differently in the past. I wrote a small, generic "GUI runner" console exe. You give that the path and arguments of the GUI program you want to run.

    It runs the GUI program, hooking its IO handles to pipes which connect to the console’s IO handles. It waits for the GUI program to exit so that the command-line launch doesn’t run in the background.

    The nice thing about this is it’s a small, generic exe which you can use with any GUI app. The GUI app doesn’t have to do anything special, except ensure that it doesn’t display any UI if given a command line.

    As someone who came from the Amiga world, where there was no distinction between a command-line app and a GUI app, I still find just about everything related to the console subsytem in Windows to be horrible.

    The past cannot be changed but a new, good, well-designed console / command-line shell could be added to Windows. I hoped that was what PowerShell would be but it turned out to run within the same old console subsystem and thus solved nothing. Ah well!

  8. Leo Davidson says:

    Based on laonianren’s description (which I think was posted as I wrote my previous post), I guess my method isn’t that different after all. :)

  9. manicmarc says:

    How does the shutdown command work? It has an option “/i” that displays a GUI.

    It doesn’t create a new process (my first guess), the command prompt window stays there waiting, and pressing CTRL + C in the command window closes the program, and therefore the GUI window.

    [Yup, that’s a console application. -Raymond]
  10. Chriso says:

    Dunno, but aren’t such "workarounds" potential backward compatibility problems in future Windows  versions?

  11. It is nice to hear from an authori^H^H^H^H^H^H^H guy who usually seems to know what he’s talking about that there is no easy solution for this one.

    It appears that it would ALMOST make sense to "inherit a console if present" — but how is cmd.exe then going to know whether it should wait for the process to terminate before emitting a new command prompt? If the process decides that today it wants to be pure GUI, the user might want his shell back before it terminates.

    Then one would need to kludge on some ad-hoc APIs to let CMD know whether and when the process has closed its console handle (which I can imagine would not integrate nicely with how the kernel otherwise handles handles).

    The reason why this is not a problem on Unix is that Unix shell users accept that they have to predict the behavior of their application and tell the shell what to do (by adding an ampersand on the command line for GUI programs). Starting to require this in Windows would probably be perceived as a compatibility break by many.

  12. Bovine says:

    The distributed.net client (dnetc) is another example of something that uses the devenv.com method.  It includes a dnetc.com frontend (see w32cuis.c) that creates pipes before launching dnetc.exe to do the real work.  If dnetc.exe is launched directly, you get a custom GUI that happens to resemble a console-like application but with pull-down menus other GUI enhancements.

  13. Cheong says:

    I don’t know whether it’s appropiate to do this way, but in my previous company, I’ve written a C# program typed "service" and detect whether it’s launched from console in the main() function. If it’s not attached then run Application.Run(new frmMain) from there… And it does seems to work.

  14. If we could just set some new flag to suppress the creation of a *new* console on launch, everything would be fine. At present, a process either *always* has a console (even if that means creating a new one) or *never* has it (even if that means detaching from the existing one) – an intermediate option, inheriting an existing console if present but never creating one, would solve the last irritating glitch of a console appearing then disappearing immediately.

    (Alternatively, having CSRSS.EXE delay opening the new console window until it’s actually needed would solve this even for existing applications: discard it unopened upon a FreeConsole call, avoiding the transient appearance where it’s unwanted.)

    [The flag would solve many cases but not all. Consider the app that wants to run either in GUI mode or console mode (creating a console if necessary) based on a command line switch. -Raymond]
  15. Tom says:

    Does anybody know how to generate a ".com" output file using VS2008?  I’m trying to work up a solution similar to what devenv.com/devenv.exe does.

  16. Dean Harding says:

    Henning: The application would call FreeConsole if it wanted to detach itself from the console, as it does already.

    Still, I don’t think this is a must-have feature, since the "console flash" thing you get with the solution used by ildasm isn’t *that* horrible. I don’t think many non-technical people would even notice it (though I guess dual-mode applications would be targeted to technical people…)

  17. 640k says:

    This "magic" can be done by using masm (and specifying wingui in the exe header). There’s even a hello world app (wingui) that writes to the console it is started in.

  18. Worf says:

    @Tom: I doubt that devenv.com is really a DOS .COM file – COM files lack the PE header. Instead it’s a renamed standard PE EXE. The only reason to call it devenv.com is because Windows will run a .COM file first, rather than a .EXE if it finds both in a directory.

    The quirk is that a) Windows inherits the DOS prefer-.com-over-.exe, and b) that the Windows image loader doesn’t inspect extensions when loading – it’ll see if a file has a valid header first, before trying to set up a DOS COM environment and running it that way.

    I think Raymond did an article about this… apparently it dates back to DOS too.

    http://blogs.msdn.com/oldnewthing/archive/2008/03/24/8332730.aspx

    Just that devenv.com is a console Win32 app with a false extension. Or how screensavers with .scr are really also .exe in disguise.

    Now the question is, if you create a console app… can it call all the GDI functions as normal (including message pump, etc)? I think you can, and I believe I have, but I’m not sure – it was a while back.

    If so, then the only real reason to have the com/exe thing was to hide an "ugly" console window…

  19. Yuhong Bao says:

    BTW, Exchange 2007’s setup.com uses the same trick as devenv.com. But why use this trick when there is AttachConsole() and AllocConsole?

  20. John says:

    AttachConsole is only available starting with XP, and AllocConsole does not attach to the parent console (which is the whole point of making it run as a console application).

  21. sandman says:

    @Henning

    Most unix GUI programs need ‘&’ in that usage, because their authors don’t think they need to detach from the console.

    It is possible (under unix) to close you stdio/err/out, and respawn yourself via fork. Then exit the original process – this is normally called daemonising.

    Moving back on topic, this is akin to want the Windows kernel does for GUI Subsytem programs. (There are differences though). Unfortunately it depends on the fork() syscall for which windows has no alternative – although I believe Cygwin can fake it somehow.

    The nearest equivalent for this trick under windows is the ildasm solution. The reason unix apps don’t suffer from the windows flicker issue – is that the kernel doesn’t set an environment up for new processes in unix in the same way. They just inherit form their parent.

    The downside of this is starting a console app in unix from inside a windowing context is that the consoles output may never reach the user since stdout may only be directed to /dev/null. (stderr is often direct to a hidden file). Of course if the GUI launcher knows an application is console app it can start a console/terminal window for it.

    It an good example of the different approaches taken.

  22. Ben Cooke says:

    Leo,

    I may be remembering wrong, but in the Amiga world I seem to remember that it was similar to the UNIX model where a GUI app is just like any other app but if you launch it from Workbench its output handles aren’t attached to anything.

    This means that if you wanted to launch a command line app from Workbench you needed a wrapper that would open a console and then spawn the command line tool… which is basically what Windows is doing, but doing it in the kernel rather than needing the intermediate app.

    UNIX desktops generally follow a similar principle to how command-line tools were launched in Workbench, but desktop environments usually make life a little easier by providing a "Run in Terminal" checkbox that launches your default terminal.

    One thing that was neat on the Amiga though was that they took "everything is a file" to an even greater extreme than UNIX, with the CON: device that would open an on-screen console, and the SPEAK: device that would route anything written to it through the text-to-speech engine. This meant that the Amiga equivalent of AllocConsole was just fopen(f, "CON:").

  23. Kujo says:

    Consider the app that wants to run either in GUI mode or console mode (creating a console if necessary) based on a command line switch.

    It’s just up to the app to let go of the console if the command-line switch is missing or create one if needed. At this point, I think that would mean another devenv.com-style trick.

    But why use this trick when there is AttachConsole()

    In addition to the OS support John mentions, you also want cmd to wait for your program to finish (when it’s run from cmd.)

    The application would call FreeConsole if it wanted to detach itself from the console, as it does already.

    Unfourtunately, cmd will still wait for it to finish.  I don’t think there’s any extant mechanism for cmd to tell the FreeConsole has happened.

    So we have two feature requests: An "inherit" application mode and something in the console subsystem that cmd would use to tell when the child calls FreeConsole.  And this "inherit" application mode can’t break backwards compatibility with older OSes, debuggers, 4nt, etc.  While I’ve wanted this power for ages, I’m not holding my breath :)

    (I seem to be having trouble submitting this.  I hope I don’t end up being "that guy" with five copies of his post.)

  24. Try this (VB code):

     ‘ Hide the console window if there are no command line args

     Dim myHandle As IntPtr

     myHandle = Process.GetCurrentProcess.MainWindowHandle

     Dim hwnd As Integer = myHandle.ToInt32

     Dim nCmdShow As Integer = SW_HIDE

     Dim bShow As Integer = ShowWindow(hwnd, nCmdShow)

     myFrm.Show() : Application.DoEvents()

  25. Sarath says:

    That’s a great post. I found MSDEV is actually loading msdev.com …. I think it’s loading the COM file because of the priority it get from .COM over .exe extension. Am I correct? Otherwise how it’s possible to determine to launch .com or .exe program?

  26. Ben Voigt [C++ MVP] says:

    [The flag would solve many cases but not all. Consider the app that wants to run either in GUI mode or console mode (creating a console if necessary) based on a command line switch. -Raymond]

    if (console_mode_selected)

     AllocConsole(), puts(“problem solved”);

    You need a better counter-example.

    [You need a better example. In your case, the output doesn’t go into the parent process’s console, which is what most people want when they run a console app. Imagine if “echo” worked this way. -Raymond]
  27. Jin says:

    I done that for few applications at work. I used C#.NET, where I create a windows app looking for number of arguments. If argument exists (proper ones) then run it as console. Otherwise follow the normal route and bring up the GUI.

    Assumption is that the argument I am looking for should be provided only from the console mode.

  28. Cameron says:

    In a similar vein, is there no way to make Visual Studio route the I/O of a console app to an "Console Output" window like IDEs on Mac OS X and Unix do, (rather than starting a console window…)?

Comments are closed.

Skip to main content