Search code examples
cocoaiokit

How can I get the icon for a storage device in Mac OS X?


I've found my devices using IOServiceGetMatchingServices and got the property dictionary like this:

kernResult = IORegistryEntryCreateCFProperties(nextMedia,
                    (CFMutableDictionaryRef *)&props,
                                              kCFAllocatorDefault, 0);

From that dictionary I can extract the informations for the icons:

NSString *bId = [props valueForKeyPath:@"IOMediaIcon.CFBundleIdentifier"];
NSString *rFile = [props valueForKeyPath:@"IOMediaIcon.IOBundleResourceFile"];

Those two give me this (as an example):

com.apple.iokit.IOStorageFamily   (Bundle identifier)
Internal.icns                     (Resource File)

I tried to extract the icon using this method:

NSBundle *bundleWithIcon = [NSBundle bundleWithIdentifier:bId];
NSString *iconPath = [bundleWithIcon pathForResource:rFile ofType:nil];

But bundleWithIcon is nil.

Is this even the correct method to get the icon?

I think I have to somehow load the bundle to be able to load it with bundleWithIdentifier, how can I do this?

PS: There's another question which (I think) tries to ask the same thing, but only asks for bundles, not if this is the correct way.


Solution

  • Just recently Andrew Myrick answered a similar question on the darwin-dev mailing list:

    KextManagerCreateURLForBundleIdentifier() in <IOKit/kext/KextManager.h> may be of use, though I believe it only works for kexts that are either 1) loaded, or 2) in /S/L/E/. Here is the Snow Leopard headerdoc:

    /*!
     * @function KextManagerCreateURLForBundleIdentifier
     * @abstract Create a URL locating a kext with a given bundle identifier.
     *
     * @param    allocator
     *           The allocator to use to allocate memory for the new object.
     *           Pass <code>NULL</code> or <code>kCFAllocatorDefault</code>
     *           to use the current default allocator.
     * @param    kextIdentifier
     *           The bundle identifier to look up.
     *
     * @result
     * A CFURLRef locating a kext with the requested bundle identifier.
     * Returns <code>NULL</code> if the kext cannot be found, or on error.
     *
     * @discussion
     * Kexts are looked up first by whether they are loaded, second by version.
     * Specifically, if <code>kextIdentifier</code> identifies a kext
     * that is currently loaded,
     * the returned URL will locate that kext if it's still present on disk.
     * If the requested kext is not loaded,
     * or if its bundle is not at the location it was originally loaded from,
     * the returned URL will locate the latest version of the desired kext,
     * if one can be found within the system extensions folder.
     * If no version of the kext can be found, <code>NULL</code> is returned.
     */
    CFURLRef KextManagerCreateURLForBundleIdentifier(
        CFAllocatorRef allocator,
        CFStringRef    kextIdentifier);
    

    Note that prior to Snow Leopard, it may only work for kexts in /S/L/E; the API existed, but there was no headerdoc describing its behavior.

    For me this worked really well on Mac OS X 10.5.