mac osx 中 runModalWindow 阻塞usb数据接收线程

来源:互联网 发布:襄县农村淘宝招聘 编辑:程序博客网 时间:2024/06/17 01:26

在mac osx 下使用IOHidInterface 与 USB Hid进行通信


USBHid.h

#ifndef USBHid_h#define USBHid_h#import <Foundation/Foundation.h>#include <IOKit/hid/IOHIDLib.h>#include <IOKit/hid/IOHIDKeys.h>@protocol UsbHIDDelegate <NSObject>@optional- (void)usbhidDidRecvData:(uint8_t*)recvData length:(CFIndex)reportLength;- (void)usbhidDidMatch;- (void)usbhidDidRemove;@end@interface UsbHID : NSObject {    IOHIDManagerRef managerRef;    IOHIDDeviceRef deviceRef;}@property(nonatomic,strong)id<UsbHIDDelegate> delegate;@property(nonatomic) BOOL connected;+ (UsbHID *)sharedManager;- (BOOL)connectHID;- (BOOL)disconnectHID;- (BOOL)senddata:(Byte *)outbuffer size:(int)size;- (int)readdata:(Byte *)readbuffer size:(int)size;- (IOHIDManagerRef)getManageRef;- (void)setManageRef:(IOHIDManagerRef)ref;- (IOHIDDeviceRef)getDeviceRef;- (void)setDeviceRef:(IOHIDDeviceRef)ref;@end#endif /* USBHid_h */



USBHid.m

#import <Foundation/Foundation.h>#import <AppKit/AppKit.h>#import "UsbHID.h"@implementation UsbHIDstatic UsbHID *_sharedManager = nil;@synthesize delegate;@synthesize connected;static void MyInputCallback(void* context, IOReturn result, void* sender, IOHIDReportType type, uint32_t reportID, uint8_t *report,CFIndex reportLength) {        id<UsbHIDDelegate> dele = [[UsbHID sharedManager] delegate];    [dele usbhidDidRecvData:report length:reportLength];}static void Handle_DeviceMatchingCallback(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {    [[UsbHID sharedManager] setDeviceRef:inIOHIDDeviceRef];   char *inputbuffer = malloc(64);    IOHIDDeviceRegisterInputReportCallback([[UsbHID sharedManager]getDeviceRef], (uint8_t*)inputbuffer, 64, MyInputCallback, NULL);        [UsbHID sharedManager].connected = true;        NSLog(@"%p设备插入,现在usb设备数量:%ld",(void *)inIOHIDDeviceRef,USBDeviceCount(inSender));    [[[UsbHID sharedManager] delegate] usbhidDidMatch];}static void Handle_DeviceRemovalCallback(void *inContext,IOReturn inResult,void *inSender,IOHIDDeviceRef inIOHIDDeviceRef) {    [[UsbHID sharedManager] setDeviceRef:nil];        [UsbHID sharedManager].connected = false;    NSLog(@"%p设备拔出,现在usb设备数量:%ld",(void *)inIOHIDDeviceRef,USBDeviceCount(inSender));    [[[UsbHID sharedManager] delegate] usbhidDidRemove];}static long USBDeviceCount(IOHIDManagerRef HIDManager){    CFSetRef devSet = IOHIDManagerCopyDevices(HIDManager);    if(devSet)        return CFSetGetCount(devSet);    return 0;}+(UsbHID *)sharedManager {    @synchronized( [UsbHID class] ){        if(!_sharedManager)            _sharedManager = [[self alloc] init];        return _sharedManager;    }    return nil;}+(id)alloc {    @synchronized ([UsbHID class]){        NSAssert(_sharedManager == nil,                 @"Attempted to allocated a second instance");        _sharedManager = [super alloc];        return _sharedManager;    }    return nil;}- (id)init {    self = [super init];    if (self) {        managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);        IOHIDManagerScheduleWithRunLoop(managerRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode);        IOReturn ret = IOHIDManagerOpen(managerRef, 0L);        if (ret != kIOReturnSuccess) {            NSAlert *alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"打开设备失败!"];            [alert runModal];            return self;        }        const long vendorID = 0x0483;        const long productID = 0x5750;        NSMutableDictionary* dict= [NSMutableDictionary dictionary];        [dict setValue:[NSNumber numberWithLong:productID] forKey:[NSString stringWithCString:kIOHIDProductIDKey encoding:NSUTF8StringEncoding]];        [dict setValue:[NSNumber numberWithLong:vendorID] forKey:[NSString stringWithCString:kIOHIDVendorIDKey encoding:NSUTF8StringEncoding]];        IOHIDManagerSetDeviceMatching(managerRef, (__bridge CFMutableDictionaryRef)dict);                IOHIDManagerRegisterDeviceMatchingCallback(managerRef, &Handle_DeviceMatchingCallback, NULL);        IOHIDManagerRegisterDeviceRemovalCallback(managerRef, &Handle_DeviceRemovalCallback, NULL);                NSSet* allDevices = (__bridge NSSet*)(IOHIDManagerCopyDevices(managerRef));        NSArray* deviceRefs = [allDevices allObjects];        if (deviceRefs.count==0) {                                }    }    return self;}- (void)dealloc {    IOReturn ret = IOHIDDeviceClose(deviceRef, 0L);    if (ret == kIOReturnSuccess) {        deviceRef = nil;    }    ret = IOHIDManagerClose(managerRef, 0L);    if (ret == kIOReturnSuccess) {        managerRef = nil;    }}- (BOOL)connectHID {    NSSet* allDevices = (__bridge NSSet*)(IOHIDManagerCopyDevices(managerRef));    NSArray* deviceRefs = [allDevices allObjects];    deviceRef = (deviceRefs.count)?(__bridge IOHIDDeviceRef)[deviceRefs objectAtIndex:0]:nil;        if (deviceRef){                IOReturn ret = IOHIDDeviceOpen(deviceRef, 0L);        if (ret !=  kIOReturnSuccess)            return false;                self.connected = true;        return true;    }        return false;}-(BOOL)disconnectHID{            if (deviceRef){                IOReturn ret = IOHIDDeviceClose(deviceRef, 0L);        if (ret !=  kIOReturnSuccess)            return false;                self.connected = false;        return true;    }        return false;    }- (BOOL)senddata:(Byte*)outbuffer size:(int)size {    if (!deviceRef) {        return false;    }    IOReturn ret = IOHIDDeviceSetReport(deviceRef, kIOHIDReportTypeOutput, 0, (uint8_t*)outbuffer, size);    if (ret != kIOReturnSuccess) {        NSAlert* alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"发送数据失败!"];        [alert runModal];        return false;    }    return true;}- (int)readdata:(Byte*)readbuffer size:(int)size {    if (!deviceRef) {        return 0;    }        CFIndex sizecf = (CFIndex)size;    IOReturn ret = IOHIDDeviceGetReport(deviceRef, kIOHIDReportTypeFeature, 0, (uint8_t *)readbuffer, (CFIndex *) &sizecf );        if (ret != kIOReturnSuccess) {        NSAlert* alert = [NSAlert alertWithMessageText:@"error" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"接收数据失败!"];        [alert runModal];        return 0;    }        return (int)sizecf;}- (IOHIDManagerRef)getManageRef {    return managerRef;}- (void)setManageRef:(IOHIDManagerRef)ref {    managerRef = ref;}- (IOHIDDeviceRef)getDeviceRef {    return deviceRef;}- (void)setDeviceRef:(IOHIDDeviceRef)ref {    deviceRef = ref;}@end

可以看到usb hid 的接收数据是使用回调的方式触发的。


之后发现在使用runModalWindow弹出模态窗口NSWindow之后,会突然无法接收到usb hid 的数据,在回调处打上断点之后一直没有进来,猜想可能是这个回调线程是基于当前的NSWindow,在runModal另一个NSWindow之后,将原来的NSWindow的线程阻塞了,


解决方法就是不要使用runModal方法,使用beginSheet方法。


例如对于NSOpenlPanel,使用

let panel : NSOpenPanel = NSOpenPanel()                let fileTypes : NSArray = ["PNG"]                panel.message = "Select a PNG file"        panel.prompt = "OK"                panel.canChooseFiles = true        panel.allowsMultipleSelection = false        panel.allowedFileTypes = (fileTypes as! [String])        //        let result : Int = panel.runModal()                panel.beginWithCompletionHandler { (result) -> Void in                        if (result == NSFileHandlingPanelOKButton){                                            }            else{                                            }                    }

使用beginsheet方法后,就可以不影响usb hid的接收数据了