1. A workaround for AESendMessage() hanging on OS X 10.8.2

    Shortly after Apple released their OS X 10.8.2 update last week, I started receiving e-mails from iPhoto Library Manager users who were getting timeout errors when trying to use iPLM to transfer photos between iPhoto libraries. iPLM uses Apple events to communicate with iPhoto, and sends the events using the AESendMessage() API in the CoreServices framework. (it doesn’t use AppleScript for reasons of control and efficiency) After some investigation, I believe there is a bug in 10.8.2 that causes this problem, and thought I’d post my findings here in case any other developers were running into similar issues. The bug is filed as number 12424662 and can be found on Open Radar at http://www.openradar.me/12424662

    The basic outline of the problem is that sometimes a particular application will get into a state where any events sent to it using AESendMessage (or AESend, for that matter) would not be delivered to the target application, and AESendMessage would simply block until the timeout value was reached, then return an errAETimeout error code. Quitting and relaunching the target application doesn’t change anything, but logging out and logging back in usually seems to fix the problem, at least temporarily. It also appears to only affect one application at a time, e.g. if iPhoto breaks in this way, other apps on the system will continue to function just fine.

    After much experimenting with different things, I found a way I was able to get events to flow again, even with the target application in this state. When you set up an Apple event, one of the parameters you need to give is an “address” of the application to which you want to send the event. The Apple event system has several ways of specifying this address, the most common of which are by bundle identifier or by the application’s four character creator code. My code was using the bundle identifier, which looks something like this:

    NSAppleEventDescriptor* addressDescriptor = [[NSAppleEventDescriptor alloc] initWithDescriptorType:typeApplicationBundleID 
    data:[self.bundleIdentifier dataUsingEncoding:NSUTF8StringEncoding]]; 
    
    NSAppleEventDescriptor* newEvent = [[NSAppleEventDescriptor alloc] initWithEventClass:kAECoreSuite 
    eventID:kAEGetData 
    targetDescriptor:addressDescriptor 
    returnID:kAutoGenerateReturnID 
    transactionID:kAnyTransactionID];
    

    What I eventually discovered was, that if I instead used an address descriptor that specified the process ID of the target application rather than just its bundle identifier, then the event would be delivered normally instead of timing out. This of course requires that the application already be running, and requires a bit of additional code to fetch the process ID for the application. This sample code does just that:

    NSRunningApplication* runningApplication = [[NSRunningApplication runningApplicationsWithBundleIdentifier:self.bundleIdentifier] lastObject];
    pid_t pid = [runningApplication processIdentifier]; 
    NSAppleEventDescriptor* addressDescriptor = [[NSAppleEventDescriptor alloc] initWithDescriptorType:typeKernelProcessID bytes:&pid length:sizeof(pid)];

    I wrote up a sample project to demonstrate the problem for the bug report. Anyone interested can download the project here. Note that I have still not come up with a way to reliably get an application into this weird state, so running the sample app might not be particularly useful for most people, but you’re welcome to take a look at the code in any case. The “Use pid address” checkbox in the main window is the relevant one - the other checkboxes there were for a bunch of other variations I was trying out, none of which turned out to be the issue. You’re all welcome to play around with it though. :)

    My speculative theory is that somewhere in the bowels of the Apple event system, there’s some sort of mapping that it keeps to translate bundle identifiers into process IDs so that events can be delivered to the right app, and it keeps this updated as processes get launched/quit. Somehow on 10.8.2, this can get messed up for a particular application, and any events sent to that bundle ID get swallowed up never to be seen again. Sending directly to the process ID still works though, because it doesn’t need to rely on the messed up internal mapping. Update: It looks like there is a background daemon called appleeventsd which seems to be responsible for this. Killing that process resets things, so you can do that rather than doing a full logout. 

    I also saw one other developer, Michael Tsai, mention that he was getting similar reports from another program trying to target his app, SpamSieve, with Apple events. In his case, the events were being sent using typeSignature (the four char code for the app) instead of typeApplicationBundleID, but the symptoms were all the same, so it does appear that this problem can potentially affect any application on OS X once the internal state for it gets messed up.

    One additional tidbit, for those who don’t want to use NSRunningApplication (e.g. targeting 10.5 or earlier), this code can also be used to translate from a bundle identifier to a process ID (courtesy of Michael).

       ProcessSerialNumber psn;
       psn.highLongOfPSN = 0;
       psn.lowLongOfPSN = kNoProcess;
       while (GetNextProcess(&psn) == noErr)
       {
           CFDictionaryRef infoCF = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
           if (infoCF)
           {
               NSDictionary *info = [(id)infoCF autorelease];
               NSString *identifier = [info objectForKey:(id)kCFBundleIdentifierKey];
               if ([identifier isEqual:@"com.c-command.SpamSieve"])
                   return [info objectForKey:@"pid"];
           }
       }
     
    

    So, hopefully this information will come in useful to some other developers out there. If anyone manages to find a way to reproduce this weird state reliably with a particular application, do get in touch, as I’d love to add that info both to this blog post and to my bug report with Apple.

     
  1. dukedusty likes this
  2. tumbler201321 reblogged this from brian-webster
  3. phoenix-hurricane likes this
  4. meimcounting reblogged this from brian-webster and added:
    Activity Monitor...your references will work again.
  5. buysteroidsunitedkingdom likes this
  6. brian-webster posted this