我有一个串口到USB设备,在Windows设备pipe理器中有一个类似命名的设备驱动程序。 在系统启动时,设备并不总是占用相同的COM端口,所以我的程序需要在启动时识别它。
我已经尝试使用RXTX来枚举系统上的COM端口,但是这不起作用,因为CommPortIdentifier.getName()
只是返回COM名称(例如COM1,COM2等),我需要获取驱动程序制造商名称或设备pipe理器中显示的驱动程序名称,并将其与COM名称相关联。
这可以轻松地在Java中完成吗? (我会对任何支持这个的第三方Java库感兴趣。)否则,我怎样才能通过win32 API来完成这个任务呢?
我在这个SO问题中使用由David提供的WinRegistry
类来获得我想要的WinRegistry
,以从与我的USB设备关联的注册表项中获取FriendlyName 。 然后我从友好的名字解析出COM号码。
有些事情要考虑:
USB设备位于HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\
(在WinXP,Win7上测试)。
我需要设备VID + PID来识别正确的设备密钥(例如VID_xxxx&PID_xxxx
。由于VID和PID是设备特定的,因此这个密钥在多个系统中应该是可靠的。
VID_xxxx&PID_xxxx
键包含另一个带有设备值的子键。 使用WinRegistry
枚举子键时遇到了一些麻烦,所以我在开发过程中将子键名称硬编码为快速入侵。 一个更安全的解决方案将搜索子键来找到正确的名称。
无论设备当前是否连接,设备密钥都存在于注册表中。 如果设备重新连接到不同的COM端口,此代码将假定Windows将更新FriendlyName 。 我没有证实这一点,但在使用测试中看起来不错。
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; }
@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))); } }
更新:我不认为这些解决方案。 为什么? 此信息静态存储在注册表中 – 即使设备未连接。
很好的例子, 在这里使用JNA。 作者(Geir Arne Ruud)已经根据Public Domain License发布了它。
我的示例代码
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; }