2017-07-18 74 views
0

我试图发现MAC OSX下的USB大容量存储设备。我希望获得设备类,并在此基础上决定设备是否是大容量存储设备。但是,对于我所有的USB闪存驱动器,我得到的设备类== 0,这似乎是一个复合设备。请帮助我弄清楚,我在做什么错误,或者,也许还有其他可靠的方式来发现USB Mass Storage设备(我需要获得PID,VID和安装点)。这里是我的代码:OS X,USB大容量存储发现

#import <iostream> 
#import <IOKit/IOkitLib.h> 
#import <IOKit/usb/IOUSBLib.h> 
#import <IOKit/IOCFPlugIn.h> 
#import <IOKit/usb/USBSpec.h> 
#import <CoreFoundation/CoreFoundation.h> 
#import <Foundation/Foundation.h> 

int main(int argc, const char * argv[]) { 
CFMutableDictionaryRef matchingDictionary = NULL; 
io_iterator_t foundIterator = 0; 
io_service_t usbDevice; 
matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName); 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &foundIterator); 
for(usbDevice = IOIteratorNext(foundIterator); usbDevice; usbDevice = IOIteratorNext(foundIterator)) 
{ 
    IOCFPlugInInterface** plugin = NULL; 
    SInt32 theScore=0; 
    IOReturn err; 

    err = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &theScore); 
    if (err!= 0){ 
     std::cout<<"error, error code: "<<err_get_code(err) <<std::endl; 
    } 
    else if (plugin && *plugin) 
    { 
     IOUSBDeviceInterface182** usbInterface = NULL; 
     (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),(LPVOID*)&usbInterface); 
     (*plugin)->Release(plugin); 
     if (usbInterface && *usbInterface) 
     { 
      UInt8 devClass; 
      UInt8 devSubClass; 
      UInt16 productId; 
      UInt16 vendorID; 

      //here I'm getting 0 for all my USB flash cards 
      (*usbInterface)->GetDeviceClass(usbInterface,&devClass); 
      (*usbInterface)->GetDeviceVendor(usbInterface, &vendorID); 
      (*usbInterface)->GetDeviceProduct(usbInterface, &productId); 
      (*usbInterface)->GetDeviceSubClass(usbInterface, &devSubClass); 
      std::cout<<"device class: "<<+devClass<<std::endl; 
      std::cout<<"device sub class: "<<+devSubClass<<std::endl; 
      std::cout<<"vendor ID: "<<vendorID<<std::endl; 
      std::cout<<"product ID: "<<productId<<std::endl; 
     } 
    } 
    IOObjectRelease(usbDevice); 
} 
IOObjectRelease(foundIterator); 
return 0; 

}

回答

0

对于我来说,通过USB迭代以下方式在OSX工作:

void scanUsbMassStorage() 
{ 
CFMutableDictionaryRef matchingDictionary = IOServiceMatching(kIOUSBInterfaceClassName); 
//now specify class and subclass to iterate only through USB mass storage devices: 
CFNumberRef cfValue; 
SInt32 deviceClassNum = kUSBMassStorageInterfaceClass; 
cfValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &deviceClassNum); 
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceClass), cfValue); 
CFRelease(cfValue); 
    SInt32 deviceSubClassNum = kUSBMassStorageSCSISubClass; 
cfValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &deviceSubClassNum); 
CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBInterfaceSubClass), cfValue); 
CFRelease(cfValue); 
//NOTE: if you will specify only device class and will not specify subclass, it will return an empty iterator, 
//and I don't know how to say that we need any subclass. 
//BUT: all the devices I've check had kUSBMassStorageSCSISubClass 
io_iterator_t foundIterator = 0; 
io_service_t usbInterface; 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &foundIterator); 
//iterate through USB mass storage devices 
for(usbInterface = IOIteratorNext(foundIterator); usbInterface; usbInterface = IOIteratorNext(foundIterator)) 
{ 
    CFTypeRef bsdName = IORegistryEntrySearchCFProperty(usbInterface, 
                 kIOServicePlane, 
                 CFSTR(kIOBSDNameKey), 
                 kCFAllocatorDefault, 
                 kIORegistryIterateRecursively); 

    CFTypeRef serial = IORegistryEntrySearchCFProperty(usbInterface, 
                 kIOServicePlane, 
                 CFSTR(kUsbSerialPropertyName), 
                 kCFAllocatorDefault, 
                 kIORegistryIterateRecursively|kIORegistryIterateParents); 

    CFTypeRef pid = IORegistryEntrySearchCFProperty(usbInterface, 
                kIOServicePlane, 
                CFSTR(kUsbPidPropertyName), 
                kCFAllocatorDefault, 
                kIORegistryIterateRecursively|kIORegistryIterateParents); 

    CFTypeRef vid = IORegistryEntrySearchCFProperty(usbInterface, 
                kIOServicePlane, 
                CFSTR(kUsbVidPropertyName), 
                kCFAllocatorDefault, 
                kIORegistryIterateRecursively|kIORegistryIterateParents); 

    //now we can perform checks and casts from CFTypeRef like this: 
    std::string filePathStr; 
    std::string serialStr; 
    uint16_t pidInt; 
    uint16_t vidInt; 
    //getMountPathByBSDName - see below 
    bool stillOk = getMountPathByBSDName(bsdName, filePath); 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2AsciiString(serial, serialStr); 
    } 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2uint16(pid, pidInt); 
    } 
    if (stillOk) 
    { 
     stillOk = CFTypeRef2uint16(vid, vidInt); 
    } 
    if (stillOK) 
    { 
     //can do something with the values here 
    } 
} 

充分利用BSD名称安装路径,虽然是意外棘手对我而言,如果有人知道更好的方法,请分享它。这是我的方法。有大量的代码,但至少这个工作在几个不同版本的OSX:

bool getMountPathByBSDName(CFTypeRef bsdName, std::string& dest) 
{ 
std::list<std::string> bsdNames; 
//for getChildBsdNames - see below =) 
getChildBsdNames(bsdName, bsdNames); 
DASessionRef session = DASessionCreate(kCFAllocatorDefault); 
if (!session) 
{ 
    return false; 
} 
for (const auto& bsdNameStr : bsdNames) 
{ 
    DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, bsdNameStr.c_str()); 
    if (disk) 
    { 
     CFDictionaryRef diskInfo = DADiskCopyDescription(disk); 
     if (diskInfo) 
     { 
      char buf[1024]; 
      CFURLRef fspath = (CFURLRef)CFDictionaryGetValue(diskInfo, kDADiskDescriptionVolumePathKey); 
      if (CFURLGetFileSystemRepresentation(fspath, false, (UInt8 *)buf, 1024)) 
      { 
       //for now, return the first found partition 
       dest = std::string(buf); 
       return true; 
      } 
     } 
    } 
} 
return false; 
} 

最后 - getChildBsdNames功能:

void getChildBsdNames(CFTypeRef bsdName, std::list<std::string>& tgtList) 
{ 
std::string bsdNameStr; 
if(!CFTypeRef2AsciiString(bsdName, bsdNameStr)) 
{ 
    return; 
} 
CFDictionaryRef matchingDictionary = IOBSDNameMatching(kIOMasterPortDefault, 0, bsdNameStr.c_str()); 
io_iterator_t it; 
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &it); 
io_object_t service; 
while ((service = IOIteratorNext(it))) 
{ 
    io_iterator_t children; 
    io_registry_entry_t child; 

    IORegistryEntryGetChildIterator(service, kIOServicePlane, &children); 
    while((child = IOIteratorNext(children))) 
    { 
     CFTypeRef bsdNameChild = IORegistryEntrySearchCFProperty(child, 
                   kIOServicePlane, 
                   CFSTR (kIOBSDNameKey), 
                   kCFAllocatorDefault, 
                   kIORegistryIterateRecursively); 
     std::string bsdNameChildStr; 
     if (CFTypeRef2AsciiString(bsdNameChild, bsdNameChildStr)) 
     { 
      tgtList.push_back(bsdNameChildStr); 
     } 
    } 
} 
/** 
* The device could get name 'disk1s1, or just 'disk1'. In first case, the original bsd name would be 
* 'disk1', and the child bsd name would be 'disk1s1'. In second case, there would be no child bsd names, 
* but the original one is valid for further work (obtaining various properties). 
*/ 
if (tgtList.empty()) 
{ 
    tgtList.push_back(bsdNameStr); 
} 
} 

附:有一个基于事件的机制,但由于某些原因,这不是一个解决方案。我选择了轮询,但修改此代码以使其基于事件并不难。但是要小心,甚至可能会早于设备的安装时间。