获取 USB 3.0 设备的盘符(Java 下 Windows)

Get drive letters of USB 3.0 devices (Java under Windows)

我的 Java 程序需要获取已连接 USB 设备的驱动器盘符列表,但只有那些支持 USB 3.0 的设备(包括设备和它插入的 USB 端口,这样它才能工作高速)。

目前我尝试通过我的 Java 程序执行的 PowerShell 命令使用 WMI。

我已经找到了:Powershell: Grab USB Drive letter。但它也会列出 USB 2.0 设备。

关于版本检测,我发现:How to check the version of the available USB ports? - 我尝试的 PowerShell 命令是 Get-WmiObject Win32_USBHub。这带来了几个问题。第一:它列出的东西远不止 USB 驱动器(我想还有我 PC 的所有 USB 集线器)。第二:即使列表中的所有项目都有一个字段 USBVersion,它始终为空。

更新

我最近几天研究的重点是,我需要连接 2 个信息领域。

对于给定的驱动器盘符,我需要找到 bcdUSB 值。但是一直没找到USB设备对应驱动的方法。

到目前为止我尝试了什么

基于 PowerShell 的 WMI

我找到的相关命令是

Get-Disk               // Get BusType
gwmi Win32_LogicalDisk // Get drive letter
// Those make the connection between disk and logical disk
gwmi Win32_LogicalDiskToPartition
gwmi Win32_LogicalDiskToPartition

即使我得到 BusType,我也无法连接到 bcdUSB

usb4java (Link)

我这里只获取USB设备领域的信息。我可以加载设备并查看 VID&PID 和 bcdUSB 值,但无法将其映射到驱动器和驱动器号。

lsusb 通过 Cygwin

根据 this post,linux 命令比 WMI 更容易处理。所以我尝试在Windows下使用它。但是我喜欢usb4java我只有VID&PID + bcdUSB,没有挂载点(盘符)。

正在搜索 Windows 注册表

我在 Windows 注册表中进行了一些字符串搜索。没有成功。

正在读取Windows事件日志

我想过观察 Windows 事件来检测同时连接的驱动器和 USB 设备。我什至在插入 U 盘时都没有找到事件。

也许这就是您要找的: Find Windows Drive Letter of a removable disk from USB VID/PID 至少有人将答案标记为有效...:-)

由于建议的 Link 解决了 C# 的这个问题而不是 Java 并省去了一个步骤,我将 post 我的最终代码放在这里。

总结

在Java

  • 使用 USB4Java 查找所有连接的 USB 设备 bcdUSB=0x0300
  • 获取该设备的供应商 ID 和产品 ID (VID&PID)

通过 Powershell(jPowerShell

  • 获取给定 VID 和 PID 的 PnPEntity
  • 获取相关 USB 控制器
  • 查找与磁盘驱动器关联的 USB 控制器的关联器
  • 获取该磁盘驱动器
  • 获取相关磁盘分区
  • 获取相关逻辑盘->LogicalDisk.DeviceID=盘符

代码

Java class:

class UsbDetector {

  private PowerShell shell;

  @PostConstruct
  private void init() {
    shell = com.profesorfalken.jpowershell.PowerShell.openSession();
  }

  @OnDestroy
  private void onShutdownHook() {
    shell.close();
  }

  /**
   * Get drive letters of USB 3.0 devices.
   */
  public List<String> getDriveLettersForUsb3Devices() throws IOException, UsbException {
    List<UsbDevice> devicesUSB3 = getAllUsb3Devices();

    ImmutableList.Builder<String> driveLetterList = ImmutableList.builder();
    for (UsbDevice device : devicesUSB3) {
      String vidAndPid = getVidAndPid(device);

      String powerShellScript = buildScript(vidAndPid);

      String driveLetter = executeOnPowerShell(powerShellScript);
      driveLetterList.add(driveLetter);
    }

    return driveLetterList.build();
  }

  private String executeOnPowerShell(String powerShellScript) {
    InputStream psScriptStream = new ByteArrayInputStream(powerShellScript.getBytes());
    BufferedReader psScriptReader = new BufferedReader(new InputStreamReader(psScriptStream));

    PowerShellResponse response = shell.executeScript(psScriptReader);

    return response.getCommandOutput();
  }

  private String buildScript(String vidAndPid) throws IOException {
    InputStream psScriptStream =
        getClass().getClassLoader().getResourceAsStream("GetUsbDrives.ps1");

    String psScript = IOUtil.toString(psScriptStream);

    psScript = String.format("$input=\"%s\"", vidAndPid) + "\n" + psScript;
    return psScript;
  }

  /**
   * The Vendor ID and Product ID are necessary to find the device via WMI.
   */
  private String getVidAndPid(UsbDevice device) {
    short vendorId = device.getUsbDeviceDescriptor().idVendor();
    short productId = device.getUsbDeviceDescriptor().idProduct();

    String vendorIdHexString = String.format("%04x", vendorId).toUpperCase();
    String productIdHexString = String.format("%04x", productId).toUpperCase();

    String vidAndPid = String.format("VID_%s&PID_%s", vendorIdHexString, productIdHexString);
    return vidAndPid;
  }

  /**
   * From all Usb devices find those with USB 3.0. The value bcdUsb is a hexadecimal coded number
   * telling us the USB version.
   */
  private List<UsbDevice> getAllUsb3Devices() throws UsbException {
    List<UsbDevice> devicesUSB3 = Lists.newArrayList();
    UsbServices services = new org.usb4java.javax.Services();

    UsbHub hub = services.getRootUsbHub();
    List<UsbDevice> devices = getAllUsbDevices(hub);
    for (UsbDevice device : devices) {
      UsbDeviceDescriptor descriptor = device.getUsbDeviceDescriptor();
      short bcdUsb = descriptor.bcdUSB();
      String bcdDecoded = DescriptorUtils.decodeBCD(bcdUsb);

      if (Objects.equal(bcdDecoded, "3.00")) {
        devicesUSB3.add(device);
      }
    }
    return devicesUSB3;
  }

  /**
   * UsbHubs can either mount UsbDevices or further UsbHubs. This method searches through the tree
   * of UsbHubs for UsbDevices and returns them as list.
   */
  private List<UsbDevice> getAllUsbDevices(UsbHub hub) {
    List<UsbDevice> devices = Lists.newArrayList();

    List<UsbDevice> attachedDevices = hub.getAttachedUsbDevices();
    for (UsbDevice device : attachedDevices) {
      if (device instanceof UsbHub) {
        List<UsbDevice> subdevices = getAllUsbDevices((UsbHub) device);
        devices.addAll(subdevices);
      } else {
        devices.add(device);
      }
    }
    return devices;
  }

}

PowerShell 脚本:

# $input = "VID_XXXX&PID_XXXX (this line is added in Java Code)

# For given VID and PID of a USB device we search for 
# the corresponding logical disk to get the drive letter.

# The chain of objects is:
# PnPEntity (PnP = Plug and Play)
# -> USBController
# -> Some associator of USBController that has a related disk drive
# -> diskDrive
# -> diskPartition
# -> logicalDisk  

# Find PnPEntity for given VID and PID
$usbPnPEntity = (gwmi Win32_PnPEntity | where DeviceID -match $input)

# Get USB Controller related to PnP Entity
$usbController = $usbPnPEntity.getRelated("Win32_USBController") 
$usbControllerID = $usbController.DeviceID

# Find objects associated with the USB Controller
$query = "ASSOCIATORS OF {Win32_USBController.DeviceID='$usbControllerID'}"
$associators = ([wmisearcher]$query).get()

# Search through associators
foreach ($associator in $associators) {
    # Find associator that is related to a disk Drive
    $assoDeviceID = $associator.DeviceID
    $diskDrive = (gwmi win32_diskdrive | where PNPDeviceID -eq $assoDeviceID)
    
    if($diskDrive){
        # Get logical Disk related to the disk drive
        $logicalDisk = $diskDrive.getRelated("Win32_DiskPartition").getRelated("Win32_LogicalDisk")
        
        # Print device ID which is the drive letter (e.g. "C:")
        $logicalDisk.DeviceID
        break
    }

}

Maven 依赖项:

    <dependency>
        <groupId>org.usb4java</groupId>
        <artifactId>usb4java-javax</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.profesorfalken</groupId>
        <artifactId>jPowerShell</artifactId>
        <version>3.1.1</version>
    </dependency>