1

I'm working on a Mac app that will optionally provide the ability to install some extra software that's in apple package format. This package requires elevated (root) privileges to install, and instead of bothering with a helper tool or AuthorizationExecuteWithPrivileges(), I figured it would be easier to use NSTask to call /usr/bin/open or even /System/Library/CoreServices/Installer.app/Contents/MacOS/Installer with the path to the package and let it handle the user authorization.

So, it works all fine and dandy, but the Installer window does not appear at the front of the stack, nor is it ever key. I can orderOut the calling window before launching the task and that helps with the ordering, but the key is really going to be, well, making it key.

Should I just go ahead and implement Apple's BetterAuthorizationSample since I'll probably have to use it at some point in the future, and since it will make be a better person for having tried?

joraff
  • 967
  • 6
  • 15
  • Definitely go with the Apple installer. Writing your own installer is not as simple as you may be thinking. Also, try to avoid using command line tools if possible, there is usually an API to do what you want. The `open` command-line tool is basically a wrapper around the Launch Services C API. You can access that API directly or via `NSWorkspace`, which is an Objective-C API that wraps several of the Launch Services calls. – Rob Keniger Jan 27 '12 at 09:24
  • Oh, yes, I'll definitely be using apple's installer! Whether that's `Installer.app` or `installer`, is undecided. – joraff Jan 27 '12 at 15:12

2 Answers2

2

There are two variables which determine which window is key. One of those is a setting within an application which determines which of its windows is in focus, and the other is which application is active. Hiding windows in our own application will make the installer visible, but it won't become key unless you make the installer application active. You have a few options for doing this.

First, you could deactivate your application using:

[[NSApplication sharedApplication] deactivate];

Next, you could find the NSRunningApplication for the installer and activate it:

NSArray *apps = [[NSWorkspace sharedWorkspace] runningApplications];
NSRunningApplication *app = // find installer
[app activateWithOptions:0];

But in your case I would recommend using NSWorkspace to open the installer for you, instead of starting it through NSTask. NSWorkspace will automatically deactivate your application, so you don't have to, you get feedback about whether the file opened successfully, and the application to open it with is automatically chosen.

BOOL success = [[NSWorkspace sharedWorkspace] openFile:@"path/to/file.pkg"];
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123
  • Awesome answer, Peter. I had briefly looked at NSWorkspace's openFile, but I'd like to know whether or not the install itself completed successfully. It seems only NSTask can return a termination status. I suppose I could always watch for a package receipt using the NSWorkspace method, but that seems unreliable. – joraff Jan 27 '12 at 15:13
  • 1
    I stumbled upon and ended up using [`STPrivilegedTask`](http://sveinbjorn.org/STPrivilegedTask) to run the installer in the background. – joraff Jan 30 '12 at 22:34
0

You can force a process to the front in-code by including the following code in your applicationDidFinishLaunching handler:

ProcessSerialNumber process;
GetCurrentProcess(&process);
SetFrontProcess(&process);

This will make it the frontmost process, regardless of anything else. You'll need to link to Carbon, though.

svth
  • 1,303
  • 1
  • 14
  • 23
  • That'll work, but it's addressing the symptom and not the root cause. If you launch your program with the appropriate APIs it should launch frontmost to begin with. – StilesCrisis Feb 04 '12 at 16:53