参考文档
https://www.cnblogs.com/catzhou/archive/2018/06/08/9156863.html
C#怎么获取已知USB设备驱动信息(请看内容) (microsoft.com)
遍历Windows设备树的几种方法_飞鹤的程序员人生-CSDN博客
目录
1. 新建类SetupAPI.cs,引用SetupAPI.dll中的操作
1.2. API函数SetupDiEnumDeviceInfo
1.3 API函数SetupDiGetDeviceInstanceId
1.4 API函数SetupDiGetDeviceRegistryProperty
2. 新建类USB2SerialPort,继承System.IO.Ports.SerialPort
2.2.1 生成VID和PID字符串,后面会用这个字符串过滤COM口
2.2.2 调用SetupDiGetClassDevs获取全部类别的所有已安装设备的信息
2.2.3 通过SetupDiEnumDeviceInfo遍历所有设备,找到COM端口号
2.2.1 前面与GetCom一样,到遍历所有设备,这里需要遍历2遍,第一遍是遍历到Class为“USB”,去获取设备的父系设备。
2.2.2 第二遍遍历和GetCom类似,先找到对应VID和PID的COM口,然后读取各个信息
1. 新建类SetupAPI.cs,引用SetupAPI.dll中的操作
1.1 API函数SetupDiGetClassDevs
-
[
DllImport("SetupAPI.dll")]
-
public static extern IntPtr SetupDiGetClassDevs(
-
ref Guid ClassGuid,
-
UInt32 Enumerator,
-
IntPtr hwndParent,
-
UInt32 Flags
-
);
最后一个参数Flags的定义:
-
public
const UInt32 DIGCF_DEFAULT =
0x00000001;
// only valid with DIGCF_DEVICEINTERFACE
-
public
const UInt32 DIGCF_PRESENT =
0x00000002;
-
public
const UInt32 DIGCF_ALLCLASSES =
0x00000004;
-
public
const UInt32 DIGCF_PROFILE =
0x00000008;
-
public
const UInt32 DIGCF_DEVICEINTERFACE =
0x00000010;
调用例程:
-
// 获取全部类别的所有已安装设备的信息
-
UInt32 dwFlag = (SetupAPI.DIGCF_ALLCLASSES | SetupAPI.DIGCF_PRESENT);
-
Guid usbGuid = Guid.Empty;
-
hDevInfo = SetupAPI.SetupDiGetClassDevs(
ref usbGuid,
0, IntPtr.Zero, dwFlag);
1.2. API函数SetupDiEnumDeviceInfo
-
[
StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
-
public
struct SP_DEVINFO_DATA
-
{
-
public
int cbSize;
-
public Guid ClassGuid;
-
public IntPtr DevInst;
-
public IntPtr Reserved;
-
}
-
-
[
DllImport("SetupAPI.dll")]
-
public static extern Boolean SetupDiEnumDeviceInfo(
-
IntPtr DeviceInfoSet,
-
UInt32 MemberIndex,
-
ref SP_DEVINFO_DATA DeviceInfoData
-
);
注意这里的SP_DEVINFO_DATA结构需要按照上面的方式声明,否则调用SetupDiGetClassDevs会返回False。SetupDiEnumDeviceInfo是获取DeviceInfoData信息。
1.3 API函数SetupDiGetDeviceInstanceId
-
[
DllImport("SetupAPI.dll")]
-
public static extern Boolean SetupDiGetDeviceInstanceId(
-
IntPtr DeviceInfoSet,
-
ref SP_DEVINFO_DATA DeviceInfoData,
-
byte[] DeviceInstanceId,
-
UInt32 DeviceInstanceIdSize,
-
ref UInt32 RequiredSize
-
);
调用例程:
-
// 获取ID
-
if (!SetupAPI.SetupDiGetDeviceInstanceId(hDevInfo,
ref sDevInfoData, PropertyBuffer, (UInt32)PropertyBuffer.Length,
ref nSize))
-
{
-
continue;
-
}
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer);
1.4 API函数SetupDiGetDeviceRegistryProperty
-
[
DllImport("SetupAPI.dll", CharSet = CharSet.Ansi)]
-
public static extern Boolean SetupDiGetDeviceRegistryProperty(
-
IntPtr DeviceInfoSet,
-
ref SP_DEVINFO_DATA DeviceInfoData,
-
UInt32 Property,
-
ref UInt32 PropertyRegDataType,
-
byte[] PropertyBuffer,
-
UInt32 PropertyBufferSize,
-
ref UInt32 RequiredSize
-
);
调用例程:
-
//读取Hardware ID
-
byte[] PropertyBuffer =
new
byte[
260];
-
UInt32 PropertyRegDataType =
0;
-
SetupAPI.SetupDiGetDeviceRegistryProperty(
-
hDevInfo,
-
ref sDevInfoData,
-
SetupAPI.SPDRP_HARDWAREID,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer);
2. 新建类USB2SerialPort,继承System.IO.Ports.SerialPort
-
public
class
USB2SerialPort:
System.IO.Ports.SerialPort
-
{
-
-
}
2.1 API函数GetCom
-
public static eUSB2SerialPortStatus GetCom(
-
UInt16 vid,
-
UInt16 pid,
-
byte[] port
-
)
作用:找到对应VID和PID的串口COM编号。
参数:vid和pid分别是USB转串口芯片的VID和PID,为0时表示该过滤条件为空;port是返回的COM编号数组。
2.2.1 生成VID和PID字符串,后面会用这个字符串过滤COM口
-
string strVidPid;
//= string.Format("VID_{0:X4}&PID_{1:X4}", vid, pid);
-
string strVid, strPid;
-
if (vid ==
0)
-
strVid =
"";
-
else
-
strVid =
string.Format(
"VID_{0:X4}", vid);
-
if (pid ==
0)
-
strPid =
"";
-
else
-
strPid =
string.Format(
"PID_{0:X4}", pid);
-
strVidPid = strVid +
"&" + strPid;
-
strVidPid.ToUpper();
2.2.2 调用SetupDiGetClassDevs获取全部类别的所有已安装设备的信息
-
IntPtr hDevInfo = SetupAPI.INVALID_HANDLE_VALUE;
-
UInt32 dwFlag = (SetupAPI.DIGCF_ALLCLASSES | SetupAPI.DIGCF_PRESENT);
-
Guid usbGuid = Guid.Empty;
-
hDevInfo = SetupAPI.SetupDiGetClassDevs(
ref usbGuid,
0, IntPtr.Zero, dwFlag);
-
if (SetupAPI.INVALID_HANDLE_VALUE == hDevInfo)
-
return eUSB2SerialPortStatus.NO_DEVICE;
2.2.3 通过SetupDiEnumDeviceInfo遍历所有设备,找到COM端口号
-
sDevInfoData.cbSize = Marshal.SizeOf(
new SetupAPI.SP_DEVINFO_DATA());
-
sDevInfoData.ClassGuid = Guid.Empty;
-
sDevInfoData.DevInst = IntPtr.Zero;
-
sDevInfoData.Reserved = IntPtr.Zero;
-
-
for (UInt32 i =
0; SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, i,
ref sDevInfoData); i++)
-
{
-
-
}
2.2.3.1 通过SetupDiGetDeviceRegistryProperty读取设备的Class,这里数组PropertyBuffer只定义了4096个字节,更好的方式是调用2次SetupDiGetDeviceRegistryProperty获取实际数据的长度。
-
byte[] PropertyBuffer =
new
byte[
4096];
-
UInt32 PropertyRegDataType =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty
-
(
-
hDevInfo,
-
ref sDevInfoData,
-
SetupAPI.SPDRP_CLASS,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if(nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1
-
);
2.2.3.2找到Class为“PORTS”的设备
-
if(strTemp.ToUpper() ==
"PORTS")
-
{
-
}
2.2.3.3读取该设备的友好名称,找到设备的COM端口号
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_FRIENDLYNAME,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
-
// "XXX Virtual Com Port (COM?)"
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
strTemp = strTemp.ToUpper();
-
-
int nStart =
-1;
-
int nEnd =
-1;
-
// 寻找串口信息
-
nStart = strTemp.IndexOf(
"(COM");
-
nEnd = strTemp.IndexOf(
")");
-
-
if (nStart >
-1 && nEnd >
-1)
-
{
-
strTemp = strTemp.Substring(nStart +
4, nEnd - nStart -
4);
-
comPort =
byte.Parse(strTemp);
-
}
-
else
-
continue;
2.2.3.4 读取该设备的Hardware ID,判断VID和PID是否一致,如果一致则将COM编号保存到port数组中。
-
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(
-
hDevInfo,
-
ref sDevInfoData,
-
SetupAPI.SPDRP_HARDWAREID,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
-
// 根据设备信息寻找VID PID一致的设备
-
strTemp.ToUpper();
-
if (strTemp.IndexOf(strVidPid) ==
-1)
-
{
-
continue;
-
}
-
-
port[n] = comPort;
-
n++;
2.2.4 遍历完成后释放设备
SetupAPI.SetupDiDestroyDeviceInfoList(hDevInfo);
2.2 API函数GetInfo
-
public static eUSB2SerialPortStatus GetInfo(
-
UInt16 vid,
-
UInt16 pid,
-
ref stUsb2UartDevice[] pstDev
-
)
获取USB转串口设备的详细信息,包括供应商,序列号,描述符等。
参数:vid和pid分别是USB转串口芯片的VID和PID,为0时表示该过滤条件为空;pstDev是返回的详细信息,其定义如下:
-
public
struct stUsb2UartDevice
-
{
-
public
byte port;
-
public
byte usbIF;
-
public
string ParentSN;
-
public
string Manufacturer;
-
public
string DeviceDsc;
-
public
string SerialNumber;
-
};
其中port表示该设备的COM编号;usbIF是设备是第几个Interface;ParentSN是设备父系的序列号;Manufacturer是供应商;DeviceDsc是设备的描述符;SerialNumber是设备的序列号。
2.2.1 前面与GetCom一样,到遍历所有设备,这里需要遍历2遍,第一遍是遍历到Class为“USB”,去获取设备的父系设备。
-
SetupAPI.SP_DEVINFO_DATA sDevInfoData =
new SetupAPI.SP_DEVINFO_DATA();
-
sDevInfoData.cbSize = Marshal.SizeOf(
new SetupAPI.SP_DEVINFO_DATA());
-
sDevInfoData.ClassGuid = Guid.Empty;
-
sDevInfoData.DevInst = IntPtr.Zero;
-
sDevInfoData.Reserved = IntPtr.Zero;
-
for (UInt32 i =
0; SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, i,
ref sDevInfoData); i++)
-
{
-
-
}
2.2.1.1 通过SetupDiGetDeviceRegistryProperty获取设备的Class,判读是否是“USB”设备
-
//读取Class
-
byte[] PropertyBuffer =
new
byte[
1024];
-
UInt32 PropertyRegDataType =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty
-
(
-
hDevInfo,
-
ref sDevInfoData,
-
SetupAPI.SPDRP_CLASS,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
-
if (strTemp.ToUpper() ==
"USB")
-
{
-
-
}
2.2.1.2 通过SetupDiGetDeviceInstanceId读入ID,这个ID包括VID、PID和SerialNumber,判断是否VID和PID一致
-
string szID =
"";
-
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceInstanceId(hDevInfo,
ref sDevInfoData, PropertyBuffer, (UInt32)PropertyBuffer.Length,
ref nSize);
-
if (nSize >
0)
-
szID = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
szID.ToUpper();
-
-
if (szID.IndexOf(strVidPid) !=
-1)
-
{
-
-
}
2.2.1.3 VID和PID一致后读入设备的Location Path,保存在一个ArrayList中,等查找到Port时再看是否为父系。
-
nSize =
0;
-
string szPath =
"";
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_LOCATION_PATHS,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
szPath = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
-
usbParent.Path = szPath.Substring(
0, szPath.IndexOf(
'\0'));
-
usbParent.SN = szID.Substring(szID.LastIndexOf((
'\\')) +
1, szID.Length - szID.LastIndexOf((
'\\')) -
1);
-
ParentList.Add(usbParent);
2.2.2 第二遍遍历和GetCom类似,先找到对应VID和PID的COM口,然后读取各个信息
2.2.2.1 读取USB设备的SerialNumber
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
if (!SetupAPI.SetupDiGetDeviceInstanceId(hDevInfo,
ref sDevInfoData, PropertyBuffer, (UInt32)PropertyBuffer.Length,
ref nSize))
-
{
-
continue;
-
}
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
pstDev[n].SerialNumber = strTemp.Substring(strTemp.LastIndexOf((
'\\')) +
1, strTemp.Length - strTemp.LastIndexOf((
'\\')) -
1);
2.2.2.2 读取USB设备的Manufacturer
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_MFG,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
pstDev[n].Manufacturer = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
2.2.2.3 读取USB设备的DeviceDsc
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_DEVICEDESC,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
pstDev[n].DeviceDsc = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
2.2.2.3 读取USB设备的Location Path,然后判断这个Path是否在前面找到父系路径内(父系路径最长那个是最终的结果,所以要遍历所有的父系设备)
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_LOCATION_PATHS,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
-
-
strTemp = strTemp.Substring(
0, strTemp.IndexOf(
'\0'));
-
int parantLen =
0;
-
for(
int j =
0; j < ParentList.Count; j++)
-
{
-
stUSBParent parent = (stUSBParent)ParentList[j];
-
if (strTemp.IndexOf(parent.Path) !=
-1 && parent.Path.Length > parantLen)
-
{
-
pstDev[n].ParentSN = parent.SN;
-
parantLen = parent.Path.Length;
-
}
-
}
2.2.2.4 通过关键字USBMI找到接口编号(似乎这个关键字并不是所有的USB转串口设备都有)
-
nStart = strTemp.IndexOf(
"#USBMI(");
-
nEnd = strTemp.LastIndexOf(
")");
-
-
if (nStart >
-1 && nEnd >
-1)
-
{
-
strTemp = strTemp.Substring(nStart +
7, nEnd - nStart -
7);
-
pstDev[n].usbIF =
byte.Parse(strTemp);
-
-
}
另外通过读取SPDRP_SECURITY_SDS可以得到一个值,和USBMI的编号一致(不确定是否就是接口编号)。
-
nSize =
0;
-
Array.Clear(PropertyBuffer,
0, PropertyBuffer.Length);
-
SetupAPI.SetupDiGetDeviceRegistryProperty(hDevInfo,
ref sDevInfoData,
-
SetupAPI.SPDRP_SECURITY_SDS,
-
ref PropertyRegDataType, PropertyBuffer,
-
(UInt32)PropertyBuffer.Length,
-
ref nSize);
-
if (nSize >
0)
-
strTemp = System.Text.Encoding.Default.GetString(PropertyBuffer,
0, (
int)nSize -
1);
获取父系的方式不是通用的,对于FTDI的FT4232H来说,它的路径都在USB类里面,以下面的打印信息为例:
Parent Path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(4)#USB(4)#USB(2)#USBMI(2)
Parent Path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(4)#USB(4)#USB(2)
Parent Path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(4)#USB(4)#USB(2)#USBMI(3)
Parent Path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(4)#USB(4)#USB(2)#USBMI(0)
Parent Path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(4)#USB(4)#USB(2)#USBMI(1)
得到的父系路径已经包括了子系路径,而PORTS类里面得到的路径是
LocationPath: FTDIBUS\VID_0403+PID_6011+FT9EF1UB\0000
但是以Path为方法还是可以得到设备的父子系关系,针对不同品牌的芯片方案可能需要调整自己的代码。
转载:https://blog.csdn.net/pq113_6/article/details/116272591