I have a command-line application written in C++ and built with gcc/make that runs on MacOS. This application does not have its own GUI and it not supposed to have any root windows -- it's a console app meant to be controlled by another application.
However, there is a need to show a file selection dialog at one point, which makes this a bit of a hybrid.
I've used wxWidgets to create the dialog, but it fails to get input. I've seen in the various FAQs and user groups that a bundle is probably required. When I tried to create a bundle it didn't solve the problem.
Here's how wxWidgets is initialized:
#ifdef __WXMAC__
if (!wxEntryStart(argc, argv))
{
cout << "Failed to initialize wxWidgets." << endl;
return 0;
}
#endif
clientApp = new MainClass();
clientApp->Run(argc, argv);
#ifdef __WXMAC__
wxEntryCleanup();
#endif
When I try to show a browse file dialog, using this code, it shows the file chooser but does not respond, acting like it has no message pump:
#ifdef __WXMAC__
wxFileDialog* dlg = new wxFileDialog( NULL, _("Upload File"), _(""), _(""),
_("All Files (*.*)|*.*"), wxFD_OPEN|wxFD_FILE_MUST_EXIST );
if (dlg->ShowModal() == wxID_CANCEL )
{
INFOLOG("File upload dialog has been cancelled." << endl )
return false;
}
#endif
Since the FAQs say that creating a bundle is a way to automagically create a message pump and make a GUI responsive. I tried creating a bundle:
myapp.app
--> Contents
--> MacOS
--> myapp (executable file)
--> cert.crt (ssl certificate used by app)
--> Resources
--> myapp.icns
--> Info.plist (points ot myapp as executable and uses myapp.icns as icon)
The application controlling this is not one I have control over and has to run it in exactly this way:
myapp
The controlling app needs to read the console output of this app and that is primarily why this has been a console-only app.
Since the executable is a few directories deeper, I tried creating a shell script in the root directory above the bundle to run the application and calling it myapp. Myapp just runs myapp.app/Contents/MacOS/myapp, forwarding the commandline parameters.
This didn't work. The browse file window is created and just sits there, giving me the rainbow spinwheel every time I mouse over it.
What can I do to get a message pump going? Is there a call I can add to the wxWidgets code or do I need to do something differnently with the bundle? Does having the shell script launch the app that is inside the bundle completely defeat the "message pump magic" that the bundle is supposed to give, and if so, is there a sensible workaround? Do I just need to create some sort of pseudo-parent for the wxFileDialog?
I've heard reports that an alternative to creating an application bundle is to use the following code (which I never recommend to anyone over a bundle, but it sounds like your situation is a good reason to use it):
#include <ApplicationServices/ApplicationServices.h>
ProcessSerialNumber PSN;
GetCurrentProcess(&PSN);
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);
This is of course platform specific, so wrap it accordingly.
Do you have a derived wxApp, and wxApp::OnInit()? These are likely still required to initialize the event loop.
You can make your app a full GUI app and just hide the main window using wxWindow::Show( false ). This will give you your working message pump. You should still be able to catch stdout and stderror.
Related
How do I create a windows application that does the following:
it's a regular GUI app when invoked with no command line arguments
specifying the optional "--help" command line argument causes the app to write usage text to stdout then terminate
it must be a single executable. No cheating by making a console app exec a 2nd executable.
assume the main application code is written in C/C++
bonus points if no GUI window is created when "--help" is specified. (i.e., no flicker from a short-lived window)
In my experience the standard visual studio template for console app has no GUI capability, and the normal win32 template does not send its stdout to the parent cmd shell.
Microsoft designed console and GUI apps to be mutually exclusive.
This bit of short-sightedness means that there is no perfect solution.
The most popular approach is to have two executables (eg. cscript / wscript,
java / javaw, devenv.com / devenv.exe etc) however you've indicated that you consider this "cheating".
You've got two options - to make a "console executable" or a "gui executable",
and then use code to try to provide the other behaviour.
GUI executable:
cmd.exe will assume that your program does no console I/O so won't wait for it to terminate
before continuing, which in interactive mode (ie not a batch) means displaying the next ("C:\>") prompt
and reading from the keyboard. So even if you use AttachConsole your output will be mixed
with cmd's output, and the situation gets worse if you try to do input. This is basically a non-starter.
Console executable:
Contrary to belief, there is nothing to stop a console executable from displaying a GUI, but there are two problems.
The first is that if you run it from the command line with no arguments (so you want the GUI),
cmd will still wait for it to terminate before continuing, so that particular
console will be unusable for the duration. This can be overcome by launching
a second process of the same executable (do you consider this cheating?),
passing the DETACHED_PROCESS flag to CreateProcess() and immediately exiting.
The new process can then detect that it has no console and display the GUI.
Here's C code to illustrate this approach:
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
if (GetStdHandle(STD_OUTPUT_HANDLE) == 0) // no console, we must be the child process
{
MessageBox(0, "Hello GUI world!", "", 0);
}
else if (argc > 1) // we have command line args
{
printf("Hello console world!\n");
}
else // no command line args but a console - launch child process
{
DWORD dwCreationFlags = CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS;
STARTUPINFO startinfo;
PROCESS_INFORMATION procinfo;
ZeroMemory(&startinfo, sizeof(startinfo));
startinfo.cb = sizeof(startinfo);
if (!CreateProcess(NULL, argv[0], NULL, NULL, FALSE, dwCreationFlags, NULL, NULL, &startinfo, &procinfo))
MessageBox(0, "CreateProcess() failed :(", "", 0);
}
exit(0);
}
I compiled it with cygwin's gcc - YMMV with MSVC.
The second problem is that when run from Explorer, your program will for a split second
display a console window. There's no programmatic way around this because the console is
created by Windows when the app is launched, before it starts executing. The only thing you can
do is, in your installer, make the shortcut to your program with a "show command" of
SW_HIDE (ie. 0). This will only affect the console unless you deliberately honour the wShowWindow field of STARTUPINFO
in your program, so don't do that.
I've tested this by hacking cygwin's "mkshortcut.exe". How you accomplish
it in your install program of choice is up to you.
The user can still of course run your program by finding the executable in Explorer and
double-clicking it, bypassing the console-hiding shortcut and seeing the brief black flash of a console window. There's nothing you can do about it.
You can use AllocConsole() WinApi function to allocate a console for GUI application. You can also try attaching to a console of a parent process with AttachConsole(), this makes sense if it already has one. The complete code with redirecting stdout and stderr to this console will be like this:
if(AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()){
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
}
I found this approach in the Pidgin sources (see WinMain() in pidgin/win32/winpidgin.c)
I know my answer is coming in late, but I think the preferred technique for the situation here is the ".com" and ".exe" method.
This may be considered "cheating" by your definition of two executables, but it requires very little change on the programmers part and can be done one and forgot about. Also this solution does not have the disadvantages of Hugh's solution where you have a console windows displayed for a split second.
In windows from the command line, if you run a program and don't specify an extension, the order of precedence in locating the executable will prefer a .com over a .exe.
Then you can use tricks to have that ".com" be a proxy for the stdin/stdout/stderr and launch the same-named .exe file. This give the behavior of allowing the program to preform in a command line mode when called form a console (potentially only when certain command line args are detected) while still being able to launch as a GUI application free of a console.
There are various articles describing this like "How to make an application as both GUI and Console application?" (see references in link below).
I hosted a project called dualsubsystem on google code that updates an old codeguru solution of this technique and provides the source code and working example binaries.
I hope that is helpful!
Operating Environment: Windows 7, Visual Studio 2010, CLR GUI.
So I've been given the unglorious task of enhancing a GUI application that is started from a command prompt. Because it is. Because poor design decisions by previous implementers. Anyway, it launches one of several GUIs depending upon the input arguments.
I'd like to be able to print back to the same command prompt window if (when) the user types something that the code doesn't understand.
Here's what I've tried (none of which output anything):
int main( array<System::String^>^ args )
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
OutputDebugString("hello");
Trace::WriteLine("hello");
Debug::Trace::WriteLine("hello");
Console::WriteLine("hello");
std::cout << "hello";
printf("hello");
return 0;
}
Thanks in advance!
Update: I don't want to use AllocConsole(), as that opens a new console that disappears along with all of the data when the application exits. Similarly, a pop-up message box won't work. I'm looking for a way to make the output persistent.
The only way I can get output from the application to date is via a message box (non-persistent) or opening a new console that disappears when the application exits (via AllocConsole() ). And I'm running from a command prompt, not the debugger's "Play" button.
Update
Why the down-vote for not doing research? I spent a day trying to solve this, looking through dozens of posts trying to find a solution, and to date I've found others looking for the same answer, but not finding it. AllocConsole() or changing the project type is always the solution, but neither is a solution for me.
Update
I added the "full code", which is the 2 statements. THAT IS ALL THE CODE. So simple. I'm skipping the start of the GUI because I don't care about that right now, I just want it to print back to the console where the application was started. The most basic HelloWorld. If there are project settings I need to post, I don't know which ones would be relevant. This is where I want to print to the console, before the GUI is ever up. I can't show the GUI if there is an error in the user input.
Right click on the project, select Properties
Under Linker -> System, Change Subsystem from Windows to Console.
A Windows subsystem application cannot write to console, but by changing the subsystem to Console (which can write to the calling console), the Form part of the application can still work (tested in Visual Studio 2010).
I'm experiencing Qt4 to Qt5 troubles. In my application when the user clicks the print button two things should happen, one is that a PDF gets written to disk (which still works fine in the new version, so I know that some of the printing functions are working properly) and the other is that a QPrintDialog should exec() and then send to a connected printer.
I see the dialog when I launch from my development machine. The application launches on the deployed machine, but the QPrintDialog never shows and the document never prints.
I am including print support.
QT += core gui network webkitwidgets widgets printsupport
I have been using Process Explorer to see what DLLs the application uses on my development machine, and I believe that everything is present. My application bundle includes:
{myAppPath}\MyApp[MyApp.exe, Qt5PrintSupport.dll, ...]
{myAppPath}\plugins\printsupport\windowsprintersupport.dll
{myAppPath}\plugins\imageformats[ qgif.dll, qico.dll,qjpeg.dll, qmng.dll, qtga.dll, qtiff.dll, qwbmp.dll ]
The following is the relevant code snippet:
void PrintableForm::printFile()
{
//Writes the PDF to disk in every environment
pdfCopy();
//Paper Copy only works on my dev machine
QPrinter paperPrinter;
QPrintDialog printDialog(&paperPrinter,this);
if( printDialog.exec() == QDialog::Accepted ) {
view->print(&paperPrinter);
}
this->accept();
}
My first thought is that the relevant DLLs are not being found come print time, and that means that my application file system is incorrect, but I have not found anything that shows me a different file structure. Am I on the right track or is there something else wrong with this setup?
This was another classic Windows/Qt5 deployment problem with a combination of missing plugins and plugins placed in incorrect places. By using the environmental variable QT_DEBUG_PLUGIN and adding CONFIG += CONSOLE to my PRO file I was able to see that on my development machine the application was loading qminimal.dll which I was not shipping.
The application root which I have defined as {myAppPath}\ is the root directory for plugins. Therefore the correct file structure is:
{myAppPath}\MyApp[MyApp.exe, Qt5PrintSupport.dll, ...]
{myAppPath}\platforms[qwindows.dll,qminimal.dll]
{myAppPath}\printsupport*
{myAppPath}\imageformats*
{myAppPath}\bearer*
Thanks peppe for the lead.
I am using MonoMac do develop a very simple application for Mac. I have a solution consisting of two projects: the main project, and the auto updater. The main project references the auto updater, so that the auto updater becomes a part of the application bundle of the main application.
When running the main application, I want to launch the auto updater if there is a new update. On windows, this is a simple Process.Start("C:\path\to\updater.exe"); On Mac, however, this seems to be problematic. When doing it the above mentioned way (both when using absolute and relative paths to the updater.exe), nothing happens, and debugging simply says that the process has been ended already when I try to get any information.
I have attempted to launch the updater using AppleScript:
do shell script "mono /path/to/bundle.app/Contents/MonoBundle/updater.exe"
But this produces the error "No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting". This same error appears when I try to start the main executable the same way, and I know that the Info.plist is configured correctly for this (since I am indeed able to start the app), so there must be something else that is wrong.
Am I doing something obviously wrong? I just want to start the updater from the main executable, how I do it doesn't matter.
In Xcode 4.6, I created a new application based on the "Command Line Tool" project template.
How can I programmatically start another application (.app application bundle) from that "Command Line Tool" app?
There are numerous ways to accomplish this, using Launch Services and or NSWorkspace.
One of the more flexible ways to identity a bundled application is via its bundle identifier (CFBundleIdentifier), which is a string like com.apple.TextEdit. This allows you to identify an application without having to hard-code an assumed path where the application will be found, or by hard-coding the name of the application bundle, both of which a user could easily change. You can use NSWorkspace's launchAppWithBundleIdentifier:options:additionalEventParamDescriptor:launchIdentifier: to launch the app. If you don't already know it, you can obtain the bundle identifier of an application bundle by checking its AppName.app/Contents/Info.plist file. Then use the following code:
if (![[NSWorkspace sharedWorkspace]
launchAppWithBundleIdentifier:#"com.apple.TextEdit"
options:NSWorkspaceLaunchDefault
additionalEventParamDescriptor:NULL
launchIdentifier:NULL]) {
NSLog(#"launching app failed!);
}
Important: NSWorkspace is part of the AppKit.framework framework, which is not initially included in the "Command Line Tool" project template. To add it to your project, select the target in the list of targets like shown in the image below, and click the + button to add additional frameworks.
Add both AppKit.framework and Cocoa.framework.
That will result in all 3 being listed in the Link Binary With Libraries step. At that point, you can remove both the Foundation.framework and AppKit.framework from the linking stage, and leave just the Cocoa.framework, like below:
Have you tried "open"? At least in terminal "open" runs files and/or apps.