I guess a workaround would be to use the serial number (see code below) and then have a lookup table for the text description in a file, but if someone has a suggestion for a solution without the need for a lookup table then I'm still very interested.
public class ComPortEnumerator
{
[DllImport("Setupapi", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetupDiOpenDevRegKey(IntPtr hDeviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, uint scope,
uint hwProfile, uint parameterRegistryValueKind, uint samDesired);
[DllImport("setupapi.dll")]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, Int32 MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, UInt32 DeviceInstanceIdSize, out UInt32 RequiredSize);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, UInt32 iEnumerator, IntPtr hParent, UInt32 nFlags);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError = true)]
private static extern int RegQueryValueEx(IntPtr hKey, string lpValueName, int lpReserved, out uint lpType,
StringBuilder lpData, ref uint lpcbData);
[DllImport("advapi32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int RegCloseKey(IntPtr hKey);
private const UInt32 DIGCF_PRESENT = 0x00000002;
private const UInt32 DICS_FLAG_GLOBAL = 0x00000001;
private const UInt32 DIREG_DEV = 0x00000001;
private const UInt32 KEY_QUERY_VALUE = 0x0001;
private const string GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR = "4D36E978-E325-11CE-BFC1-08002BE10318";
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public Int32 cbSize;
public Guid ClassGuid;
public Int32 DevInst;
public UIntPtr Reserved;
};
public static void logAllUsbToSerialCablesSerialNo()
{
Guid guidComPorts = new Guid(GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR);
Int32 iMemberIndex = 0;
while (true)
{
SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
deviceInfoData.cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
IntPtr hDeviceInfoSet = SetupDiGetClassDevs(ref guidComPorts, 0, IntPtr.Zero, DIGCF_PRESENT);
if (hDeviceInfoSet == IntPtr.Zero)
{
// Failed to get device information set for the COM ports
break;
}
bool success = SetupDiEnumDeviceInfo(hDeviceInfoSet, iMemberIndex, ref deviceInfoData);
if (!success)
{
// No more devices in the device information set
break;
}
string comPortName = GetDeviceName(hDeviceInfoSet, deviceInfoData);
string deviceInstancePath = ComPortEnumerator.getDeviceInstancePath(hDeviceInfoSet, deviceInfoData);
string serialNo = ComPortEnumerator.extractSerialNoFromDeviceInstancePath(deviceInstancePath);
Debug.WriteLine(comPortName + " has serial number " + serialNo);
iMemberIndex++;
}
}
private static string getDeviceInstancePath(IntPtr hDeviceInfoSet, SP_DEVINFO_DATA deviceInfoData)
{
StringBuilder descriptionBuf = new StringBuilder(256);
uint length = (uint)descriptionBuf.Capacity;
bool success = SetupDiGetDeviceInstanceId(hDeviceInfoSet, ref deviceInfoData,
descriptionBuf, length, out length);
if (!success)
{
throw new Exception("Can not read the Device Instance Path for device " + deviceInfoData.ClassGuid);
}
return descriptionBuf.ToString();
}
public static string extractSerialNoFromDeviceInstancePath(string deviceInstancePath)
{
if ((deviceInstancePath.Contains("VID_0403")) && (deviceInstancePath.Contains("PID_6001")))
{ // 3 MBaud USB-to-serial cable
// Example: FTDIBUS\VID_0403+PID_6001+FT9FPQAKA\0000
// The serial number is FT9FPQAK
return deviceInstancePath.Substring(26, 8);
}
else if ((deviceInstancePath.Contains("VID_0403")) && (deviceInstancePath.Contains("PID_6014")))
{ // 12 MBaud USB-to-serial cable
// Example: FTDIBUS\VID_0403+PID_6014+FT478YLWA\0000
// The serial number is FT478YLW
return deviceInstancePath.Substring(26, 8);
}
return "";
}
private static string GetDeviceName(IntPtr pDevInfoSet, SP_DEVINFO_DATA deviceInfoData)
{
IntPtr hDeviceRegistryKey = SetupDiOpenDevRegKey(pDevInfoSet, ref deviceInfoData,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (hDeviceRegistryKey == IntPtr.Zero)
{
throw new Exception("Failed to open a registry key for device-specific configuration information");
}
StringBuilder deviceNameBuf = new StringBuilder(256);
try
{
uint lpRegKeyType;
uint length = (uint)deviceNameBuf.Capacity;
int result = RegQueryValueEx(hDeviceRegistryKey, "PortName", 0, out lpRegKeyType, deviceNameBuf, ref length);
if (result != 0)
{
throw new Exception("Can not read registry value PortName for device " + deviceInfoData.ClassGuid);
}
}
finally
{
RegCloseKey(hDeviceRegistryKey);
}
string deviceName = deviceNameBuf.ToString();
return deviceName;
}
}