Using the console in a GUI app in windows, only if its run from a console
Asked Answered
N

4

7

My application is a GUI app that has helpful (though optional) information through the terminal (via cout).

In Windows I either have a console appear (by compiling as a console app, or allocating it dynamically) or I don't.

My intention is to make use of the console IF it is being run from the console, but ignore the console completely if it was not. (Essentially what happens in Linux and OS X).

I do not wish to redirect to a file (and in the case of using cin, this is not a viable solution anyway).

Is there a way to attach a GUI app in Windows to the console it is run from, if and only if it is run from a console?

Nullity answered 11/4, 2013 at 15:31 Comment(5)
possible duplicate of How to know whether we are in a console or a windowed app?Carswell
Do you just want to print to the console if your app is being run via one? You can use cout, and if the program was not launched from a console, it will just be ignored.Dielle
@slavik262 I do not spend much time in Windows, but from previous experience printing to the console gets ignored if it is not a console app. Does it matter cout, stdout, printf, etc...?Nullity
See https://mcmap.net/q/671718/-how-do-i-write-to-stdout-from-an-mfc-program - the question is specific to MFC but the answer is only Win32 calls. I've tested it from a Windows app and it works.Doubleteam
Probably helpful: https://mcmap.net/q/671719/-where-do-writes-to-stdout-go-when-launched-from-a-cygwin-shell-no-redirection/103167Bocock
L
20

and in the case of using cin, this is not a viable solution anyway

This is the killer detail in your question. It is simple on paper, just first call AttachConsole(ATTACH_PARENT_PROCESS) to try to attach to an existing console. That will fail when your program got started from a GUI program like Explorer or a desktop shortcut. So if it returns FALSE then call AllocConsole() to create your own console.

Using cin is a problem however. The command processor pays attention to your EXE and checks if it is console mode app or a GUI app. It will detect a GUI app in your case and then doesn't wait for the process to complete. It displays the prompt again and waits for input. You will then also wait for input but you'll lose, the command processor got there first. Your output is also intermingled with the command prompt, the easy problem to solve.

There's a simple workaround for that, your user should start your program with start /wait yourapp to tell the command processor to wait for the process to complete. Problem is: nobody ever uses that. And the user will not realize what happens when they type input, intending it to go into your program but it is actually interpreted by the command processor. Producing a mystifying error message or formatting the hard drive.

Only two good ways to solve this unsolvable problem. Either build your program as a console mode app and call FreeConsole() when you find out you want to display a GUI. Or always call AllocConsole(). These are not great alternatives. The first approach is the one used by the Java JVM on Windows. One of the oldest bugs filed against the JVM and driving Java programmers completely batty from the flashing console window.

The third alternative is the only decent one, and the one you don't want, create another EXE that will always use the console. Like Java does, javaw.exe vs java.exe.

A trick is possible, you can rename that file from "yourapp2.exe" to "yourapp.com". It will be picked first when the user types "yourapp" at the command line prompt, a desktop shortcut can still point to "yourapp.exe". Visual Studio uses this trick, devenv.com vs devenv.exe.

Leolaleoline answered 11/4, 2013 at 17:36 Comment(2)
+1 on particularly blistering form today. I think Java and Python both opt for the separate executables option javaw and pythonw. It's the only way to stay sane.Corduroy
While not what I was hoping for, this is the best, most complete answer that addresses all of the points. I wish it were doable without such sacrifices. Accepted, thank you.Nullity
M
3

You can check CONSOLE_SCREEN_BUFFER_INFO (via GetConsoleScreenBufferInfo) on startup to determine if you've been run from within an existing console. If the buffer's position is 0,0, you were run from outside of the console. For details, see this Microsoft Knowledgebase Article which describes the process.

In order for this to work, you need to compile your application as a console application (using /SUBSYSTEM:CONSOLE), and then detach yourself from the console if the application started a new console (buffer at 0,0). This will cause the program to properly "attach" to the calling console when launched from a command line.

Magazine answered 11/4, 2013 at 15:35 Comment(7)
While this is definitely part of the answer I need, it is not complete as (and I am recalling from memory of previously porting apps to Windows) when I print to a console, and I run my GUI app from in a console in Windows, the print commands just...vanish.Nullity
@Nullity You need to make your application a console application and free the console if you don't want it - edited my answer with more details.Magazine
@latreides, I'm afraid he's right - if you don't create it as a console app in the first place, the necessary startup code to connect cin and cout to the console is skipped. But to know if there's a console you should use GetConsoleWindow instead of GetConsoleScreenBufferInfo.Doubleteam
So let me make sure I am understanding the process. Make a console app, check to see if its being run in a console, if so, we are good (it will use the existing console), if not, free the console (because I dont want it to pop up a console). Is that correct? If so, will the console (the one I dont want) "show" before its freed?.Nullity
@latreides, yes you're right - if you start a console app you'll have a console, whether it was run from an existing console or Windows created a new one for you. The only way to know then if it was newly allocated is to look at the position as stated in this answer. You can't prevent a console from flashing up temporarily. But see the link I posted on your question to a different answer.Doubleteam
@MarkRansom the console flashing up temporarily is not acceptable behavior. Is there no way to achieve this result in a different way? I have read the link you posted, will those solutions also "flash"?Nullity
It is possible to attach stdout et al to the console after the fact using /SUBSYSTEM:WINDOWS, but I'm not sure if it is possible to use this method with the parent process (the magic needed to make this work might happen in the loader, which won't do it for a /SUBSYSTEM:WINDOWS program).Swec
I
0

As others have pointed out you have to create a console app and a window app. So, you'd end up with console.exe and app.exe. To make it less obvious at the command-line, you can take advantage of the PATHEXT trick like devenv does. cmd.exe treats a file as a command if its extension is in the PATHEXT environment variable. COM is there by default so you could rename console.exe as app.com, allowing the command app to start the console app attached to the current console.

Note: Of course, a console app can show a GUI if desired.

The difference in the build between app.com and app.exe depends on your build system but it could just be the one attribute that sets the output type. With msbuild (for .vcxproj files), it's just a matter of another build configuration.

Iverson answered 13/4, 2013 at 12:0 Comment(0)
S
0

you can create an application in console that get a line using argc and prints it;

////
int main(int argc, char *argv[])
{
    //here print argv....using cout or printf
}

save the file as console.exe in the folder of your app. now in your app if you want to see any line in console you can call the command

system("console.exe this is the line i want to print and see in console");
Selene answered 31/7, 2013 at 10:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.