macOS系统如何监听USB设备连接和断开呢?
需要先区分是监听USB设备还是disk(U盘、SD卡等),如果为后者,则可以直接通过系统通知,而不需要调用IOKit
监听。
监听可装载的USB设备(U盘、SD卡等)
1.注册通知
// Notification for Mountingthe USB device
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(deviceMounted:) name: NSWorkspaceDidMountNotification object: nil];
// Notification for Un-Mountingthe USB device
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(deviceUnmounted:) name: NSWorkspaceDidUnmountNotification object: nil];
2.设备挂载后收到通知
NSArray* devices = [[NSWorkspacesharedWorkspace] mountedRemovableMedia];
3.获取所有已挂载USB的卷路径之后,识别已挂载USB
#import <IOKit/usb/IOUSBLib.h>
#import <IOKit/IOCFPlugIn.h>
// The following code will return an array having configured Ids and Name of all the mounted USB devices.
-(NSArray *) deviceAttributes {
mach_port_t masterPort;
CFMutableDictionaryRef matchingDict;
NSMutableArray * devicesAttributes = [NSMutableArray array];
kern_return_t kr;
// Create a master port for communication with the I/O Kit
kr = IOMasterPort (MACH_PORT_NULL, &masterPort);
if (kr || !masterPort) {
NSLog (@"Error: Couldn't create a master I/O Kit port(%08x)", kr);
return devicesAttributes;
}
// Set up matching dictionary for class IOUSBDevice and its subclasses
matchingDict = IOServiceMatching (kIOUSBDeviceClassName);
if (!matchingDict) {
NSLog (@"Error: Couldn't create a USB matching dictionary");
mach_port_deallocate(mach_task_self(), masterPort);
return devicesAttributes;
}
io_iterator_t iterator;
IOServiceGetMatchingServices (kIOMasterPortDefault, matchingDict, &iterator);
io_service_t usbDevice;
// Iterate for USB devices
while (usbDevice = IOIteratorNext (iterator)) {
IOCFPlugInInterface**plugInInterface = NULL;
SInt32 theScore;
// Create an intermediate plug-in
kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &theScore);
if ((kIOReturnSuccess != kr) || !plugInInterface)
printf("Unable to create a plug-in (%08x)\n", kr);
IOUSBDeviceInterface182 **dev = NULL;
// Create the device interface
HRESULT result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&dev);
if (result || !dev)
printf("Couldn't create a device interface (%08x)\n", (int) result);
UInt16 vendorId;
UInt16 productId;
UInt16 releaseId;
// Get configuration Ids of the device
(*dev)->GetDeviceVendor(dev, &vendorId);
(*dev)->GetDeviceProduct(dev, &productId);
(*dev)->GetDeviceReleaseNumber(dev, &releaseId);
UInt8 stringIndex;
(*dev)->USBGetProductStringIndex(dev, &stringIndex);
IOUSBConfigurationDescriptorPtr descriptor;
(*dev)->GetConfigurationDescriptorPtr(dev, stringIndex, &descriptor);
// Get Device name
io_name_t deviceName;
kr = IORegistryEntryGetName (usbDevice, deviceName);
if (kr != KERN_SUCCESS) {
NSLog (@"fail 0x%8x", kr);
deviceName[0] = '\0';
}
NSString * name = [NSStringstringWithCString:deviceName encoding:NSASCIIStringEncoding];
// data will be initialized only for USB storage devices.
// bsdName can be converted to mounted path of the device and vice-versa using DiskArbitration framework, hence we can identify the device through it's mounted path
CFTypeRef data = IORegistryEntrySearchCFProperty(usbDevice, kIOServicePlane, CFSTR("BSD Name"), kCFAllocatorDefault, kIORegistryIterateRecursively);
NSString* bsdName = [(NSString*)data substringToIndex:5];
NSString* attributeString = @"";
if(bsdName)
attributeString = [NSString stringWithFormat:@"%@,%@,0x%x,0x%x,0x%x", name, bsdName, vendorId, productId, releaseId];
else
attributeString = [NSString stringWithFormat:@"%@,0x%x,0x%x,0x%x", name, vendorId, productId, releaseId];
[devicesAttributes addObject:attributeString];
IOObjectRelease(usbDevice);
(*plugInInterface)->Release(plugInInterface);
(*dev)->Release(dev);
}
// Finished with master port
mach_port_deallocate(mach_task_self(), masterPort);
masterPort = 0;
return devicesAttributes;
}
监听所有USB设备(包括键盘、鼠标等)
- (void)ListenUsbConnectEvent {
// dictionary
CFMutableDictionaryRef matchingDict = matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
// create notification
IONotificationPortRef notificationObject; // notification object to listen
mach_port_t masterPort = 0; // received from IOMasterPort
notificationObject = IONotificationPortCreate(masterPort);
// create run loop
CFRunLoopSourceRef notificationRunLoopSource;
// use notification obejct received from notificationPortCreate
notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationObject);
CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
io_iterator_t iter;
CFRetain(matchingDict);
IOServiceAddMatchingNotification(notificationObject, kIOFirstMatchNotification, matchingDict, isAttached, (__bridge void*)self, &iter);
isAttached(NULL, iter);
IOServiceAddMatchingNotification(notificationObject,kIOTerminatedNotification, matchingDict, isRemoved, (__bridge void*)self, &iter);
isRemoved(NULL, iter);
}
void isAttached(void *refcon, io_iterator_t iterator) {
io_service_t usbDevice;
while((usbDevice = IOIteratorNext(iterator))) {
io_name_t name;
IORegistryEntryGetName(usbDevice, name);
printf("\tName:\t\t%s\n", (char *)name);
CFNumberRef idProduct = (CFNumberRef)IORegistryEntrySearchCFProperty(usbDevice, kIOServicePlane, CFSTR("idProduct"), kCFAllocatorDefault, 0);
uint16_t PID;
CFNumberGetValue(idProduct, kCFNumberSInt16Type, (void *)&PID);
printf("\tidProduct:\t0x%x\n", PID);
IOObjectRelease(usbDevice);
CFRelease(idProduct);
}
}
void isRemoved(void *refcon, io_iterator_t iterator) {
io_service_t usbDevice;
while((usbDevice = IOIteratorNext(iterator))) {
io_name_t name;
IORegistryEntryGetName(usbDevice, name);
printf("\tName:\t\t%s\n", (char *)name);
CFNumberRef idProduct = (CFNumberRef)IORegistryEntrySearchCFProperty(usbDevice, kIOServicePlane, CFSTR("idProduct"), kCFAllocatorDefault, 0);
uint16_t PID;
CFNumberGetValue(idProduct, kCFNumberSInt16Type, (void *)&PID);
printf("\tidProduct:\t0x%x\n", PID);
IOObjectRelease(usbDevice);
CFRelease(idProduct);
}
}
// Just for testing
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// 监听USB事件,需要在主线程中执行
[self ListenUsbConnectEvent];
}
获取到设备后可根据自身需要获取其VID、Pid及序列号等,根据自己业务需要进行后续数据处理。
参考资料
本文为原创文章,版权归字节时代所有,欢迎分享本文,转载请保留出处!