Extracting Hard Disk Information Programmatically Using C++

Discussion in 'Mac Programming' started by holy3daps, May 24, 2007.

  1. holy3daps macrumors newbie

    Joined:
    Jul 14, 2006
    #1
    Hi!

    I'm trying to create an application that reads certain information about the hard disk on a Macintosh. Specifically, I'm interested in the model name, the hard disk serial number, and the total size (in bytes) of the drive. System Profiler gives me this information directly; I've run IORegistryExplorer and I can find some of the information, but not the exact same data that System Profiler provides. And I've barely scratched the surface using the IOKit - I can acquire the proper IOMedia object (I think), but I don't know how to get the information I'm looking for out of it (have executed IORegistryEntryCreateCFProperties on the IOMedia object which returns me a CFDictionary that contains 6 entries, none of whose values or keys is readable as text).

    So does anyone have an example code of something that will

    1) find the main (BSD Name: "disk0") hard drive
    2) find its model name
    3) find its capacity
    4) find its serial number

    C/C++ would be great.

    Thanks!

    karl
     
  2. holy3daps thread starter macrumors newbie

    Joined:
    Jul 14, 2006
    #2
    Hard Disk Information

    Hi!

    Finally managed to find what I want, for those of you who are curious.

    1. I purchased Amit Singh's Mac OS X Internals: A Systems Approach. This gave me a code example that made use of the DiskArbitration Framework. This provided me with information about the specific BSD disk, disk0, that's used to boot the Macintosh, including the model name and the size.

    2. The book also provided a good glimpse of the IOKit, and between that and navigating the elements of IORegistryExplorer, I found the appropriate approach to retrieve hard disk information. Basically, it went like this:

    Code:
    std::string modelName = getHDModelName() // retrieved using DA Framework
    CFMutableDictionaryRef propertyDictionary = CFDictionaryCreateMutable( ... ); // create a dictionary of properties we're looking for
    CFStringRef modelNameRef = CFStringCreateWithCString( ... ); // create a CF String that can be put in the dictionary
    CFDictionarySetValue( propertyDictionary, CFSTR( "Model" ), modelNameRef ); // looking for a key "Model" that has the value of the model string from DA
    CFMutableDictionaryRef matchingDictionary = CFDictionaryCreateMutable( ... ); // create a dictionary of matching properties to search on
    CFDictionarySetValue( matchingDictionary, CFSTR( kIOPropertyMatchKey ), propertyDictionary ); // set the dictionary of properties that we're looking for to be the match criteria for the search
    io_iterator_t serviceIterator;
    kern_return_t kernResult = IOServiceGetMatchingServices( kIOMasterPortDefault, matchingDictionary, &serviceIterator ); // search for all services that match our criteria
    do
    {
        io_service_t sdService = IOIteratorNext( serviceIterator );
        if (0 != sdService)
        {
            CFMutableDictionaryRef serviceProperties;
            kernResult = IORegistryEntryCreateCFProperties( sdService, &serviceProperties, kCFAllocatorDefault, 0 ); // get the properties associated with this entry
            HardDriveInfo hdInfo; // local struct to hold s/n, model
            CFDictionaryApplyFunction( serviceProperties, (CFDictionaryApplierFunction)CFDictionaryValueCycler, &hdInfo ); // cycle over dictionary entries to find s/n & model
        }
    }
    
    typedef struct HardDriveInfo
    {
        std::string serialNumber;
        std::string model;
    } HardDriveInfo;
    
    void CFDictionaryValueCycler( const void* inKey, const void* inValue, void* inContext )
    {
        HardDriveInfo* hdInfoPtr = (HardDriveInfo*)inContext;
        const char* keyStr = CFStringGetCStringPtr( (CFStringRef)inKey, CFStringGetSystemEncoding() ); // get a char* for the key
        if (strstr( keyStr, "Serial Number"))
        {
            const char* valueStr = CFStringGetStringPtr( (CFStringRef)inValue, CFStringGetSystemEncoding() ); // get the value associated with s/n
            hdInfoPtr->serialNumber = string( valueStr );
        }
        if (strstr( keyStr, "Model" ))
        {
            const char* valueStr = CFStringGetStringPtr( (CFStringRef)inValue, CFStringGetSystemEncoding() ); // get the value associated with the model
            hdInfoPtr->model = string( valueStr );
        }
    }
    
    
    This is the best way to get the correct information, as far as I can tell. However, I'm having some difficulty in that apparently the DiskArbitration framework isn't available in 10.3.9 or earlier, which is weird. Although perhaps that's a PowerPC vs Intel thing; the code runs fine on Intel-based machines. I'm going to try it out on my 10.4.X home PPC machine this evening.
     
  3. hawaiian macrumors member

    hawaiian

    #3
    That would seem to make sense as the system calls for the two platforms are no doubt very different. Have you tried using some global data to determine which code you would want to run on different platforms? There are some similar checks that occur throughout linux source code in order to check whether the platform is x86 or PPC. However, that code is done primarily in C as far as I know.

    If you aren't writing operating system code, then perhaps the OS APIs between 10.3.9 and 10.4.x are different enough in semantics such that the functionality that you're trying to obtain isn't working for one?
     
  4. holy3daps thread starter macrumors newbie

    Joined:
    Jul 14, 2006
    #4
    To build and climb a mountain but it wasn't there...

    Hawaiian: Thanks for the thoughts. Actually, the DiskArbitration.framework is literally not there in anything pre-10.4. Which makes deploying this app so much more fun! I'm about to try just blindly installing the framework to those machines that don't have it, right after I flip a message off to the boys and girls at Cupertino to see if they have any better solution to offer. My guess is that the framework probably depends on some other framework, and so on, and so on.....

    I'll keep the thread updated as details become apparent. If I cannot easily (or legally) deploy the DiskArbitration.framework package along with my application on those platforms that require it, I'm yet again going to have to find another way to get the information I need. I think the IOKit.framework is available on OS 10.3 machines; however, I need to determine how to unambiguously target the boot disk drive using IOKit calls, and that has proven challenging. By using the DiskArbitration framework, I could retrieve the model name of the disk independently, and use that information to find the right IOKit entry to get the serial number of the disk. I'm not sure how to do that without DiskArbitration.

    Cheers,

    karl
     
  5. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #5
    To get the BSD name of a disk, use statfs (sample code).

    To get the capacity, you could use FSGetVolumeInfo, and statfs as well seems to provide that info.

    For the other info, you should be able to use IOKit.
     

Share This Page