PDA

View Full Version : Both a command line and GUI application at the same time?



agarny
26th January 2011, 15:21
Hi,

I have a GUI application to which I would like to add command line support. Basically, if I was, from a command prompt, to enter something like


$ ./myApp
then the GUI would just show up. However, if I was to enter something like


$ ./myApp -i myInputFile -o myOutputFile
then my application would execute in 'silence', i.e. without the GUI ever showing up.

I thought I would use the QxtCommandOptions class (from LibQxt (http://libqxt.org/)) to do this, but the only way I have got that class to work was by having a 'pure' Qt console application. As soon as the Qt application is a GUI application, then I am not able to use the class.

Any idea of what I might be doing wrong or what I would need to do get what I am after?

EDIT: I can do what I am after on Linux (and, I would assume, on Mac OS X), but not on Windows and I believe this is due to the way Windows handles console applications. Still working on finding a solution though...

HeReSY
26th January 2011, 15:23
You can check your Gui-App. If it has parameters you hide it, else you show it.

agarny
26th January 2011, 15:40
You can check your Gui-App. If it has parameters you hide it, else you show it.
Thanks, but..
I wouldn't want to have to reinvent the wheel and would therefore like to be able to use QxtCommandOptions or something similar which could handle command line options for me; and I would like to be able to output some text to the console and I haven't been able to do that using std::cout from a GUI Qt application, but then again maybe it's not supposed to work and/or it should be done in a different way with a GUI Qt application?

arnaiz
26th January 2011, 17:43
Maybe you can use hide() to hide your main window. Now you can process the input parameters as usual, but without a GUI showing up...

Is this what you are looking for?

high_flyer
26th January 2011, 17:47
Maybe its just me I don't understand what the problem is.
You application get argumens every time it starts (GUI or not):


int main(int argc, char *argv[])
{
QApplication a(argc, argv);
...
}


just parse the arguments and start the application any way you want to (with or without GUI).

If someone will call the application from the console with arguments, the you can do with them something, or with a double click on the exe, then the arguments are empty.

agarny
26th January 2011, 18:09
high_flyer, you are correct and I have got things to work the way I want on Linux (and most likely on Mac OS X too) using the following code:


int main(int pArgc, char *pArgv[])
{
QtSingleApplication app(pArgc, pArgv);

// Handle the command line options, if any

QxtCommandOptions options;

options.add("help", "Show this help text");
options.alias("help", "h");

options.parse(app.arguments());

if(options.count("help") || options.showUnrecognizedWarning())
{
options.showUsage();

return -1;
}

...

// Execute the application

return app.exec();
}
It's just that on Windows, a GUI application cannot directly write to the console (unless your application is a pure console application which mine clearly isn't) and


options.showUsage();
above will therefore never output anything while it does on Linux.

Now, I had a quick look at the Windows API and it would seem that one might be able to do work around this issue by attaching a console to the GUI application:


#ifdef Q_WS_WIN
#define _WIN32_WINNT 0x0501 // I.e. Windows XP and higher

#include <windows.h>
#endif

...

int main(int pArgc, char *pArgv[])
{
...

if(options.count("help") || options.showUnrecognizedWarning())
{
#ifdef Q_WS_WIN
AttachConsole(ATTACH_PARENT_PROCESS);

// Some calls to WriteConsole to simulate the call to options.showUsage() which
// works perfectly fine on Linux but not on bloody Windows!
#else
options.showUsage();
#endif

return -1;
}

...
}
The problem with the above is that it's really ugly and very much looks like a big hack to get around Windows' stupid limitation.

high_flyer
26th January 2011, 18:12
If you are using MSVS just set Linker->system->console. - and it will add a console window to which your application can output.

arnaiz
26th January 2011, 18:40
Also you can add console to the CONFIG line in your project file. No need to make simple things complex... :)

agarny
26th January 2011, 19:04
Sorry, I guess I should have made it clear that I do NOT want the console window to be visible at all times, ONLY if there is a need for it, i.e. when arguments passed to my application require output to the console. (Otherwise, I don't use QMake, but CMake and this not with MSVS but with MinGW :))

high_flyer
26th January 2011, 19:16
If what you need is only an output window, then just make one using widgets - I don't know if its possible to call a console window in runtime, and associating it with your application for output in any cross platform way.
For startup arguments you will have to have console before your application starts any way.

agarny
26th January 2011, 19:24
If what you need is only an output window, then just make one using widgets - I don't know if its possible to call a console window in runtime, and associating it with your application for output in any cross platform way.It's not only an output window that I need. I really need access to the console. On Linux, for example, one might want to redirect the output to a file or something. If I was to create an output window, I couldn't do that.


For startup arguments you will have to have console before your application starts any way.Not sure what you mean by this...

arnaiz
26th January 2011, 19:39
Again, you can hide() your main window, so there is no GUI showing up, and then you can use a QProcess to execute the command processor of your OS. You can use readData() and writeData() for your IO.

Now you can control your GUI, use objects already written, open a console if you need to, and read or write from that process. Is this an option to solve your problem?

agarny
26th January 2011, 19:57
Again, you can hide() your main window, so there is no GUI showing up, and then you can use a QProcess to execute the command processor of your OS. You can use readData() and writeData() for your IO.

Now you can control your GUI, use objects already written, open a console if you need to, and read or write from that process. Is this an option to solve your problem?I get the feeling that we are not understanding one another. As I said, everything works as I expect on Linux and Mac OS X (I have just checked), so let me explain a bit more what is 'wrong' with Windows.

Ok, say that you open a command prompt window. From there, if I enter the name of my application without any argument, then I want the GUI to show up (easy, since it's already the way it works):


C:\MyApplicationDirectory>MyApplication

C:\MyApplicationDirectory>

Now, say that I add some arguments. This time round, I want my application to behave as a pure console application, so that I can do whatever I want with it (e.g. redirect the output to a file):


C:\MyApplicationDirectory>MyApplication /i myInputFile
MyApplication is going to proceed using myInputFile as an input file
...

C:\MyApplicationDirectory>Now, as I have said in a previous reply, I can use AttachConsole() and WriteConsole() to 'mimick' the above output, but 1) it's a hack and 2) I can't get the right kind of output (and I am not sure whether I could redirect to a file; I haven't tried) since when starting a GUI application from the command line, Windows goes back to the prompt straightaway, so if anything using AttachConsole() and WriteConsole() I could get:


C:\MyApplicationDirectory>MyApplication /i myInputFile
C:\MyApplicationDirectory>
MyApplication is going to proceed using myInputFile as an input file
...which is clearly not professional...

high_flyer
27th January 2011, 08:53
It's not only an output window that I need. I really need access to the console.
I don't understand how you mean that.
Lets say you have an open console, and you start you application with arguments.
Your application starts, and outputs to the console - now you want in that very output console interact with the application??
Or do yo mean you want your application to spawn two consoles, one for input and one for output?


Not sure what you mean by this...
Well, how do you suppose you can start an application with arguments with out a console?

agarny
27th January 2011, 11:34
I don't understand how you mean that.
Lets say you have an open console, and you start you application with arguments.
Your application starts, and outputs to the console - now you want in that very output console interact with the application??
Or do yo mean you want your application to spawn two consoles, one for input and one for output?As you said, let's say that I have an open console. Now, what I would like is that if I start my application from that console, but with no argument, then I just want the GUI to show up. However, if from that same console window, I start my application with some arguments, then I want my application to take advantage of the console and interact with it (for output only, since I wouldn't expect to need it for input, though one can never tell for certain), and this is where my problem lies... I can't seem to be able to do that in a neat way on Windows while it all works fine on Linux and Mac OS X (even though I do get to see the application icon popping up temporarily on Mac OS X, but I am 'OK' with it I suppose).


Well, how do you suppose you can start an application with arguments with out a console?Well, you can always create a shortcut which points to your application and which has arguments associated with it, but this might be besides the point indeed.

agarny
27th January 2011, 14:27
I had never noticed this: QApplication::QApplication ( int & argc, char ** argv, bool GUIenabled ) (http://doc.qt.nokia.com/latest/qapplication.html#QApplication-2). Now, if only it was working on all three platforms... Argh!

high_flyer
27th January 2011, 14:38
Now I learned something new too! :)


Now, if only it was working on all three platforms... Argh!
Where does it say that it doesn't?
It only says that on Windoes and Mac the windowing system is intialized, but that is not a a problem for you.

Also, I don't see how this actually solvers your problem of spawning a console...

agarny
27th January 2011, 16:08
Well, I gave it a try and when I tried to write something to the console, e.g.
std::cout << "Hello World!" << std::endl;it just didn't work while it (obviously) does when the application is a pure console application.

agarny
28th January 2011, 13:13
Okie dokie, I have finally got a solution which I am happy with...

Basically, on Windows, an application can either be a console application or a GUI application, not both. There is no way around that. However, yesterday evening, I found an old MSN magazine article (http://msdn.microsoft.com/en-us/magazine/cc164023.aspx) about making a 'hybrid' console/GUI application. Basically, the trick consists of having a .com and a .exe version of your executable (gosh, I had completely forgotten about the .com trick!). The .com is a console application while the .exe a GUI one. Note that it's not the same as having both a console and a GUI version of your application, since for most people this would likely involve two .exe files, e.g. myAppConsole.exe and myAppGUI.exe, which is clearly not what one would ideally want. Consider indeed the case where you a console window is opened. Well, you don't want to have to decide between myAppConsole.exe and myAppGUI.exe. Instead you would just like to be able to enter something like:


C:\>myAppand that is it, and that's where having both a .com and a .exe version of your application (i.e. myApp.com and myApp.exe) comes in very handy.

Anyway, all that to say that I have just implemented this in Qt and it all works like a charm. I have attached the source code and binary version of that small Qt application (5829; note that I have left my .pro.user file since I do a tiny bit of post-processing), in case people wanted to do something similar. When unzipping the file, you will get a ListProc folder. In it, you will find a Bin folder which contains both ListProc.com and ListProc.exe. Open a console window, go to that Bin directory and you should be able to reproduce what follows:


C:\Users\Alan\Desktop\ListProc\Bin>ListProc -h
Usage: ListProc [-c]
-c Console mode

C:\Users\Alan\Desktop\ListProc\Bin>ListProc -c
Processes:
- Processus #1...
- Processus #2...
- Processus #3...
- Processus #4...
- Processus #5...
- Processus #6...
- Processus #7...
- Processus #8...
- Processus #9...
- Processus #10...

C:\Users\Alan\Desktop\ListProc\Bin>ListProc

C:\Users\Alan\Desktop\ListProc\Bin>Following the last call to the program, the GUI version of the application should show up.

So, yes, very simple in the end... that is, when you know how to do it! :)

high_flyer
28th January 2011, 13:38
Basically, on Windows, an application can either be a console application or a GUI application, not both. There is no way around that.
That is not true, at least for sure not true for output.
I have added consoles to my GUI's, with the MSVS Linker settings I suggested at post #7, but only used it for output, not for input, so I don't know about input, although I don't see a reason why input should work as well.
I never really looked beyond to see what this setting does under the hood to the project arguments, but I leave it to you as an exercise ;)

agarny
28th January 2011, 13:43
Of course that you can attach a console to a GUI application (as has been discussed several times in this thread!), but this is NOT what I am after!

Anyway, you don't seem to be getting (or want to get) the point I am trying to get across. So, please have a look at the article I referred to and... maybe you will finally understand what I am talking about and what I was after.

high_flyer
28th January 2011, 14:26
you don't seem to be getting (or want to get) the point I am trying to get across. So, please have a look at the article I referred to and... maybe you will finally understand what I am talking about and what I was after.
You should listen to your own advice.
In the link YOU provided:


OK, now that I'm off my soapbox, how can you implement a combined GUI/console app? As nearly every programmer targeting Windows knows by now, Windows divides the EXE universe into two camps: console apps and GUI apps. This architecture goes back to the earliest days of Windows, when it first evolved from MS-DOS®. Nowadays, you tell the linker which kind of app you want to generate by using a switch: either /subsystem:Windows or /subsystem:console.


Which is what I was talking about.

agarny
28th January 2011, 15:07
Oh god, here we go again. Let me remind you, you replied to my comment that read:
Basically, on Windows, an application can either be a console application or a GUI application, not both. There is no way around that.and you said that this was not true, implying that one could perfectly have a Windows application that is both a console and a GUI application. From your last message quoting the link I gave:
OK, now that I'm off my soapbox, how can you implement a combined GUI/console app? As nearly every programmer targeting Windows knows by now, Windows divides the EXE universe into two camps: console apps and GUI apps. This architecture goes back to the earliest days of Windows, when it first evolved from MS-DOS®. Nowadays, you tell the linker which kind of app you want to generate by using a switch: either /subsystem:Windows or /subsystem:console.Now, unless I am mistaken this confirms what I said above which was, in fact, nothing more than me paraphrasing the author of the old MSDN magazine article (http://msdn.microsoft.com/en-us/magazine/cc164023.aspx) I referenced:
In short, a Windows-based app must be either a console app or a GUI app and there's no way to have your cake and eat it too. (Unless you want to write your own startup code—which is waaaay more than I'm up for tonight!) But you know it can be done because I already told you Visual Studio does it—but how?From what you said in comment #7, it would seem that one can associate a console to a GUI application, and I am very happy to believe that. So, yes, you are 'right', but only in some way since I wouldn't imagine that the linker was supposed to ever be used in that way, and the fact that it can be done might be more the result of a bad design than anything else. More importantly, though, my understanding is that to use both options will result in the console window being created every time and, as mentioned in comment #9, this is not at all what I want.

ars
28th January 2011, 20:53
I tried the following code snippet compiled with CONFIG += console set in the .pro file:


#include <QtGui>
#include <QApplication>

#include <iostream>

#include <windows.h>

using namespace std;

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;

if(argc < 2)
{
cout << "GUI project" << endl;
FreeConsole();
w.show();
}
else
{
cout << "Console project" << endl;
return 0;
}
return a.exec();
}


Running from command line without arguments just opens the empty QMainWindow. The console where I called the program from shows output "Gui project".

Running from command line with an arbitrary argument just outputs "Console project" to the calling console.

Next use Explorer to start the executable. In this case, a new console and the empty QMainWindow appear. But due to the call to FreeConsole(), the new console gets removed quickly.

So if that "flickering" of the console is ok for you, this might be a solution.

agarny
28th January 2011, 21:04
Yes, I am aware of FreeConsole(), but that flickering is not acceptable to me (I tend to be a bit of a perfectionist when it comes to user experience). Otherwise, as the author of that old MSDN Magazine article (http://msdn.microsoft.com/en-us/magazine/cc164023.aspx) said:
You can destroy the console by calling FreeConsole, but the console window flashes briefly, announcing to the whole world that you don't really know what you're doing.and I must confess that I couldn't agree more! :)

wysota
28th January 2011, 22:41
Correct me if I'm wrong but you suggest to build two exactly identical applications and call one myapp.exe and the other myapp.com and then do something in your app to detect which is the case (sorry, I didn't bother to open your archive, so I'm just guessing) so that you can operate adequately, right? So how is it different from providing two binaries containing the same code but one built with subsystem:Windows and the other with subsystem:console? And what's wrong exactly with attaching a console to your running program and having a single binary?

agarny
29th January 2011, 09:20
You are correct in that the .com (console) and .exe (GUI) versions of the application I attached are identical. However, I just wanted to show, using Qt, what was described in that old MSDN Magazine article (http://msdn.microsoft.com/en-us/magazine/cc164023.aspx). At the end of the day, it's completely up to the developer to do whatever they want.

Now, having said all of that, in my project, the .com and .exe version of my application are not the same at all. In the case of Linux and Mac OS X, the .exe version (well, I am clearly using that naming convention only for convenience here!) I handle both the console and GUI side of my application. If one of the command line options is such that the GUI shouldn't be shown, then I don't show it, simple as that. Now, in the case of Windows, if the user specifically starts the .exe version, then I go straight into GUI mode (ignoring command line options, except for file names that were passed to the .exe file). However, if the user specifically starts the .com version (or do something like C:\>myApp), then I will first analyse the command line options and determine whether there is a need for a console or a GUI version of my application.

So, yes, I am anticipating (since I am still very much working on this project and, therefore, not everything is yet implemented in it) having my back-end code to be common to both the .com and .exe version of my application. I am thinking of having a flag that will let that code know whether it is being run in console or GUI mode, and therefore allow it to determine whether outputs to the console can/should be done or not.

With regards to providing two binaries, one with subsystem:console and the other with subsystem:Windows, there would, in principle, be no difference... as long as you make sure they are named myApp.com and the other myApp.exe, and as long as the .com version can switch to the .exe version if needed (see above).

The key point here, I think, is that the basename of the binaries is the same. If the basename was to be different, then there is, from an end-user's perspective, no point in having two different binaries. I mean that, as an end-user, just want to have to 'decide' which version of the application I need to run to get a console or a GUI behaviour. Indeed, that 'decision' ought to be made by the application, not the end-user.

Finally, with regards to attaching a console to my GUI (and thus having only one binary indeed), the problem is that when you use AttachConsole, you must then use WriteConsole/ReadConsole, and though I got something to 'work', it wasn't 'clean' (see comment #13 (http://www.qtcentre.org/threads/38171-Both-a-command-line-and-GUI-application-at-the-same-time?p=175379#post175379)), as once again mentioned in that old MSDN Magazine article (http://msdn.microsoft.com/en-us/magazine/cc164023.aspx):
There's a new (for Windows XP) function that would seem to be just the ticket: AttachConsole. This function lets you "attach" your program to a console window of another process. If you use the special process ID ATTACH_PARENT_CONSOLE, AttachConsole attaches to the console from which your program was invoked. Great! But there're two problems. First, AttachConsole only exists in Windows XP, so if you want your program to run in other versions of Windows, you're out of luck; and second, AttachConsole doesn't work exactly as hoped. You can write stuff to the console, but the command prompt is all messed up after your program exits—oops!I am telling you, that article is definitely worth a read for anyone who needs their their application to be a hybrid console/GUI application. :)

To sum up, attaching a console is, in my view, not the way to go (as just mentioned above). Instead, one should indeed have two binaries (one .com and one .exe) with the same basename. This implies the back-end code to be shared by both binaries, but I can't see much way around this unfortunately...?

wysota
29th January 2011, 12:22
All mixed console/GUI Windows apps I have seen for a long time were exe files. And what's wrong in wrapping ReadConsole/WriteConsole and stdin/stdout into one clean interface and using that? It's probably around 10-20 lines of code total.

agarny
29th January 2011, 15:14
All mixed console/GUI Windows apps I have seen for a long time were exe files. And what's wrong in wrapping ReadConsole/WriteConsole and stdin/stdout into one clean interface and using that? It's probably around 10-20 lines of code total.Would any of those console/GUI Windows applications be freely available for download, so that I can try them out? I would certainly interested to see how they behave and, therefore, see whether they have any of the pitfalls that I have read about and experienced myself.

As for ReadConsole/WriteConsole, did you read the quote I just gave and my comment #13 (http://www.qtcentre.org/threads/38171-Both-a-command-line-and-GUI-application-at-the-same-time?p=175379#post175379)? In my experience (and that of the article author, if one is to believe what he wrote) is that it's messy. Now, I am certainly not claiming that my use of AttachConsole and WriteConsole was perfect. In other words, I am not excluding the fact that I may have done something sub-optimal, if not wrong, but in that case I would then very much appreciate some pointers to what you would consider to be the 'perfect' use of those Windows API functions.

wysota
29th January 2011, 19:43
Would any of those console/GUI Windows applications be freely available for download, so that I can try them out? I would certainly interested to see how they behave and, therefore, see whether they have any of the pitfalls that I have read about and experienced myself.
I don't know, I guess some tools Microsoft provides with Windows fall into this category but to be honest, I'm not sure.

Anyway I still fail to see what's wrong with attaching a console. Redirection works in terms of stdin/stdout and not console so it might work. At worst you can find a way to open stdout but it is so Windows specific that you have to look for help at more suited sites than ours.

agarny
29th January 2011, 21:58
Anyway I still fail to see what's wrong with attaching a console.As I said, I may be wrong and maybe attaching a console could work. It's just that I haven't been able to get things to work to my satisfaction (again, see comment #13), and what I have found on the Internet so far seems to indicate that it might not be possible indeed. I shall certainly keep looking for it when I have time, since I would rather just have one .exe file than both a .com and a .exe file.

Redirection works in terms of stdin/stdout and not console so it might work.I am not sure what you mean by this.

At worst you can find a way to open stdout but it is so Windows specific that you have to look for help at more suited sites than ours.That, I can only agree with indeed!

wysota
29th January 2011, 22:04
I am not sure what you mean by this.
By redirection I mean the redirection operator (">") which associates stdout of a process with a file. Console has nothing to do with this really.

agarny
29th January 2011, 22:14
Oh, ok, got you, and yes indeed.