【问题标题】:How to implement IOServiceMatchingCallBack in Swift如何在 Swift 中实现 IOServiceMatching 回调
【发布时间】:2016-04-10 06:26:14
【问题描述】:

我想检测在我的应用程序中插入/移除了特定 USB。目前,我可以通过本教程 Working With USB Device Interfaces 获取设备名称。但是,如何在 Swift 中实现 (deviceAdded)IOServiceMatchingCallBack 的回调函数。

我尝试如下,但出现错误:无法将类型“(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
        }

    }

}

【问题讨论】:

    标签: swift macos usb iokit


    【解决方案1】:

    工作在我把回调函数放到类之外。但是,我不知道为什么。

    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)
        }
    }
    

    【讨论】:

    • 我可以告诉你原因:类内的func对象方法,而类外的func 是真正的静态函数。在 Obj-C 中也是如此。如果你用 Obj-C 编写那段代码,你必须传入一个静态 C 函数,而不是一个 Obj-C 对象方法。
    • 你们中的任何一个人有没有运气把它转换成 Swift 3?我在这里挣扎:/ @Mecki
    • @simonthumper 到目前为止还没有尝试过,但我明天会试一试,只是为了玩更多 Swift 3 的乐趣。我会及时通知你。
    • 我也只是在一个单独的问题上回答了这个问题......也许它是重复的:/ stackoverflow.com/a/41279799/23649
    【解决方案2】:

    这是一个 Swift 3 版本,使用闭包而不是全局函数(带有 oa 上下文的闭包可以桥接到 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) }
    

    【讨论】:

    • 互联网上的最佳答案,谢谢。欠你一杯啤酒。
    • 我创建了USBDeviceSwift 库,以便于使用IOKit.usbIOKit.hid
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2014-09-20
      • 2010-12-13
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-25
      • 2018-01-06
      • 1970-01-01
      相关资源
      最近更新 更多