代码之家  ›  专栏  ›  技术社区  ›  Willjay

如何在Swift中实现IOServiceMatchingCallBack

  •  3
  • Willjay  · 技术社区  · 10 年前

    我想检测我的应用程序中插入/移除的特定USB。现在,我可以通过本教程获得deviceName Working With USB Device Interfaces 。但是,如何执行(deviceAdded)的回调函数 IOServiceMatching回调 在Swift中。

    我尝试了以下操作,但我遇到了一个错误: 无法转换“”类型的值(UnsafePointer,迭代器:io_iterator_t)->()”应为参数类型“IOServiceMatchingCallback!”

    func detectUSBEvent() {
        var portIterator: io_iterator_t = 0
        var kr: kern_return_t = KERN_FAILURE
        let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
    
        let vendorIDString = kUSBVendorID as CFStringRef!
        let productIDString = kUSBProductID as CFStringRef!
        CFDictionarySetValue(matchingDict, unsafeAddressOf(vendorIDString), unsafeAddressOf(VendorID))
        CFDictionarySetValue(matchingDict, unsafeAddressOf(productIDString), unsafeAddressOf(ProductID))
    
        // To set up asynchronous notifications, create a notification port and add its run loop event source to the program’s run loop
        let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
        let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
        let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
    
        CFRunLoopAddSource(gRunLoop, runLoopSource.takeUnretainedValue(), kCFRunLoopDefaultMode)
    
        // Notification of first match:
        kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict, deviceAdded, nil, &portIterator)
        deviceAdded(nil, iterator: portIterator)
     }
    
    
    func deviceAdded(refCon: UnsafePointer<Void>, iterator: io_iterator_t) {
        if let usbDevice: io_service_t = IOIteratorNext(iterator)
        {
            let name = String()
            let cs = (name as NSString).UTF8String
            let deviceName: UnsafeMutablePointer<Int8> = UnsafeMutablePointer<Int8>(cs)
    
            let kr: kern_return_t = IORegistryEntryGetName(usbDevice, deviceName)
            if kr == KERN_SUCCESS {
                let deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName,
                    kCFStringEncodingASCII)
                print(deviceNameAsCFString)
                // if deviceNameAsCFString == XXX
                // Do Something
            }
    
        }
    
    }
    
    3 回复  |  直到 10 年前
        1
  •  12
  •   Community Mohan Dere    6 年前

    这是一个Swift 3版本,使用闭包代替全局函数(没有上下文的闭包可以桥接到C函数指针),使用GCD代替Runloops(更好的API),使用回调和调度来通知事件,使用真实对象代替静态对象或单例:

    import Darwin
    import IOKit
    import IOKit.usb
    import Foundation
    
    
    class IOUSBDetector {
    
        enum Event {
            case Matched
            case Terminated
        }
    
        let vendorID: Int
        let productID: Int
    
        var callbackQueue: DispatchQueue?
    
        var callback: (
            ( _ detector: IOUSBDetector,  _ event: Event,
                _ service: io_service_t
            ) -> Void
        )?
    
    
        private
        let internalQueue: DispatchQueue
    
        private
        let notifyPort: IONotificationPortRef
    
        private
        var matchedIterator: io_iterator_t = 0
    
        private
        var terminatedIterator: io_iterator_t = 0
    
    
        private
        func dispatchEvent (
            event: Event, iterator: io_iterator_t
        ) {
            repeat {
                let nextService = IOIteratorNext(iterator)
                guard nextService != 0 else { break }
                if let cb = self.callback, let q = self.callbackQueue {
                    q.async {
                        cb(self, event, nextService)
                        IOObjectRelease(nextService)
                    }
                } else {
                    IOObjectRelease(nextService)
                }
            } while (true)
        }
    
    
        init? ( vendorID: Int, productID: Int ) {
            self.vendorID = vendorID
            self.productID = productID
            self.internalQueue = DispatchQueue(label: "IODetector")
    
            guard let notifyPort = IONotificationPortCreate(kIOMasterPortDefault) else {
                return nil
            }
    
            self.notifyPort = notifyPort
            IONotificationPortSetDispatchQueue(notifyPort, self.internalQueue)
        }
    
        deinit {
            self.stopDetection()
        }
    
    
        func startDetection ( ) -> Bool {
            guard matchedIterator == 0 else { return true }
    
            let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
                as NSMutableDictionary
            matchingDict[kUSBVendorID] = NSNumber(value: vendorID)
            matchingDict[kUSBProductID] = NSNumber(value: productID)
    
            let matchCallback: IOServiceMatchingCallback = {
                (userData, iterator) in
                    let detector = Unmanaged<IOUSBDetector>
                        .fromOpaque(userData!).takeUnretainedValue()
                    detector.dispatchEvent(
                        event: .Matched, iterator: iterator
                    )
            };
            let termCallback: IOServiceMatchingCallback = {
                (userData, iterator) in
                    let detector = Unmanaged<IOUSBDetector>
                        .fromOpaque(userData!).takeUnretainedValue()
                    detector.dispatchEvent(
                        event: .Terminated, iterator: iterator
                    )
            };
    
            let selfPtr = Unmanaged.passUnretained(self).toOpaque()
    
            let addMatchError = IOServiceAddMatchingNotification(
                self.notifyPort, kIOFirstMatchNotification,
                matchingDict, matchCallback, selfPtr, &self.matchedIterator
            )
            let addTermError = IOServiceAddMatchingNotification(
                self.notifyPort, kIOTerminatedNotification,
                matchingDict, termCallback, selfPtr, &self.terminatedIterator
            )
    
            guard addMatchError == 0 && addTermError == 0 else {
                if self.matchedIterator != 0 {
                    IOObjectRelease(self.matchedIterator)
                    self.matchedIterator = 0
                }
                if self.terminatedIterator != 0 {
                    IOObjectRelease(self.terminatedIterator)
                    self.terminatedIterator = 0
                }
                return false
            }
    
            // This is required even if nothing was found to "arm" the callback
            self.dispatchEvent(event: .Matched, iterator: self.matchedIterator)
            self.dispatchEvent(event: .Terminated, iterator: self.terminatedIterator)
    
            return true
        }
    
    
        func stopDetection ( ) {
            guard self.matchedIterator != 0 else { return }
            IOObjectRelease(self.matchedIterator)
            IOObjectRelease(self.terminatedIterator)
            self.matchedIterator = 0
            self.terminatedIterator = 0
        }
    }
    

    下面是一些简单的测试代码来测试该类(根据您的USB设备设置产品和供应商ID):

    let test = IOUSBDetector(vendorID: 0x4e8, productID: 0x1a23)
    test?.callbackQueue = DispatchQueue.global()
    test?.callback = {
        (detector, event, service) in
            print("Event \(event)")
    };
    _ = test?.startDetection()
    while true { sleep(1) }
    
        2
  •  2
  •   Willjay    10 年前

    它 作品 在我将回调函数放到类之外之后。然而,我不知道为什么。

    class IODetection {
        class func monitorUSBEvent(VendorID: Int, ProductID: Int) {
    
    
            var portIterator: io_iterator_t = 0
            var kr: kern_return_t = KERN_FAILURE
            let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
    
            // Add the VENDOR and PRODUCT IDs to the matching dictionary.
            let vendorIDString = kUSBVendorID as CFStringRef!
            let productIDString = kUSBProductID as CFStringRef!
            CFDictionarySetValue(matchingDict, unsafeAddressOf(vendorIDString), unsafeAddressOf(VendorID))
            CFDictionarySetValue(matchingDict, unsafeAddressOf(productIDString), unsafeAddressOf(ProductID))
    
            // To set up asynchronous notifications, create a notification port and add its run loop event source to the program’s run loop
            let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
            let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
            let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
    
            CFRunLoopAddSource(gRunLoop, runLoopSource.takeRetainedValue(), kCFRunLoopDefaultMode)
    
            // MARK: - USB in Notification
            let observer = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
            kr = IOServiceAddMatchingNotification(gNotifyPort,
                                                  kIOMatchedNotification,
                                                  matchingDict,
                                                  deviceAdded,
                                                  observer,
                                                  &portIterator)
            deviceAdded(nil, iterator: portIterator)
    
    
            // MARK: - USB remove Notification
            kr = IOServiceAddMatchingNotification(gNotifyPort,
                                                  kIOTerminatedNotification,
                                                  matchingDict,
                                                  deviceRemoved,
                                                  observer,
                                                  &portIterator)
            deviceRemoved(nil, iterator: portIterator)
    
        }
    }
    
    func deviceAdded(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) -> Void {
        var kr: kern_return_t = KERN_FAILURE
    
        while case let usbDevice = IOIteratorNext(iterator) where usbDevice != 0 {
            let deviceNameAsCFString = UnsafeMutablePointer<io_name_t>.alloc(1)
            defer {deviceNameAsCFString.dealloc(1)}
            kr = IORegistryEntryGetName(usbDevice, UnsafeMutablePointer(deviceNameAsCFString))
            if kr != KERN_SUCCESS {
                deviceNameAsCFString.memory.0 = 0
            }
            let deviceName = String.fromCString(UnsafePointer(deviceNameAsCFString))
            print("Device Added: \(deviceName!)")
    
            // Do something if I get the specific device
            if deviceName == "YOUR DEVICE" {
                /// Your Action HERE
            }
    
            IOObjectRelease(usbDevice)
        }
    }
    
        3
  •  0
  •   simonthumper    5 年前

    我的问题是我没有在回调函数中使用迭代器,所以该函数甚至没有被调用!在我看来,这是一种奇怪的行为,但这是我的问题。