Well, the details caught up with me very fast. By late afternoon, one user e-mailed me a crash report for Zingg! 1.4. After dinner, another user e-mailed me with a simple way to reproduce the crash. 30 minutes later it was fixed, so 1.4.1 has just been published.
This sort of thing is very embarrassing… neither I nor my handful of beta-testers thought of doing the particular choreography that causes the crash. Namely, one has to:
- set either DropStuff or Disk Copy to “always/override”;
- set the “Show” popup to either “No comment” or “Styled name”;
- open the contextual menu and the Zingg! submenu;
- without releasing the mouse, go outside the submenu to close it;
- still without releasing the mouse, go back and reopen the submenu. Boom, as Steve Jobs is fond of saying.
Actually, any application which has exactly 8 or 9 letters and which starts with any hexadecimal digit would cause the crash, but only DropStuff (and, for Jaguar users, Disk Copy) fall within these parameters. At least of the applications I have here.
OK. Why can such a weird combination of circumstances trigger a crash? The answer lies within the way Contextual Menu Plugins work. Briefly, the plugin is a loadable bundle with a few precisely defined entry points. When the user control-clicks (or right-clicks) on some item(s), the “Examine Context” entry point is called with a list of AppleEvent records – one for each item. Such a record resolves to a file path or URL. So I have to loop over the list and by a somewhat complex logic return another list containing parameters for the items I wish to have inserted into the Finder’s contextual menu.
These parameters unfortunately are somewhat restricted; I can pass the menu item’s text, a so-called command-ID, and a few attributes for the item. Later on, if the user selects one of my menu items, the “Handle Selection” entry point is called, this time with that item’s command-ID, and the same list of records that was passed to the first call I described. At this point, I decode the command-ID to get back which application I should open, and I re-decode and encode the record list to pass the list of items to be opened to that application.
All very well, but how to attach application icons to each menu item? This demands more complicated hackery. First, I had to register yet another entry point: a “Menu Event Handler”. Unfortunately, this gets called for the main contextual menu and for every submenu – not just for my own little submenu! So first I had to detect my own submenu among the others; not easy when contents are wildly variable. I finally hit upon the trick of setting an invisible first item with a special encoded title.
Also, for some weird reason, at the point this handler gets called the command-IDs haven’t yet been attached to the menu items – so there’s no way of telling directly to which application each item corresponds. So I simply encoded that information into the menu item texts I return from the “Examine Context” entry point, instead of putting in the actual application name. When the handler sees such an encoded text, it decodes it to obtain the application path, and from that gets both the name and the icon, and puts them into the menu item.
What I conveniently forgot is that the handler gets called every time the submenu is opened – not just when it’s built. So the second time around, the handler dutifully tries to re-decode the menu items again – only now they already contain the actual application names! And for certain names, the decoding will actually proceed with invalid data and crash later on. So, a simple first-time flag and it was fixed. For some reason, in my tests I never played around with the mouse, opening and closing the submenu…