Как вызвать IOCFPlugInInterface.QueryInterface() для запроса USB-устройств

Я хочу преобразовать этот USB-детектор в Swift 3 /а>

Но я застрял на этой строке:

let deviceInterfaceResult = plugInInterface.QueryInterface(
        plugInInterfacePtrPtr,
        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
&deviceInterfaceVoidPtr)

Невозможно вызвать значение нефункционального типа '(@convention(c) (UnsafeMutableRawPointer?, REFIID, UnsafeMutablePointer?) -> HRESULT)!'

введите здесь описание изображения

Источник QueryInterface:

public var QueryInterface: (@convention(c) (UnsafeMutableRawPointer?, REFIID, UnsafeMutablePointer<LPVOID?>?) -> HRESULT)!

Как мне вызвать эту функцию? Использование CFNotificationCallback в Swift или @convention(c) в блоках Swift этот ответ мне не помог.


person Arti    schedule 08.12.2016    source источник
comment
@JAL let deviceInterfaceResult:HRESULT = {(_: plugInInterfacePtrPtr, _: CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), _: &deviceInterfaceVoidPtr) -> HRESULT in }() не работает   -  person Arti    schedule 08.12.2016


Ответы (2)


Ответ Кера очень близок. plugInInterface.QueryInterface() ожидает в качестве последнего аргумента адрес двойного-косвенного указателя

UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?

но «замаскированный» под указатель на LPVOID?, также известный как UnsafeMutableRawPointer. Следовательно, полученный указатель необходимо разыменовать дважды. withMemoryRebound() можно использовать для этого приведения указателя.

Я сделал еще несколько изменений в коде:

  • Используйте defer для продолжения со следующим USB-устройством, даже если текущая итерация была «прервана» из-за ошибки.
  • Удалите ненужные аннотации типов.
  • Используйте MemoryLayout<io_name_t>.size вместо 128.
  • Отпустите usbDevice и указатели интерфейса после использования, чтобы избежать утечек памяти.
  • Переместите некоторые объявления переменных сверху туда, где они необходимы.

Собираем все вместе:

import Foundation
import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib

print("Scanning USB Bus.....\n\n\n")

//
// These constants are not imported into Swift from IOUSBLib.h as they
// are all #define constants
//

let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                                  0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
                                                                  0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                            0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
                                                            0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)

let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                             0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
                                                             0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)


// Create dictionary with IOUSBDevice as IOProviderClass.
let matchingDictionary: NSMutableDictionary = IOServiceMatching(kIOUSBDeviceClassName)

// Get iterator for matching USB devices.
var usbIterator = io_iterator_t()
let matchingServicesResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &usbIterator)
if matchingServicesResult != kIOReturnSuccess {
    print("Error getting deviceList!")
    exit(EXIT_FAILURE)
}

// Iterate devices until usbDevice == 0.
var usbDevice = IOIteratorNext(usbIterator)
while usbDevice != 0 {
    defer {
        usbDevice = IOIteratorNext(usbIterator)
    }

    // io_name_t imports to Swift as a tuple (Int8, ..., Int8) with 128 ints
    // although in device_types.h it is defined as
    //      typedef char io_name_t[128];
    var deviceNameCString = [CChar](repeating: 0, count: MemoryLayout<io_name_t>.size)
    let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)
    if deviceNameResult != kIOReturnSuccess {
        print("Error getting device name")
        continue
    }
    let deviceName = String(cString: &deviceNameCString)
    print("USB device name: \(deviceName)")

    // Get plug-in interface for current USB device
    var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>?
    var score: Int32 = 0
    let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
        usbDevice,
        kIOUSBDeviceUserClientTypeID,
        kIOCFPlugInInterfaceID,
        &plugInInterfacePtrPtr,
        &score)

    // USB device object is no longer needed.
    IOObjectRelease(usbDevice)

    // Dereference pointer for the plug-in interface
    guard plugInInterfaceResult == kIOReturnSuccess,
        let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Plug-In Interface")
            continue
    }

    // Use plug-in interface to get a device interface.
    var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>?
    let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) {
        $0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) {
            plugInInterface.QueryInterface(
                plugInInterfacePtrPtr,
                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
                $0)
        }
    }

    // Plug-in interface is no longer needed.
    _ = plugInInterface.Release(plugInInterfacePtrPtr)

    // Dereference pointer for the device interface.
    guard deviceInterfaceResult == kIOReturnSuccess,
        let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else {
            print("Unable to get Device Interface")
            continue
    }

    // Use device interface to get vendor ID.
    var usbVendorID: UInt16 = 0
    let vendorResult = deviceInterface.GetDeviceVendor(deviceInterfacePtrPtr, &usbVendorID)

    // Device interface is no longer needed:
    _ = deviceInterface.Release(deviceInterfacePtrPtr)

    if vendorResult != kIOReturnSuccess {
        print("Unable to get device Vendor ID")
        continue
    }

    print("USB Vendor ID: \(usbVendorID)")
}

exit(EXIT_SUCCESS)
person Martin R    schedule 08.12.2016
comment
@Arti: Добро пожаловать. Я снова отредактировал код, чтобы исправить некоторые утечки памяти. Я также позволил себе отредактировать заголовок вашего вопроса, @convention(c) не имеет отношения к этой проблеме. - person Martin R; 09.12.2016

Не знаю, правильно ли это вообще, но это сборка с Swift 3 и Xcode 8.0. Ключ был в том, чтобы написать var deviceInterfaceVoidPtr: UnsafeMutableRawPointer? = nil.

import IOKit
import IOKit.usb
import IOKit.usb.IOUSBLib

print("Scanning USB Bus.....\n\n\n")

//
// These constants are not imported into Swift from IOUSBLib.h as they
// are all #define constants
//

let kIOUSBDeviceUserClientTypeID:   CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                                        0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4,
                                                                        0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

let kIOCFPlugInInterfaceID:         CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                                        0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4,
                                                                        0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F)

let kIOUSBDeviceInterfaceID:        CFUUID = CFUUIDGetConstantUUIDWithBytes(kCFAllocatorDefault,
                                                                        0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4,
                                                                        0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61)

var usbIterator:    io_iterator_t   = io_iterator_t()
var usbDevice:      io_service_t    = io_service_t()
var usbVendorID:    UInt16          = 0
var score:          Int32           = 0

func ptrFromAddress<T>(_ p: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T> {
    return p
}
var myInterface:IOCFPlugInInterface = IOCFPlugInInterface()
var plugInInterfacePtr: UnsafeMutablePointer<IOCFPlugInInterface>? = ptrFromAddress(&myInterface)
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>? = ptrFromAddress(&plugInInterfacePtr)

// From: CFPlugInCOM.h: public typealias LPVOID =  UnsafeMutablePointer<Void>()
var deviceInterfaceVoidPtr: UnsafeMutableRawPointer? = nil

// create dictionary with IOUSBDevice as IOProviderClass
let matchingDictionary: NSMutableDictionary = IOServiceMatching(kIOUSBDeviceClassName)

// get iterator for matching USB devices
let matchingServicesResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &usbIterator)
if matchingServicesResult != kIOReturnSuccess {
    print("Error getting deviceList!")
    exit(EXIT_FAILURE)
}

// usbDevice = 0 when finished iterating all devices
repeat {
    usbDevice = IOIteratorNext(usbIterator)
    // io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints
    // although in device_types.h it's defined:
    // typedef  char io_name_t[128];
    var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128)
    let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString)

    if deviceNameResult != kIOReturnSuccess {
        print("Error getting device name")
        exit(EXIT_FAILURE)
    }

    let deviceName = String(cString: &deviceNameCString)
    print("usb Device Name: \(deviceName)")

    // Get plugInInterface for current USB device
    let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
        usbDevice,
        kIOUSBDeviceUserClientTypeID,
        kIOCFPlugInInterfaceID,
        &plugInInterfacePtrPtr,
        &score)

    if (plugInInterfacePtrPtr == nil) || (plugInInterfaceResult != kIOReturnSuccess) {
        print("Unable to get Plug-In Interface")
        continue
    }

    // dereference pointer for the plug in interface
    let plugInInterface: IOCFPlugInInterface = plugInInterfacePtrPtr!.pointee!.pointee

    // use plug in interface to get a device interface
    // public var QueryInterface: (@convention(c) (UnsafeMutablePointer<Void>, REFIID, UnsafeMutablePointer<LPVOID>) -> HRESULT)!

    let deviceInterfaceResult = plugInInterface.QueryInterface(
        plugInInterfacePtrPtr,
        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID),
        &deviceInterfaceVoidPtr)

    if (deviceInterfaceResult != kIOReturnSuccess) || (deviceInterfaceVoidPtr == nil) {
        print("Unable to get Device Interface")
        exit(EXIT_FAILURE)
    }

    // dereference the IOUSBDeviceInterface struct from pointer var after
    // converting from a void to a IOUSBDeviceInterface pointer
    let opaquePtr = OpaquePointer(deviceInterfaceVoidPtr!)
    let deviceInterface = UnsafeMutablePointer<IOUSBDeviceInterface>(opaquePtr).pointee

    // get USB Vendor ID --> CRASH
    let vendorResult = deviceInterface.GetDeviceVendor(deviceInterfaceVoidPtr!, &usbVendorID)

    if vendorResult != kIOReturnSuccess {
        print("Unable to get Device Vendor ID")
        exit(EXIT_FAILURE)
    }

    print("usb Vendor ID: \(usbVendorID)")

    usbDevice = IOIteratorNext(usbIterator)
} while (usbDevice != 0)

exit(EXIT_SUCCESS)

Кроме того, обратите внимание, что, согласно основному комментарию от cellininicholas, вы должны удалить одно из двух вхождений

usbDevice = IOIteratorNext(usbIterator)
person Cœur    schedule 08.12.2016
comment
о, это позор. Я забыл импортировать IOKit, поэтому у меня много проблем. - person Arti; 08.12.2016
comment
У меня тут вылетает: // get USB Vendor ID --> CRASH let vendorResult = deviceInterface.GetDeviceVendor(deviceInterfaceVoidPtr!, &usbVendorID) - person Arti; 08.12.2016
comment
Ну, я думаю, авария была в изначальной сути. Давайте похвалим Мартина за его гораздо лучший ответ. :) - person Cœur; 09.12.2016