Getting device/driver information related to a COM port?

£可爱£侵袭症+ 提交于 2019-12-28 13:51:14

问题


I have a Serial-to-USB device with a similarly named device driver in the Windows device manager. The devices do not always grab the same COM port on system boot, so my program needs to identify it on start up.

I've tried using RXTX to enumerate the COM ports on the system, but this didn't work because CommPortIdentifier.getName() simply returns the COM name (eg. COM1, COM2, etc.) I need to acquire either the driver manufacturer name, or the driver name as it appears in the device manager, and associate it with the COM name.

Can this easily be done in Java? (I'd be interested in any 3rd party Java libraries that support this.) Otherwise, how I could begin to accomplish this via the win32 API?


回答1:


I achieved what I wanted by using the WinRegistry class provided by David in this SO question to obtain the FriendlyName from registry key associated with my USB device. I then parse out the COM number from the friendly name.

Some things to consider:

  1. USB devices are located at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\ in the registry (tested on WinXP, Win7.)

  2. I required the device VID + PID to identify the correct device key (eg. VID_xxxx&PID_xxxx.) Since VID and PID are device specific, this key should be reliable across multiple systems.

  3. The VID_xxxx&PID_xxxx key contains another sub-key with device values. I had some trouble enumerating the sub-keys with WinRegistry, so I hard-coded the sub-key name as a quick hack during development. A much safer solution would search sub-keys to find the correct name.

  4. The device keys exist in the registry regardless of whether the device is currently connected. This code makes the assumption that Windows will update FriendlyName if the device is reconnected to a different COM port. I haven't verified this, but things looked good during use-testing.

Example

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));

Code

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 Your code does not allow for more than one device to be connected. How will the user know the device name? I added to your code thus to return a list of com ports:

    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)));
        }
    }

Update: I don't think these are solutions. Why? This information is statically stored in the registry - even when the device is not connected.




回答3:


Great example, using JNA, here. The author (Geir Arne Ruud) has released it under Public Domain License.

My example code

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;
}


来源:https://stackoverflow.com/questions/6362775/getting-device-driver-information-related-to-a-com-port

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!