1

我的 Java 程序需要获取已连接 USB 设备的驱动器号列表,但仅限于那些支持 USB 3.0 的设备(设备和它所插入的 USB 端口,以便高速运行)。

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

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

关于版本检测,我发现:如何检查可用 USB 端口的版本?- 我试过的 PowerShell 命令是Get-WmiObject Win32_USBHub. 这带来了几个问题。第一:它列出的东西远不止 USB 驱动器(我认为我的 PC 的所有 USB 集线器也是如此)。第二:即使列表中的所有项目都有一个字段USBVersion它始终为空。

更新

我过去几天研究的本质是,我需要连接两个领域的信息。

  • 驱动器/逻辑驱动器
    • 盘符
    • BusType(就我而言,等于“USB”)
  • USB 设备
    • 供应商 ID 和产品 ID (VID&PID)
    • bcdUSB(USB 设备描述符中的值,表示 USB 版本)

对于给定的驱动器号,我需要找到 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 (链接)

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

lsusb 通过 Cygwin

根据这篇文章,linux命令比WMI更容易处理。所以我尝试在Windows下使用它。但我喜欢 usb4java 我只有 VID&PID + bcdUSB,而不是挂载点(驱动器号)。

搜索 Windows 注册表

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

读取 Windows 事件日志

我考虑过使用 Windows 事件来检测同时连接的驱动器和 USB 设备。插入 USB 记忆棒时,我什至没有找到事件。

4

2 回答 2

1

也许这就是您正在寻找的: 从 USB VID/PID 中查找可移动磁盘的 Windows 驱动器号 至少有人将答案标记为工作... :-)

于 2021-03-15T07:48:22.617 回答
0

由于建议的链接为 C# 而不是 Java 解决了这个问题并且省略了一步,所以我将在此处发布我的最终代码。

概括

在 Java 中

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

通过 Powershell(使用jPowerShell

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

代码

Java类:

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>
于 2021-03-24T14:48:52.217 回答