Q: Some applications fool my contextual menu plug-in into adding its menu items at inappropriate times. Then, when the user selects one of my items, my plug-in understandably gets confused when trying to extract the offered data and process it. What gives?
A: This usually occurs as a result of both complex data in the descriptor offered to your plug-in and a bug in Apple Event Manager.
Sometimes an application will offer your plug-in a descriptor of
typeAERecord . This kind of descriptor is really just a fancy
descriptor list which associates a keyword with each list item.
If your code doesn't detect that it has been passed a typeAERecord ,
it may attempt to do something which coerces it to another data type.
The coercion may not be explicit; remember, asking for a descriptor list item
as a specific type can implicitly coerce the item into the specified type.
Coercion is ordinarily not a bad thing, but there are two reasons why it
is bad in this case:
- Apple Event Manager has a bug such that it will appear to
coerce
typeAERecord to anything requested without
actually performing any coercion.
- Since
typeAERecord is a list, you'd be better off traversing its items to see what they are, rather than attempting to coerce the whole list into a single item.
So, suppose your plug-in is interested in processing file system objects
(files, folders, volumes). It's probably going to want to determine
whether it has been offered typeAlias . If it implicitly converts typeAERecord to typeAlias , it is not only going to exercise the bug in the Apple Event Manager and get confused, but even if the bug were not present, your plug-in would be throwing away an opportunity to process an an embedded list of typeAlias descriptors.
Here's some code which shows how to properly traverse embedded lists.
A side-effect of this code is to avoid the Apple Event Manager bug.
static pascal OSStatus SearchForAcceptableDescriptors
(DescType acceptableDescType, const AEDesc *desc, Boolean *allOK)
{
OSStatus err = noErr;
*allOK = true;
if (desc->descriptorType != acceptableDescType)
{
if (desc->descriptorType == typeAERecord
|| desc->descriptorType == typeAEList)
{
long index;
if (!(err = AECountItems (desc,&index)) && index) do
{
AEDesc nthDesc;
AEKeyword keyword;
OSStatus err2;
err = AEGetNthDesc (desc,index,
typeWildCard,&keyword,&nthDesc);
if (err) break;
err = SearchForAcceptableDescriptors(
acceptableDescType,&nthDesc,allOK);
err2 = AEDisposeDesc (&nthDesc);
if (err) break;
err = err2;
if (err) break;
if (!*allOK) break;
}
while (--index);
}
else
{
AEDesc coerced;
if (!(err = AECoerceDesc (desc,acceptableDescType,&coerced)))
err = AEDisposeDesc (&coerced);
else if (err == errAECoercionFail)
{
err = noErr;
*allOK = false;
}
}
}
return err;
}
|
To this function, you should pass the pointer to the descriptor offered to your plug-in, the descriptor type of the descriptors your plug-in is
willing to process, and a pointer to a Boolean (which will be set
according to whether the descriptor contains only descriptors of the
specified type). When you take action on the descriptors, you'll need to
recurse similarly.
[Feb 08 1999]
|