The fun started yesterday when I was testing an application, and I noticed this little gem in Xcode's debug console:
CoreEndianFlipData: error -4940 returned for rsrc type FREF
(id 128, length 7, native = no)
At about the time this message appeared, my application seemed to stop responding. I didn't know what was causing that, but this mysterious message had to be a clue. I was also, sometimes, getting a "Choose Application" window appearing on the screen, for no readily apparent reason.
I scratched my head for a minute and asked Google what it thought about it. Google wasn't sure. The FREF suggested some old-school pre-X Mac OS programming, but I wasn't doing anything of the sort. The part about endian flipping suggested an Intel vs. PowerPC issue, but I've been doing everything universal for a while now, so that shouldn't be it.
So I asked a team of experts. Thanks to IRC I was able to get some friends on the case, and Daniel Jalkut, Jon Wight, Manton Reece and I spent an interesting and bizarre afternoon trying to figure out what the hell was going on.
Early theories held that, while I wasn't personally using FREFs, perhaps I had included some code that did. That briefly made NDResourceFork a suspect. NDResourceFork is a Cocoa-style interface for dealing with HFS+ resources, and one of its associated classes mentioned FREFs. That ended up being a red herring; I have a graphical front-end app and a back-end worker app, and NDResourceFork is used in the front end while the error was coming from the back end. It's in my project but not actually in the application printing the error message.
So we looked at what the error message appeared to be trying to tell us. Clearly, something must going on with FREFs and/or switching from one endian style to another. Fortunately the experts know a thing or two about old-style Mac stuff, and before long I was setting breakpoints on functions like GetResource() and GetIndResource(), and using PrintResourceChain() in the debugger (functions which, I believe, are older than Mac OS X and which I had never heard of before-- heck, some of them aren't even documented anymore). I also found I could reproduce the problem in the debugger, and it's always nice when the application behaves the same way in a debugger as when it's running normally.
That led to a surprising discovery, that the mystery message was occurring when my application was making AppleScript calls to another application via NSAppleScript. In a way that just confused things for a while, though. There didn't seem to be any reason for this script to be using FREFs. And I verified that my application, the target application, and the AppeScript (compiled with osacompile) were all universal binaries, so there shouldn't have been any question of endianness. It was about at this time I noticed that, for some reason, the bug only manifested itself when the AppleScript was targeting OmniGraffle Pro. Cue ominous music here.
The debugging also led to some seriously weird results, in cases. Trying PrintResourceChain() at the gdb prompt showed resources loaded from completely unrelated applications-- not mine, not the target app, not the source of a scripting addition, not anything that should have been remotely involved.
Trying to pin down exactly when the mystery message appeared, Daniel suggested I set a breakpoint on write(), which is pretty near the lowest-level print statement that should get used by nearly everything printing anything. That was when I regretted all of those NSLog() statements I had added for what Jon Wight called "nuke 'em from orbit style debugging". Some careful disabling and reenabling of breakpoints got me through that without too much trouble, though. And, aha! CoreEndianFlipData appeared in the stack trace! And that was a direct result of my own NSAppleScript call, albeit about 45 levels down in the stack (not an exaggeration, BTW) from there. As for what was going on in between, well, that took some interpreting.
Daniel and Jon both noticed the presence of AEVTsysoppcb in the stack which is, they tell me, AppleScript deciding it needs to ask you where the target application is. The "choose application" window loads icons for all applications. And THAT, apparently, is where FREFs come into all of this. Remember the FREFs? This started with an error message about FREFs. I was now able to start reproducing the error message from Script Editor, which simplified things a bit and, frankly, made me feel a little better by knowing this might not be my fault.
Reducing the AppleScript to the bare essentials needed to demonstrate the behavior, I get this:
if application "OmniGraffle Pro" is running then beep end if
Do this in Script Editor, and a "Choose Application" window appears, asking "Where is OmniGraffle Pro?", even if you have it installed. Click "Cancel", and Script Editor crashes. Watch the console, and you'll see the mystery message that started all of this. Of course OmniGraffle's full name is "OmniGraffle Professional", but fixing that doesn't change the behavior.
Out of curiosity I tried a bunch of other applications. The only other one I've found that causes this is RealPlayer. There might be others.
Apparently then, the chain of events is:
- My app calls its AppleScript, targeting OmniGraffle
- The AppleScript tries to find out if OmniGraffle is running
- For some reason it can't figure this out, because it can't find OmniGraffle. I can only guess this is because of something weird in OmniGraffle's Info.plist, though I'm not sure what.
- AppleScript helpfully asks the user where to find it.
- The "Choose Application" window, in trying to find application icons, runs smack into that unrelated application that PrintResourceChain showed. It prints out a weird message about FREFs and endianness
- If the user cancels, AppleScript freaks out and explodes, killing innocent bystanders like Script Editor or my application.
Now, all I want to do is run this damn AppleScript. If I can find a workaround, I don't actually care why OmniGraffle is making the existing script choke. Daniel pointed out that if an AppleScript says "application appname" somewhere, it causes a full name resolution for the application. If I could make the script check for a running application without that, no name resolution would happen, and tragedy would be avoided. The Pre-Leopard way to do this does just that:
tell application "System Events" return first application process of application "System Events" whose name is "OmniGraffle" end tell
Unfortunately it's not workable for me. My script's not actually about OmniGraffle, it targets a bunch of different applications, and it gets the application name from NSWorkspace. NSWorkspace tells me that OmniGraffle is named "OmniGraffle Pro". But AppleScript sees it as "OmniGraffle Professional", and if I use anything but that then it can't find the process. AppleScript then reports that it's not running, even if it is. There's no name resolution, which is nice, but there's also no reliable result.
I'm still beating on this a bit, but it looks like another Leopard-ism may save the day. Beginning with 10.5 it's possible to target an AppleScript based on an application's bundle ID instead of its name. Bundle IDs are, fortunately, consistent where application names may not be. And since they're not application names, they don't get resolved in the same way. So I can do something like this:
if application id "com.omnigroup.OmniGrafflePro" is running then beep end if
And... it works! I think. I need to do some testing to see if it's as reliable as I need it to be. But it looks like what I need.
I'll be reporting this to Apple, because whatever OmniGraffle is doing, AppleScript shouldn't crash and burn like that. I'll probably also report it to Omni, who may well be interested to know.
I'd also like to thank Daniel, Jon, and Manton for taking so much time to help track this down. I don't think I would have got this far without their help.
Update: Daniel Jalkut pointed out that it's possible to address applications by bundle ID in AppleScript on Mac OS X 10.4, although it involves what Apple's AppleScript release notes describe as "...a multi-line incantation using Finder." That incantation turns out to be something like this:
tell application "Finder" set appname to displayed name of application file id "com.omnigroup.OmniGrafflePro" end tell tell application "System Events" if exists process appname then beep end if end tell






Sat, 01/26/2008 - 19:25
This is very good info to know, especially with the fact that there are a few new Mac apps with the same name floating out there [eg: Flow, Iris, etc]. Congrats on finding the issue and publishing it - I'm sure it will save time for a lot of developers using AppleScript out there!
Sat, 01/26/2008 - 20:08
I too encountered endian issues with AppleEvents in Tiger (I haven't tested it in Leopard) but for me it stemmed from the fact that AppleEvents use "four letter codes" eg 'FREF', 'abso' ,'indx' etc
My issue that I was trying to pass in a four letter code 'all ' as bytes: parameter to a NSAppleEventDescriptor and it was completely bombing on me. My workaround was to store it so a int variable first and then pass the variable in.
eg int aKludgeVariable = 'all ';
This assignment took care of all the issues.
perhaps this is related.
or just an indication that AppleEvents are not fully safe with endian issues.
Sat, 01/26/2008 - 20:17
This is definitely a bug that should be fixed, but I'm curious about something. One of the (nice, in my opinion) things that OmniGroup does is use language localization so that the name of the application you see is not the actual name of the OmniGraffle application itself. Thus the real name of the application might be something like "OmniGraffle 4.app", but show up as simply "OmniGraffle". This means I can keep *both* OmniGraffle 4 in my /Applications folder, while keeping and trying out the OmniGraffle 5 beta alongside it.
I should point out that I'm seeing this with the standard version of OmniGraffle, not Pro, and it doesn't explain RealPlayer as far as I know. But what happens if you use the "real" name of the program? In my case, I *don't* crash if I change the name to "OmniGraffle 4" instead, but do otherwise.
Sun, 01/27/2008 - 17:52
Bruce, that's a good point, and when I change the application name to "OmniGraffle Professional 4" then the initial script works normally. That would seem to explain why AppleScript can't find OmniGraffle in my initial script. Thanks for pointing that out, I didn't fully understand that part of the sequence.
Sat, 01/26/2008 - 21:33
Note that this crash has nothing to do with the "FREF" resource (file reference), the logging of the CoreEndian flipper, endianess, or OmniGraffle itself.
You can reproduce the problem by simply running the below applescript and hitting cancel in the "Choose Application" dialog.
if application "Surprise Buttsecks" is running thenbeep
end if
So the steps are:
The error you are seeing logged is because the FREF for some application the choose application dialog wants to show is too short (errCoreEndianDataTooShortForFormat) and is invalid. AppleScript logs a message on ICBMs and goes on to the next method of finding an icon for the application.
This crash appears on PowerPCs as well, therefore it's not related to the flippers. And of course, OmniGraffle has no FREF resource, so it's not related to OmniGraffle (not that it matters as the crash occurs even if OmniGraffle isn't on the computer).
The Choose Application method in AppleScript is borked to high heaven. It shows irrelevant applications and applications that are part of Time Machine backups. So it's always best to avoid ever having to show the user the dialog by using
tell application id "com.omnigroup.OmniGrafflePro"as this will return an error if it is not available instead of crashing or asking the user to find the application (then crashing).Sat, 01/26/2008 - 21:43
Also note the
NSApplicationBundleIdentifierin the dictionary-[NSWorkspace launchedApplications]returns. This means you do not need to use the application name as you have the bundle ID.Sun, 01/27/2008 - 17:54
Right, it looks like the bundle ID is the way to go, I'll just need to pound on it a little and see if it's as robust as it looks like it should be. As Vince LaMonica pointed out, it's a better idea anyway, given that bundle IDs should be unique even if app names aren't.
Sat, 01/26/2008 - 22:04
Wow, I didn't expect the bug would turn into such a great blog post.
Mankind owes you a collective thank-you for taking the time to post ;-)
I love these kind of bugs. Ones where the cause & effect are lightyears apart and you have to do your (possibly with help from friends) best Sherlock Holmes impression to understand what the heck is going on.
Well done getting to the bottom of it.
Sun, 01/27/2008 - 05:27
The error message means that a the FREF resource flipper in CoreServices was invoked with an FREF resource which is shorter than the resource format CoreServices knows about. This is often due to an obscure or undocumented but actually supported format which wasn't known to the engineer writing the resource flipper (often me).
If you can figure out which FREF it is, file a bug to point Apple to it and the fix should be straightforward.
Sun, 01/27/2008 - 18:00
Eric, as Daniel pointed out, it's not an Apple application that causes the problem, but a third-party app which was questionable anyway. If I remove that application, I still get the application chooser window, but Script Editor doesn't crash anymore. I guess I'm not sure if it's worth sending this in to Apple or not; on the one hand, the broken app isn't theirs, but on the other hand it doesn't seem like AppleScript should react so violently to it.
I'm also still puzzled by the mention of endian flipping; would it be because the app with the bad FREF is a PowerPC application?
Sun, 01/27/2008 - 10:42
The script: if application "SurpriseMe" is running then beep 2 works perfectly for me (PM dual-core G5, Leopard 10.5.1)
Sun, 01/27/2008 - 18:02
Adam, as Eric Albert pointed out, the crashing only happens because I have (had) an application on my Mac with a bad FREF. If you don't have one (and you probably don't), you should still see the app chooser but have no negative side effects of canceling from it.
Sun, 01/27/2008 - 11:06
I throw up a little bit every time I use a Carbon Event Manager API (or any antiquated Carbon API for that matter).
I aways referred to debugging with printf or NSLog as "Ghetto Debugging" :)
Sun, 01/27/2008 - 12:20
Eric: I think the FREF in question here was from a 3rd party app (an eSellerate Installer). It just so happened that the "choose application" dialog being put up by AppleScript enumerates every dang app on the system.
Sun, 01/27/2008 - 18:08
Just in the interest of not blaming parties that aren't responsible-- it wasn't a legitimate eSellerate installer, it was an installer for a cracked version of some eSellerate libraries. Since I use eSellerate I downloaded this a while ago to see if it was a potential problem, and then kind of forgot about it. I don't believe this installer was created with any eSellerate or MindVision tools.
Sun, 01/27/2008 - 13:22
Unless you need your AppleScripts to be user-editable, another solution would be to do away with AppleScript and its stupid 'Where is application...?' dialogs entirely and just send Apple events directly from ObjC. Leopard's Scripting Bridge isn't great, but may do if you don't need Tiger support. Alternatively, objc-appscript (see my sig.) is still a little rough in places, but it's polishing up and should work on anything from Panther onwards.
HTH
has
--
http://appscript.sourceforge.net
http://rb-appscript.rubyforge.org
Sun, 01/27/2008 - 18:06
Thanks, I'll check these out. In the short term using bundle IDs looks like it covers me, but I'll definitely check into these options for future work.
Mon, 01/28/2008 - 12:07
I'd file the bug about the crash, and enclose the offending application. It may be a third-party application, but the system shouldn't choke on it.
Thu, 02/07/2008 - 22:39
Wow, that's a crazy one. Thanks for taking the time to write it up.
Post new comment