Cocoa applications from the command line - cocoa

I've been attempting to create a Cocoa (desktop) application without using NIBs or XIBs, following the instructions here. All is well, and from Terminal.app I can successfully run the application. However it's spawned without any of the usual decorations, such as a menu bar or a dock icon, behind the terminal window and seemingly attached very much to the terminal process.
I suspected that this might be a consequence of running the binary itself directly from the command line, but playing around a bit I found that at least Calculator.app is more than happy to spawn a "real" application when called using:
$ /Applications/Calculator.app/Contents/MacOS/Calculator
What am I missing, if anything, here? I have also tried loading a XIB file via
NSNib *mainNib = [[NSNib alloc] initWithContentsOfURL:[[NSURL URLWithString:#"../MainMenu.xib"] absoluteURL]];
[NSNib instantiateNibWithOwner:application topLevelObjects:nil];
which seems to find the XIB, but makes no difference (i.e. the GUI isn't fully loaded).

After a bit of fiddling, I found the solution to the issue. Transforming the process type via TransformProcessType and then using the accurately-named [NSApp activateIgnoringOtherApps:YES] did the trick:
ProcessSerialNumber psn = { 0, kCurrentProcess };
OSStatus returnCode = TransformProcessType(& psn, kProcessTransformToForegroundApplication);
[NSApp activateIgnoringOtherApps:YES];
(I also added a custom menu to finish it off). Thanks to Mankarse for the pointers!

You need a NSApplication.. why are you even doing this?
If you create a new GUI application, look at main.m you see that it is spawninng a NSApplication there.
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
This requires you to have a bundle btw and if you want to launch a command line utility without the terminal you could either use launchd or create a bundle that you simply double click in the finder.

Related

Visual Studio C++ console output when EXE started from console, no console when started without console [duplicate]

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!

Loading main window at applicationDidFinishLaunching in Cocoa Application

In a Cooca Application the MainMenu.xib is setup for you in the standard template. This nib has been setup with the application delegate too. In the info.plist the key "Main nib file bas ename" sets the nib file to load at startup.
I want the application to start if possible without a nib, I want to load the MainMenu.xib at applicationDidFinishLaunching in the application's main delegate.
Is it possible?
First, comment out NSApplicationMain in supporting files -> main.m. NSApplicationMain() loads the main nib mentioned in your Info.plist, so skip it. Instead, setup the app and delegate and run the application:
int main(int argc, const char * argv[])
{
//return NSApplicationMain(argc, argv);
#autoreleasepool {
NSApplication * application = [NSApplication sharedApplication];
MYAppDelegate* appDelegate = [[MYAppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
}
return EXIT_SUCCESS;
}
Then, in the app delegate's applicationDidFinishLaunching: function, call something similar to createMainWindow:
- (void)createMainWindow
{
self.wincon = [[MYCustomWindowController alloc] initWithWindowNibName:#"MainMenu"];
self.window = self.wincon.window; // window property in appdelegate created for single-view app
// Also had to connect About: to application's orderFrontStandardAboutPanel
}
MainMenu.xib's File's Owner custom class should be switched to MYCustomWindowController from the application.
If MainMenu.xib has a window like in this example, it's "referencing outlet" needs to be connected to File's Owner->window.
If you started with a single view application, DELETE the App Delegate object from MainMenu.xib -- otherwise the xib will create a second instance of your app delegate. This can be terrible if you're referencing something like MYAppDelegate.managedObjectContext. If you need to bind to the application delegate, you can bind to the Application with a key path of delegate.managedObjectContext.
Why did I do this? Because sometimes my application launches with a GUI, and sometimes it doesn't.
This is possible, but seldom worth the trouble IMO. If you have a bundle already there is little cost in including a small nib file (menu only; no window). If you want to load the rest of your UI from a separate nib file after launch, that's fine. But I recommend allowing MainMenu.nib to load and provide the main menu. (You're not clear on what problem you're trying to solve with your approach.)
That said, Lap Cat wrote a series of articles on this called "Working without a nib" that's worth reading. You'll want the last article The Empire Strikes Back where he includes the link to his nibless project. His technique still works in 10.7.

Building Mac OS X App instead of console

I have a port of my application, a game, running on Mac OS X. I build with make, and only added a few .mm files to access the necessities from NSApplication, NSWindow and NSOpenGLView.
How do I "convert it" into a proper App as opposed to the current console form? Do I bundle it with something, if so how? Or is it some type of linker setting (as in Windows)? Do I have to build using XCode?
Currently I just "tick" the system, i.e. poll it for events rather than utilizing OS X message pump properly. Could you point me to some basic sample or tutorial which shows me how to do it properly, while still being able to "tick" my own stuff at a fixed frame rate? I say "sample or tutorial", since I am blind when it comes to documentation.
Thanks!
For the creation of a proper Mac application, you'll have to create a Mac OS X bundle. You can find the great documentation on the Apple site, here. You can create them with standard tool (I did write a small python script to create a bundle from a simple .ini file that describe files to pack, and how to construct the Info.plist file).
Basically, an application bundle is just a regular directory with a .app extension, and a fixed structure. The following file are required:
Application.app/
+ Contents/
+ MacOS/
| + <Executable>
+ Resources/
| + <Icon>
+ Info.plist
The Info.plist file is a standard property list file (either in XML or in the old format), that indicate what is the name of the executable file (CFBundleExecutable), what is the name of the icon file (CFBundleIconFile), the bundle type (CFBundleType with a value of APPL), and some other informations (file type supported, version string, development language, ...). Those file is the strict minimum required to have a basic Mac OS X application.
For explanation of how the Mac OS X message pump work, I recommend the reading of this article by Matt Gallagher. He explains how the run message of the NSApplication class is implemented. You can then write this method runOnce that only iterate when there are pending messages. You'll then call this function periodically (it is really similar to the PeekMessage, TranslateMessage, and DispatchMessage sequence on Win32):
- (void)runOnce
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self finishLaunching];
for (;;)
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event =
[self
nextEventMatchingMask:NSAnyEventMask
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (sender == nil)
break;
[self sendEvent:event];
[self updateWindows];
}
[pool release];
}
Unless you really want to learn a bunch of Mac-specific APIs and XCode, you should just use SDL. It's a cross-platform library that abstracts things like event handling, threading, and multimedia. It includes XCode templates that allow you to build a proper app bundle with minimal changes to your code. Using SDL would mean throwing away most of the Mac-specific code you've written, but it would vastly simplify any future ports and would make it easier to maintain your app for multiple platforms.

wxWidgets Commandline / GUI Hybrid Application Fails to Get Dialog Input

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.

Create simple cocoa application wrapper

I have an application I want to bundle (with some additional files) together inside a wrapper application.
Literally the only thing I want to do is have the wrapper application launch a resource file (i.e. as if I'd typed:
/path/to/Resources/Bundled.app/Contents/MacOS/executable
into the terminal.
Make sense? I thought this should be simple, but I caouldn't find a simple way to do this-- my cocoa programming is limited, to say the least.
Thanks in advance!
One way, if the wrapped “application” is just a shell script or something, is Platypus.
If it's an actual application (.app bundle), why does app A do nothing but launch app B? Why have app A at all?
Your outer program can use NSBundle to locate the inner program within the outer program's bundle.
To run the inner program: If it's an application, use Launch Services or NSWorkspace; if it's a command-line tool, use NSTask.
I have a blog post up on this: Turn any shell script into a double-clickable app. The entry mentions "start with an empty app bundle"... which you can get by using the Pashua tool mentioned, if I remember correctly...
Just for the sake of posterity (and if it helps anyone else, here is the full code I used (inside the AppDelegate.m file):
NSString *appName = #"";
NSString *bundledApp = [[NSBundle bundleWithPath:[[NSBundle
mainBundle] pathForResource:appName ofType:#"app"]]
bundlePath];
NSWorkspace *launchApp = [[NSWorkspace alloc] init];
NSLog(#"Launching %s", bundledApp);
[launchApp launchApplication:bundledApp];
[launchApp release];
// Make Launcher terminate (if it serves no other purpose)
[NSApp terminate:nil];

Resources