在 C# 中查找有关通过 USB 连接的所有串行设备的信息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11458835/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Finding information about all serial devices connected through USB in C#
提问by sebo
My project requires detection of a specific device when it is connected to USB. The only way I can identify this device is by its description/device name, not the com port. What I have found to perform the correct function is using a WMI query and checking the name property:
我的项目需要在连接到 USB 时检测特定设备。我可以识别此设备的唯一方法是通过其描述/设备名称,而不是 com 端口。我发现执行正确功能的是使用 WMI 查询并检查 name 属性:
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * from WIN32_SerialPort");
foreach (ManagementObject port in searcher.Get())
{
deviceName = (string)foundPort.GetPropertyValue("Name");
...
I initially tested this by connecting my phone, and the query returned the phone found on COM3 as expected. Then, I connected another device (a USB to serial converter, which more closely resembles the device I need this project for) and the query simply did not find it. It only finds the phone. This device does, however, show up on port COM4 in Device Manager. To spite me even more, the SerialPort class finds both devices, but it does not provide the information I need to identify the device:
我最初通过连接我的手机对此进行了测试,并且查询返回了在 COM3 上找到的预期的手机。然后,我连接了另一个设备(一个 USB 到串行转换器,它更类似于我需要这个项目的设备)并且查询根本没有找到它。它只能找到电话。但是,此设备确实显示在设备管理器的 COM4 端口上。更让我恼火的是,SerialPort 类找到了两个设备,但它没有提供我识别设备所需的信息:
string[] tempPorts = SerialPort.GetPortNames();
I have read numerous threads on SO and elsewhere and cannot find a satisfactory solution. Could someone please clarify why the WIN32_SerialPort query does not find my other device? Is it not considered a win32 serial port for some reason? And, could someone please point me in the direction of a solution to this problem?
我已经阅读了许多关于 SO 和其他地方的主题,但找不到令人满意的解决方案。有人可以澄清为什么 WIN32_SerialPort 查询找不到我的其他设备吗?由于某种原因它不被认为是win32串口吗?而且,有人可以指出我解决这个问题的方向吗?
回答by Jay
I think i see what you are trying to do, look at this code made using WMICodeCreator ( link to WMICodeCreator http://www.microsoft.com/en-us/download/details.aspx?id=8572) from this article http://www.codeproject.com/Articles/32330/A-Useful-WMI-Tool-How-To-Find-USB-to-Serial-Adapto
我想我看到你正在尝试做的,看看这个代码使用WMICodeCreator造(链接到WMICodeCreator http://www.microsoft.com/en-us/download/details.aspx?id=8572从这篇文章)的HTTP ://www.codeproject.com/Articles/32330/A-Useful-WMI-Tool-How-To-Find-USB-to-Serial-Adapto
//Below is code pasted from WMICodeCreator
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\WMI",
"SELECT * FROM MSSerial_PortName");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);
Console.WriteLine("-----------------------------------");
Console.WriteLine("MSSerial_PortName instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("PortName: {0}", queryObj["PortName"]);
//If the serial port's instance name contains USB
//it must be a USB to serial device
if (queryObj["InstanceName"].ToString().Contains("USB"))
{
Console.WriteLine(queryObj["PortName"] + "
is a USB to SERIAL adapter/converter");
}
}
}
catch (ManagementException e)
{
MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}
回答by AlexS
How to list all serial ports:
如何列出所有串口:
There are several System-Defined Device Setup Classesavailable to hardware vendors. Properly written drivers for COM-Portsshould use the Ports (COM & LPT ports)-class (guid: 4d36e978-e325-11ce-bfc1-08002be10318). Probably this class is used by the device manager as well.
有多种系统定义的设备设置类可供硬件供应商使用。正确编写的驱动程序COM-Ports应该使用Ports (COM & LPT ports)-class (guid: 4d36e978-e325-11ce-bfc1-08002be10318)。可能这个类也被设备管理器使用。
So you can use the following query to list every serial port you also see in the devicemanager:
因此,您可以使用以下查询列出您在设备管理器中看到的每个串行端口:
ManagementObjectSearcher searcher = new ManagementObjectSearcher(
"root\CIMV2",
"SELECT * FROM Win32_PnPEntity WHERE ClassGuid=\"{4d36e978-e325-11ce-bfc1-08002be10318}\""
);
foreach (ManagementObject queryObj in searcher.Get())
{
// do what you like with the Win32_PnpEntity
}
See this detailed description of the Win32_PnPEntity-class. You should have everything you need for identifying your device.
请参阅Win32_PnPEntity类的详细说明。您应该拥有识别设备所需的一切。
For determining the port number I examine the name property and extract it. Until now this works fine, but I don't know if the port number is garanteed to be included in the name. I haven't found any serial port device until now, that doesn't have the port number included in the name.
为了确定端口号,我检查了 name 属性并将其提取。到目前为止,这工作正常,但我不知道端口号是否保证包含在名称中。到目前为止,我还没有找到任何串行端口设备,名称中没有包含端口号。
The above query finds every serial port device, no matter if it is a bluetooth SPP, a FTDI-chip, a port on the mainboard, an extension card or a virtual serial port generated by some modem driver (i.e. Globetrotter GTM66xxW).
上面的查询找到每一个串口设备,无论是蓝牙SPP、FTDI芯片、主板上的端口、扩展卡或由某些调制解调器驱动程序(即Globetrotter GTM66xxW)生成的虚拟串口。
To determine the type of connection (bluetooth, usb, etc.) you can examine the deviceid (have a look at the first part of the deviceid). There you can also extract the bt-mac address (be careful with that: the deviceid looks different at least on Windows 7 and Windows XP).
要确定连接类型(蓝牙、USB 等),您可以检查设备 ID(查看设备 ID 的第一部分)。您还可以在那里提取 bt-mac 地址(请注意:至少在 Windows 7 和 Windows XP 上,deviceid 看起来不同)。
Regarding why some devices are not listed with Win32_SerialPort:
关于为什么某些设备未与 Win32_SerialPort 一起列出:
I suspect it depends on the driver implementation, since I have some usb-devices that get their ports listed and some that don't.
我怀疑这取决于驱动程序的实现,因为我有一些 USB 设备列出了它们的端口,而有些则没有。
回答by Melbourne Developer
Seeing that you want to search by "Name", I think you will need to iterate through all connected devices and query them to get the product name.
看到您想按“名称”进行搜索,我认为您需要遍历所有连接的设备并查询它们以获取产品名称。
Here is code to iterate through WinUSB devices:
下面是遍历 WinUSB 设备的代码:
public async Task<IEnumerable<DeviceDefinition>> GetConnectedDeviceDefinitions(uint? vendorId, uint? productId)
{
return await Task.Run<IEnumerable<DeviceDefinition>>(() =>
{
var deviceDefinitions = new Collection<DeviceDefinition>();
var spDeviceInterfaceData = new SpDeviceInterfaceData();
var spDeviceInfoData = new SpDeviceInfoData();
var spDeviceInterfaceDetailData = new SpDeviceInterfaceDetailData();
spDeviceInterfaceData.CbSize = (uint)Marshal.SizeOf(spDeviceInterfaceData);
spDeviceInfoData.CbSize = (uint)Marshal.SizeOf(spDeviceInfoData);
var guidString = ClassGuid.ToString();
var copyOfClassGuid = new Guid(guidString);
var i = APICalls.SetupDiGetClassDevs(ref copyOfClassGuid, IntPtr.Zero, IntPtr.Zero, APICalls.DigcfDeviceinterface | APICalls.DigcfPresent);
if (IntPtr.Size == 8)
{
spDeviceInterfaceDetailData.CbSize = 8;
}
else
{
spDeviceInterfaceDetailData.CbSize = 4 + Marshal.SystemDefaultCharSize;
}
var x = -1;
while (true)
{
x++;
var isSuccess = APICalls.SetupDiEnumDeviceInterfaces(i, IntPtr.Zero, ref copyOfClassGuid, (uint)x, ref spDeviceInterfaceData);
if (!isSuccess)
{
var errorCode = Marshal.GetLastWin32Error();
if (errorCode == APICalls.ERROR_NO_MORE_ITEMS)
{
break;
}
throw new Exception($"Could not enumerate devices. Error code: {errorCode}");
}
isSuccess = APICalls.SetupDiGetDeviceInterfaceDetail(i, ref spDeviceInterfaceData, ref spDeviceInterfaceDetailData, 256, out _, ref spDeviceInfoData);
WindowsDeviceBase.HandleError(isSuccess, "Could not get device interface detail");
//Note this is a bit nasty but we can filter Vid and Pid this way I think...
var vendorHex = vendorId?.ToString("X").ToLower().PadLeft(4, '0');
var productIdHex = productId?.ToString("X").ToLower().PadLeft(4, '0');
if (vendorId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(vendorHex)) continue;
if (productId.HasValue && !spDeviceInterfaceDetailData.DevicePath.ToLower().Contains(productIdHex)) continue;
var deviceDefinition = GetDeviceDefinition(spDeviceInterfaceDetailData.DevicePath);
deviceDefinitions.Add(deviceDefinition);
}
APICalls.SetupDiDestroyDeviceInfoList(i);
return deviceDefinitions;
});
}
For each of these devices you can query the device like this:
对于这些设备中的每一个,您都可以像这样查询设备:
https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net/Windows/WindowsUsbDevice.cs
https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net/Windows/WindowsUsbDevice.cs
var isSuccess = WinUsbApiCalls.WinUsb_Initialize(_DeviceHandle, out var defaultInterfaceHandle);
HandleError(isSuccess, "Couldn't initialize device");
var bufferLength = (uint)Marshal.SizeOf(typeof(USB_DEVICE_DESCRIPTOR));
isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.DEFAULT_DESCRIPTOR_TYPE, 0, EnglishLanguageID, out _UsbDeviceDescriptor, bufferLength, out var lengthTransferred);
HandleError(isSuccess, "Couldn't get device descriptor");
if (_UsbDeviceDescriptor.iProduct > 0)
{
//Get the product name
var buffer = new byte[256];
isSuccess = WinUsbApiCalls.WinUsb_GetDescriptor(defaultInterfaceHandle, WinUsbApiCalls.USB_STRING_DESCRIPTOR_TYPE, _UsbDeviceDescriptor.iProduct, 1033, buffer, (uint)buffer.Length, out var transfered);
HandleError(isSuccess, "Couldn't get product name");
Product = new string(Encoding.Unicode.GetChars(buffer, 2, (int)transfered));
Product = Product.Substring(0, Product.Length - 1);
}
public static partial class WinUsbApiCalls
{
public const uint DEVICE_SPEED = 1;
public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
public const int WritePipeId = 0x80;
/// <summary>
/// Not sure where this constant is defined...
/// </summary>
public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;
public const int USB_STRING_DESCRIPTOR_TYPE = 0x03;
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);
[DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, UInt16 LanguageID, byte[] Buffer, UInt32 BufferLength, out UInt32 LengthTransfered);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);
[DllImport("winusb.dll", SetLastError = true)]
public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
}
回答by chapter12
Alex's answer really helped me out. But I had to adjust my query based on my machine...
亚历克斯的回答真的帮助了我。但是我不得不根据我的机器调整我的查询......
I had to query "root\CIMV2", but I had to select all rows from a different table: "SELECT * FROM Win32_SerialPort".
我必须查询“root\CIMV2”,但我必须从不同的表中选择所有行:“SELECT * FROM Win32_SerialPort”。
For anyone struggling to figure out how to use WMI or the Management Object Searcher, the WMI Code Creator by Microsoft really just saved me. Using the app, you can query WMI for different information in order to figure out what to put into your Management Object Searcher query.
对于那些努力弄清楚如何使用 WMI 或管理对象搜索器的人来说,Microsoft 的 WMI Code Creator 真的救了我。使用该应用程序,您可以查询 WMI 以获取不同的信息,以便找出要放入管理对象搜索器查询中的内容。
Link might expire in the future but here it is in 2020:
链接将来可能会过期,但现在是 2020 年:
https://www.microsoft.com/en-us/download/details.aspx?id=8572
https://www.microsoft.com/en-us/download/details.aspx?id=8572

