Получение информации о устройстве/драйвере, связанной с COM-портом?

У меня есть устройство Serial-to-USB с аналогичным драйвером устройства в диспетчере устройств Windows. Устройства не всегда захватывают один и тот же COM-порт при загрузке системы, поэтому моя программа должна идентифицировать его при запуске.

Я попытался использовать RXTX для перечисления COM-портов в системе, но это не сработало, потому что CommPortIdentifier.getName() просто возвращает COM-имя (например, COM1, COM2 и т.д.) Мне нужно получить либо имя производителя драйвера, либо имя драйвера, как оно отображается в диспетчере устройств, и связать его с именем COM.

Можно ли это сделать на Java? (Меня бы интересовали любые сторонние библиотеки Java, которые поддерживают это.) В противном случае, как я мог бы начать это делать с помощью win32 API?

Ответ 1

Я достиг того, чего хотел, используя класс WinRegistry, предоставленный Дэвидом в этот вопрос SO, чтобы получить имя FriendlyName из раздела реестра, связанного с моим USB-устройством. Затем я разбираю номер COM из дружественного имени.

Некоторые вещи, которые следует учитывать:

  • USB-устройства расположены в HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\ в реестре (проверены на WinXP, Win7.)

  • Мне нужно, чтобы устройство VID + PID идентифицировало правильный ключ устройства (например, VID_xxxx&PID_xxxx.) Поскольку VID и PID являются специфичными для устройства, этот ключ должен быть надежным в нескольких системах.

  • Клавиша VID_xxxx&PID_xxxx содержит другую под-клавишу с значениями устройства. У меня возникли проблемы с перечислением подкатегорий с WinRegistry, поэтому я жестко закодировал имя подкарта в качестве быстрого взлома во время разработки. Более безопасное решение будет искать под-ключи, чтобы найти правильное имя.

  • Ключи устройства существуют в реестре независимо от того, подключено ли устройство в данный момент. Этот код делает предположение, что Windows будет обновлять FriendlyName, если устройство повторно подключено к другому COM-порту. Я не проверял это, но во время тестирования использования все выглядело хорошо.

Пример

String keyPath = "SYSTEM\\CurrentControlSet\\Enum\\USB\\Vid_067b&Pid_2303\\";
String device1 = "5&75451e6&0&1";
System.out.println("First COM device: " + getComNumber(keyPath + device1));

код

import java.util.regex.Pattern;
import java.util.regex.Matcher;

// Given a registry key, attempts to get the 'FriendlyName' value
// Returns null on failure.
//
public static String getFriendlyName(String registryKey) {
    if (registryKey == null || registryKey.isEmpty()) {
        throw new IllegalArgumentException("'registryKey' null or empty");
    }
    try {
        int hkey = WinRegistry.HKEY_LOCAL_MACHINE;
        return WinRegistry.readString(hkey, registryKey, "FriendlyName");
    } catch (Exception ex) { // catch-all: 
        // readString() throws IllegalArg, IllegalAccess, InvocationTarget
        System.err.println(ex.getMessage());
        return null;
    }
}

// Given a registry key, attempts to parse out the integer after
// substring "COM" in the 'FriendlyName' value; returns -1 on failure.
//
public static int getComNumber(String registryKey) {
    String friendlyName = getFriendlyName(registryKey);

    if (friendlyName != null && friendlyName.indexOf("COM") >= 0) {
        String substr = friendlyName.substring(friendlyName.indexOf("COM"));
        Matcher matchInt = Pattern.compile("\\d+").matcher(substr);
        if (matchInt.find()) {
            return Integer.parseInt(matchInt.group());
        }
    }
    return -1;
}   

Ответ 2

@robjb В вашем коде не допускается подключение более одного устройства. Как узнать имя устройства? Я добавил к вашему коду таким образом, чтобы вернуть список COM-портов:

    ArrayList<String> subKeys = WinRegistry.readStringSubKeys(WinRegistry.HKEY_LOCAL_MACHINE, keyPath);
    ArrayList<Integer> comPorts = new ArrayList<Integer>();
    for (String subKey : subKeys) {
        String friendlyName = getFriendlyName(keyPath + subKey);
        if (friendlyName != null && friendlyName.contains("MyDriverName") && friendlyName.contains("COM")) {
            int beginIndex = friendlyName.indexOf("COM") + 3 /*length of 'COM'*/;
            int endIndex = friendlyName.indexOf(")");
            comPorts.add(Integer.parseInt(friendlyName.substring(beginIndex, endIndex)));
        }
    }

Обновление: я не думаю, что это решения. Зачем? Эта информация статически хранится в реестре - даже если устройство не подключено.

Ответ 3

Отличный пример, используя JNA, здесь. Автор (Geir Arne Ruud) опубликовал его под лицензией Public Domain.

Мой примерный код

public static String getFriendlyName(GoGPSModel model, String name) 
{
   if(model.getSystem().getOSType() != OSType.Windows32 
   && model.getSystem().getOSType() != OSType.Windows64) {
      return name;
   }
   for (DeviceInformation devInfo : infoObjects) {
      System.out.println(devInfo.toString());
      String friendlyName = devInfo.getFriendlyName();
      if(friendlyName != null && !friendlyName.equals("") && friendlyName.contains(name)) {
         return devInfo.getManufacturer() + ": " + friendlyName;
      }
   }
   return name;
}