/ Published in: Objective C
The preference pane that need to be reloaded must fork 'reload' with two arguments, then terminate System Preferences. In Cocoa, the fork is easily achieved with a NSTask. The first argument must be the bundle identifier of the preference pane to reload. The second argument must be the process identifier of the terminating application, i.e. System Preferences. In Cocoa, you can get it with [[NSProcessInfo processInfo] processIdentifier], which is equivalent to getpid().
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
// gcc -Wall -arch i386 -arch ppc -mmacosx-version-min=10.4 -framework AppKit -framework Carbon -weak_framework ScriptingBridge -o reload reload.m 2>&1 | egrep -v "(In file included from reload.m:8:)|(Mac OS X version 10.5 or later is needed for use of property)" #import <AppKit/AppKit.h> #import <Carbon/Carbon.h> // Generate this header with // sdef "/Applications/System Preferences.app" | sdp -fh --basename SystemPreferences #import "SystemPreferences.h" { const char *prefPaneIdentifier; pid_t parentProcessId; } - (void) reload; @end @implementation TerminationListener - (id) initWithPrefPaneIdentifier:(const char *)paneId parentProcessId:(pid_t)ppid { self = [super init]; if (self != nil) { prefPaneIdentifier = paneId ; parentProcessId = ppid; // This adds the input source required by the run loop [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidTerminate:) name:NSWorkspaceDidTerminateApplicationNotification object:nil]; if (getppid() == 1) { // ppid is launchd (1) => parent terminated already [self reload]; } } return self; } { if (parentProcessId == [[[notification userInfo] valueForKey:@"NSApplicationProcessIdentifier"] intValue]) { // parent just terminated [self reload]; } } { if ([[notificationInfo valueForKey:@"NSApplicationName"] isEqualToString:@"System Preferences"]) { // System Preferences just relaunched ProcessSerialNumber psn = {[[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberHigh"] intValue], [[notificationInfo valueForKey:@"NSApplicationProcessSerialNumberLow"] intValue]}; SetFrontProcess(&psn); // Load the pref pane by sending the appropriate apple event AppleEvent event = {typeNull, NULL}; AEBuildAppleEvent(kAECoreSuite, kAESetData, typeProcessSerialNumber, &psn, sizeof(psn), kAutoGenerateReturnID, kAnyTransactionID, &event, NULL, [gizmo UTF8String]); AESend(&event, NULL, kAENoReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL); AEDisposeDesc(&event); } } - (void) reload { if (NSClassFromString(@"SBApplication")) { SystemPreferencesApplication *SystemPreferences = [SBApplication applicationWithBundleIdentifier:@"com.apple.systempreferences"]; @try { [SystemPreferences activate]; SystemPreferences.currentPane = [SystemPreferences.panes objectWithID:[NSString stringWithCString:prefPaneIdentifier encoding:NSUTF8StringEncoding]]; NSLog(@"%@", [exception description]); } } else { [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(applicationDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil]; } } } { /* As it is impossible to get the right combination of {[NSApp stop:self] call, [NSApp abortModal] call, [NSApp terminate:self] call, on Tiger, on Leopard, from Terminal, from NSTask} to work (that is, exit the run loop), just call the more radical exit() function. */ } @end int main (int argc, const char * argv[]) { if (argc != 3) return EXIT_FAILURE; [[[TerminationListener alloc] initWithPrefPaneIdentifier:argv[1] parentProcessId:atoi(argv[2])] autorelease]; [pool release]; return EXIT_SUCCESS; }