I think I've got a solution for making a hybrid application that can either be run in GUI or console mode, depending on command-line parameters. However, I've been unable to find an example of such a program using wxWidgets. wx-users contains much discussion about this, but I'm failing to find any code.
My solutions follows. If any command-line parameter is passed, no GUI is used. (The check is a bit of a hack on MSW, I know.) Any thing I'm doing wrong?
c> My solutions follows. If any command-line parameter is passed, no GUI c> is used. (The check is a bit of a hack on MSW, I know.) Any thing c> I'm doing wrong?
I don't think you do anything wrong but it would be nicer (e.g. if you intend to put this on the wiki or maybe submit for inclusion in the manual or as a new sample) if wx command line parsing could be used, is it too late to do this in OnCmdLineParsed()?
Also, writing GUIlessTest seems rather pointless, usually people want to have their "main()" called if they don't use GUI so you could just replace this class creation with calls to wxInitialize (or use wxInitializer) and call some console_main() function.
> c> My solutions follows. If any command-line parameter is passed, no GUI > c> is used. (The check is a bit of a hack on MSW, I know.) Any thing > c> I'm doing wrong?
> I don't think you do anything wrong but it would be nicer (e.g. if you > intend to put this on the wiki or maybe submit for inclusion in the manual > or as a new sample) if wx command line parsing could be used, is it too > late to do this in OnCmdLineParsed()?
Considering that OnCmdLineParsed() is a member of wxAppConsole, I don't think this could be done since we are using the command-line arguments to figure which application to create. Or am I missing something?
Alternately I can just trigger the parsing myself by instancing a wxCmdLineParser in main() or WinMain() and calling Parse(), right? I'm not sure how to get the appropriate argc and argv for the parser's constructor though. On Windows, I get one string in lpCmdLine, which doesn't include the program name. Does wxCmdLineParser::SetCmdLine() expect the program name to be first? It doesn't say in the docs. Everywhere else, the incoming argv is char ** and the constructor expects wxChar **. Is there an easy way to convert this? wxApp* objects set their own argc and argv somewhere behind the scenes, and I'm not sure how they do it.
c> Considering that OnCmdLineParsed() is a member of wxAppConsole, I c> don't think this could be done since we are using the command-line c> arguments to figure which application to create. Or am I missing c> something?
I thought about deriving a wxGUIOrConsoleApp class from wxApp (i.e. the GUI version) but overriding its virtual Initialize() to call wxAppConsole version if argc != 1. I didn't test it but I think it should be possible to make this work.
c> Alternately I can just trigger the parsing myself by instancing a c> wxCmdLineParser in main() or WinMain() and calling Parse(), right?
With a "smart" wxApp class above you wouldn't need to manually deal with WinMain() at all.
c> I'm not sure how to get the appropriate argc and argv for the parser's c> constructor though. On Windows, I get one string in lpCmdLine
You can use wxCmdLineParser::ConvertStringToArgs() just as the code in src/msw/main.cpp does. But, again, hopefully this is unnecessary anyhow.
> c> Considering that OnCmdLineParsed() is a member of wxAppConsole, I > c> don't think this could be done since we are using the command-line > c> arguments to figure which application to create. Or am I missing > c> something?
> I thought about deriving a wxGUIOrConsoleApp class from wxApp (i.e. the > GUI version) but overriding its virtual Initialize() to call wxAppConsole > version if argc != 1. I didn't test it but I think it should be possible to > make this work.
Some (many?) GUI apps are capable to parse command lines, e.g. to detect which file to open etc. IMHO, just checking argc != 1 seems too restrictive.
On Mon, 14 Apr 2008 22:05:07 +0200 w...@61131.com wrote:
> > c> Considering that OnCmdLineParsed() is a member of wxAppConsole, I > > c> don't think this could be done since we are using the command-line > > c> arguments to figure which application to create. Or am I missing > > c> something?
> > I thought about deriving a wxGUIOrConsoleApp class from wxApp (i.e. the > > GUI version) but overriding its virtual Initialize() to call wxAppConsole > > version if argc != 1. I didn't test it but I think it should be possible to > > make this work.
> Some (many?) GUI apps are capable to parse command lines, e.g. to detect which file to open etc. IMHO, just checking argc != 1 seems > too restrictive.
Yes, sure, it was just an example, we probably would want to use wxCmdLineParser in some way if we wanted to do it properly anyhow.
On Apr 14, 2:53 pm, Vadim Zeitlin <va...@wxwidgets.org> wrote:
> c> Considering that OnCmdLineParsed() is a member of wxAppConsole, I > c> don't think this could be done since we are using the command-line > c> arguments to figure which application to create. Or am I missing > c> something?
> I thought about deriving a wxGUIOrConsoleApp class from wxApp (i.e. the > GUI version) but overriding its virtual Initialize() to call wxAppConsole > version if argc != 1. I didn't test it but I think it should be possible to > make this work.
I like this idea quite a bit. I found I had a bit more to do to get it to work, and more things probably remain. I had to override wxApp::CleanUp() since it too fiddled with GTK. And I had to make sure the active log target was stderr instead of wxLogGui. It seemed the most correct way to do this was derive a new wxAppTraits class and override CreateLogTarget().
c> I like this idea quite a bit. I found I had a bit more to do to get c> it to work, and more things probably remain. I had to override c> wxApp::CleanUp() since it too fiddled with GTK.
Sorry, what exactly was the problem? I think we should fix it in wxGTK.
c> And I had to make c> sure the active log target was stderr instead of wxLogGui. It seemed c> the most correct way to do this was derive a new wxAppTraits class and c> override CreateLogTarget().
Indeed.
c> Here's the code, which now uses wxCmdLineParser: c> c> ------------ c> #include <iostream> c> #include <wx/wx.h> c> #include <wx/apptrait.h> c> #include <wx/cmdline.h> c> c> class HybridTraits : public wxGUIAppTraits { c> public: c> HybridTraits(bool gui_enabled) c> : wxGUIAppTraits(), c> gui_enabled(gui_enabled) { c> } c> c> wxLog *CreateLogTarget() { c> if (gui_enabled) { c> return wxGUIAppTraits::CreateLogTarget(); c> } else { c> return new wxLogStderr; c> } c> } c> c> private: c> bool gui_enabled; c> }; c> c> class GUITest : public wxApp { c> private: c> bool OnInit(); c> c> bool Initialize(int& argc, wxChar **argv) { c> static const wxCmdLineEntryDesc desc[] = { c> { wxCMD_LINE_SWITCH, "c", "console", "run in console mode" }, c> { wxCMD_LINE_NONE } c> }; c> c> wxCmdLineParser parser(desc, argc, argv); c> if (parser.Parse(true) != 0) { c> exit(1); c> } c> c> gui_enabled = !parser.Found("c"); c> if (gui_enabled) { c> return wxApp::Initialize(argc, argv); c> } else { c> return wxAppConsole::Initialize(argc, argv); c> } c> } c> c> void CleanUp() { c> if (gui_enabled) { c> wxApp::CleanUp(); c> } else { c> wxAppConsole::CleanUp(); c> } c> } c> c> HybridTraits *CreateTraits() { c> return new HybridTraits(gui_enabled); c> } c> c> bool gui_enabled; c> }; c> c> bool GUITest::OnInit() { c> c> wxString msg; c> c> if (gui_enabled) { c> msg = "in gui mode"; c> } else { c> msg = "in console mode"; c> } c> c> wxLogMessage(msg); c> return false; c> c> } c> c> IMPLEMENT_APP(GUITest); c> c> ------------ c> c> Vadim, if you think this would make a good sample, I'm happy to make c> any changes to get it to a presentable state.
Yes, I think this is a useful example and I'd be favourable to adding it as a new sample. I don't have a good name for it though ("hybrid" is too generic), do you?
>c> Vadim, if you think this would make a good sample, I'm happy to make >c> any changes to get it to a presentable state.
> Yes, I think this is a useful example and I'd be favourable to adding it >as a new sample.
I haven't tried this code, but when I once tried to create such an app I was told that it's not possible on windows because the linker decides the type of application with /SUBSYSTEM:WINDOWS or CONSOLE.
So how would this hybrid app now be created? Would it still have a console attached even when using a GUI?
On Apr 15, 12:39 pm, Vadim Zeitlin <va...@wxwidgets.org> wrote:
> c> I like this idea quite a bit. I found I had a bit more to do to get > c> it to work, and more things probably remain. I had to override > c> wxApp::CleanUp() since it too fiddled with GTK.
> Sorry, what exactly was the problem? I think we should fix it in wxGTK.
Well, the assertion failure occurs in wxApp::CleanUp in src/gtk/ app.cpp. At last checkout, this was line 529.
void wxApp::CleanUp() { if (m_idleSourceId != 0) g_source_remove(m_idleSourceId);
// release reference acquired by Initialize() g_type_class_unref(g_type_class_peek(GTK_TYPE_WIDGET)); // line 529
However, I don't think we can say it constitutes a bug. If I override Initialize(), in which the GTK libraries are dynamically loaded and which fails if they're not, I should probably expect that I need to override any other wxApp functions that assume GTK is available.
> Yes, I think this is a useful example and I'd be favourable to adding it > as a new sample. I don't have a good name for it though ("hybrid" is too > generic), do you?
guioptional? guinogui?
I'm not sure how to address any Windows issues, since I don't have a Windows machine to build on. It was my understanding that wApps do not show the console window at all, which would be the case here. wxAppConsoles, on the other, optionally show the console window.
FC> I haven't tried this code, but when I once tried to create such an app FC> I was told that it's not possible on windows because the linker decides FC> the type of application with /SUBSYSTEM:WINDOWS or CONSOLE. FC> FC> So how would this hybrid app now be created? Would it still have a FC> console attached even when using a GUI?
Good question, I forgot about this issue to be honest. There is indeed a problem with this under MSW because console applications always have a console and while it can be explicitly destroyed with FreeConsole() it is still visible on startup. And while Windows applications can be run from the console, they don't have any console of their own. I thought that doing ::AttachConsole(ATTACH_PARENT_PROCESS) could help here but this function fails for me (Windows 2003) with "invalid handle" error and, anyhow, it's XP and later only. And in fact even allocating a new console doesn't work (the console does appear but printf() still does nothing).
So it unfortunately seems that the usefulness of this method under Windows is indeed limited to console applications which sometimes need to show GUI.