2016-01-06 5 views
2

Я хотел бы обнаружить, что определенный USB подключен/удален в моем приложении. На данный момент я могу получить имя устройства с этим учебником Working With USB Device Interfaces. Но как я могу выполнить функцию обратного вызова (deviceAdded) IOServiceMatchingCallBack в Swift.Как реализовать IOServiceMatchingCallBack в Swift

Я попытался сделать следующее, но получил ошибку: Невозможно преобразовать значение типа '(UnsafePointer, iterator: 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 
     } 

    } 

} 

ответ

6

Вот версия Swift 3, с помощью закрытия вместо глобальных функций (замыкание ж/контекста габаритного может быть мостиком к указателю функции C), используя НОД вместо 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") 

     let notifyPort = IONotificationPortCreate(kIOMasterPortDefault) 
     guard notifyPort != nil 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):

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

Лучший ответ в Интернете, спасибо. Должно тебе пиво. – user1244109

+0

Я создал [USBDeviceSwift] (https://github.com/Arti3DPlayer/USBDeviceSwift) библиотеку для удобной работы с 'IOKit.usb' и' IOKit.hid' – Arti

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

Я могу сказать вам, почему: «func» внутри класса является объектным методом **, в то время как 'func' вне класса является реальным ** статическая функция **. То же самое в Obj-C. Если вы напишете этот фрагмент кода в Obj-C, вам придется пройти статическую функцию C, а не объект Object-объект Obj-C. – Mecki

+0

У вас было счастье превратить это в Swift 3? Я боюсь здесь:/@Mecki – simonthumper

+0

@simonthumper Не пробовал, что до сих пор, но я дам ему попробовать завтра, просто для удовольствия играть еще немного с Swift 3. Я буду держать вас в курсе. – Mecki

0

Получил эту рабочую благодарность! Единственная проблема заключалась в том, что я не использовал итератор в своей функции обратного вызова, так что функция даже не вызывалась! Мне кажется странным поведением, но это была моя проблема.