2

I've got a Windows application (written in C, compiled with MSVC Express edition, 32-bit mode), which has two main modes of operation:

  • Windowed mode -- create a window, and draw stuff in it (namely, a fractal).
  • Benchmark mode -- when run with --benchmark as an argument, don't make a window but just print some benchmark statistics to stdout.

During development I've compiled as a Console app, and used SDL to create the window and perform other GUI functions. So benchmark mode runs fine (no window is created), and graphical mode just has a lingering console window.

However for my release compilation I've enabled the Windows subsystem instead of Console. (As explained in this question). This works great except I've suddenly discovered I can't run benchmarks any more. :o

I'm just wondering, is there a way for an application to choose at run time (e.g. based on the command line it's given) which kind of subsystem behaviour to use?

I've done some experimentation with EXE files in Windows (explorer, notepad, winword) and none of them seem to print anything to the console when run with an argument like "/?" (which most Windows console apps support). So it doesn't look like it, but I thought it's worth asking here in case there's a special trick.

Update. It looks like, no, you can't. Thanks for the answers guys.

Additional academic question. Does this mean that the subsystem choice is marked in the EXE header, and it's the operating system that examines this and sets up the Window or connects it to the console it's run from? I don't know much about EXE loading, but I would be curious to learn a few details here.

Conclusion. I think there are four good solutions (plus two semi-solutions, making five total :p) to choose from:

  1. Use the console subsystem, but use FreeConsole when running in GUI mode.
  2. Use the windows system, and use AllocConsole when running in benchmark mode. Not perfect if fractal.exe is run from an existing console, so I'll count this as half a solution ;-).
  3. Just have one executable for each subsystem, fractal.exe and fractalgui.exe.
  4. Have two (or more) executables, one of which does the work and passes it to the other to be displayed on the console or in a Window as appropriate. Needs some thought on how to divide the programs and how to communicate between them.
  5. Another half-solution: have fractalgui.exe print the benchmark to standard out, and pipe that to a utility that will simply print it.

I haven't yet chosen, but I'm leaning towards #3.

Thanks to Matteo and smerlin for the ideas!

Community
  • 1
  • 1
Edmund
  • 10,533
  • 3
  • 39
  • 57
  • 1
    I remember seeing an article about it, and, long story short, you can't. You can simulate it in some way by using `AllocConsole` and some magic, but it still doesn't work like it should (the process doesn't inherit the parent console when launched). – Matteo Italia Nov 06 '11 at 22:23
  • 1
    Re: Matteo's comment. The article is here: http://www.halcyon.com/~ast/dload/guicon.htm – rushman Nov 06 '11 at 22:40
  • @rushman: yes, it was that one plus [this](http://blogs.msdn.com/b/oldnewthing/archive/2009/01/01/9259142.aspx) and [this](http://blogs.msdn.com/b/junfeng/archive/2004/02/06/68531.aspx), which are also linked in [this](http://stackoverflow.com/questions/493536/can-one-executable-be-both-a-console-and-gui-app) answer. – Matteo Italia Nov 06 '11 at 22:47
  • Yes I saw something called `AllocConsole`, but I suspected it would work like that. – Edmund Nov 06 '11 at 22:55

3 Answers3

2

There is no way a application can choose her subsystem at runtime (well there are some really ugly workarounds, but those are full of quirks).

Then general solution for this problem is to have a console application, which starts your gui application if necessary

For your benchmark case, it would just print your benchmark statistics.

example setup:
- fractalgui.exe (subsystem: windows)
- fractal.exe (subsystem: console)

* the shortcut on the user desktop links to your fractalgui.exe
* if the user starts fractal.exe from the console, fractal exe starts fractalgui.exe
* if the user starts fractal.exe --benchmark, it either does the benchmark itself (if its possible to add this benchmark logic to another executable) and prints the information directly to console, or - if thats not possible - it will need to start fractalgui.exe --nogui --benchmark. The tricky case here is to get your output from fractalgui.exe to fractal.exe, so you can print it on the appropriate console. There are several ways to do this, e.g. named pipes (there are ways to start fractalgui.exe in a way, that you can just use stdout / cout there, and the data will be piped to the stdout of fractal.exe, but i dont recall how excactly this works anymore (edit: maybe this works)). The easiest way would be to start fractalgui.exe --nogui --benchmark > mylogfile and then print mylogfile after fractalgui.exe finished (since stdout/cout of fractalgui.exe will work if the output is redirected to a file), however you wont get "live" output, since all the output will be printed on the console when fractalgui.exe is already finished.

smerlin
  • 6,446
  • 3
  • 35
  • 58
  • 1
    The magic trick that is used by some applications (notably `devenv`, i.e. Visual Studio) to fake this behavior is to have the console application named `devenv.com` and the GUI version `devenv.exe`; the loader doesn't care about the `.com` extension (it recognizes that it's an `exe` from the header), but the command prompt, when the user writes `devenv`, starts the `.com` version (because it looks first for a `.com`, then for a `.exe`). Instead, the shortcuts for the user will point to the `.exe` (that will be the GUI version). – Matteo Italia Nov 06 '11 at 22:29
  • @MatteoItalia: yes, i just wanted to edit in this known example, but you were faster ;). As a matter of fact, you could just use devenv.com and rename it to myapplication.com and you would be done, but this would most likely violate microsofts copyrights etc. – smerlin Nov 06 '11 at 22:41
  • Thanks smerlin. I'll probably go with your workaround. Assuming I run it from a shortcut (e.g. on the Start Menu), is there a way to avoid the flicker of a console window opening and closing when it runs the windowed version? – Edmund Nov 06 '11 at 22:51
  • @Edmund: there's no flicker, from the shortcut you'll be launching directly the GUI application. – Matteo Italia Nov 06 '11 at 22:52
  • 1
    @MatteoItalia: thanks, that's an interesting trick! However .com files seem so old fashioned that I'm not sure I want to have one (even if it is an EXE in disguise). ;) Probably just cleaner to have `fractal.exe` as the console app (more obvious name to enter at the keyboard) and `fractalgui.exe` for GUI. – Edmund Nov 06 '11 at 22:54
  • @Edmund: disregarding the "old-fashioned" thing, I think that your decision is better just for the fact that it's much cleaner. By the way, you could reuse the code between the two modules by moving it all inside a common dll. – Matteo Italia Nov 06 '11 at 22:58
  • Hmm, I'm not sure I've understood smerlin's answer here. I'm "unaccepting" it for now (so the question is still open) and I'll try it out when I get home. Once I figure it out I'll reaccept :). – Edmund Nov 07 '11 at 01:28
  • Thanks! I'm not sure which solution I'll use yet but you've given me several good ones to choose from. – Edmund Nov 18 '11 at 01:38
  • One more question... if I I run something like: `fractalgui.exe | more`, will that be a clever trick to get the GUI app to print its results on a console? – Edmund Nov 18 '11 at 01:45
  • @Edmund: Yes, that works for printing on console, but it has the drawback, that it print the output when `fractalgui.exe` is finished, not while its still running. And of course it cant be used for input. – smerlin Nov 20 '11 at 17:31
  • 1
    @Edmund: just did some more tests, and `fractalgui.exe | more` will print the output whenever `std::cout` is flushed or alternatively, when the program has finished. So if you insert some flushes to `std::cout` into your code in the relevant parts, unless you already have enough, you shall be good. (I assume you use `std::cout`, if you use something different, you should use the appropriate flush method for that). – smerlin Nov 20 '11 at 17:39
  • @smerlin: thanks, that makes one more technique to choose from. I tried a few other CMD utils like TYPE, but there's nothing as simple as "read from stdin, and write it to stdout". MORE is the closest we can get. – Edmund Nov 21 '11 at 02:24
2

To add to @smerlin's answer, the other oft-seen method (cited into the articles I linked inside the comment) is to mark your application as a console application, but free the console (using FreeConsole) when you determine that you don't need it.

This is how ildasm does it, but it has the disadvantage of flashing the console for a brief moment between the start of the application and the call to FreeConsole.

Additional academic question. Does this mean that the subsystem choice is marked in the EXE header, and it's the operating system that examines this and sets up the Window or connects it to the console it's run from? I don't know much about EXE loading, but I would be curious to learn a few details here.

Yes, the loader checks the PE header and sets up everything according to the subsystem specified here.

Contrast with the *NIX approach: no executable is "special", and everyone has a working stdin/stdout/stderr; applications that want to display something will call the appropriate functions of Xlib. The drawback is that GUI applications have no clue if the application you are starting normally uses the console, so the system has to ask if you want to spawn also a terminal emulator or to discard the standard streams and just wait for it to spawn a window (obviously shortcuts store this information).

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • the other workaround is, that the executable edits its pe header, to switch the subsystem entry there, and then restarts itself. I dont know any quirks for this workaround, but it seems so damn dirty so i wouldnt even consider this. – smerlin Nov 06 '11 at 23:23
  • @smerlin: that's one of the dirtiest things I've ever seen. The big quirk is that a normal user is not supposed to have the rights to write inside the program files directory, so this would require privileges elevation just for the sake of it. Also, it shares the quirks of the other methods: if you make it GUI by default you don't inherit the console, if you make it console by default you get a flashing console. – Matteo Italia Nov 06 '11 at 23:51
0

I described a technique for achieving this in my question here.

Matteo already mentioned the .com trick, but that's only part of a viable solution.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720