System.IO.Ports:如何让GetPortNames()获取隐藏串口?
获取包含隐藏串口的完整列表
SerialPort.GetPortNames()仅能检测系统中已启用的串口,无法识别设备管理器里的隐藏串口,且该方法无参数可调整。你尝试的WMI查询返回空,可通过以下几种方法获取完整串口列表:
方法1:调整WMI查询(包含隐藏设备)
默认WMI查询不会返回隐藏设备,需修改查询逻辑,同时覆盖不同状态的设备。
using System.Management; // 枚举所有含COM标识的PnP设备(含隐藏) var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Caption LIKE '%(COM%'"); foreach (ManagementObject obj in searcher.Get()) { string caption = obj["Caption"]?.ToString(); string status = obj["Status"]?.ToString(); Console.WriteLine($" - {caption} (状态: {status})"); }
若仍无结果,可尝试重启WMI服务(执行命令winmgmt /reset),或确认程序以管理员权限运行。
方法2:读取注册表获取所有串口
Windows会将所有串口信息(含隐藏)存储在注册表中,可直接读取对应路径获取:
using Microsoft.Win32; // 读取USB类下的串口设备 RegistryKey usbKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Enum\USB"); if (usbKey != null) { foreach (string subKeyName in usbKey.GetSubKeyNames()) { using (RegistryKey subKey = usbKey.OpenSubKey(subKeyName)) { if (subKey != null) { foreach (string deviceKeyName in subKey.GetSubKeyNames()) { using (RegistryKey deviceKey = subKey.OpenSubKey(deviceKeyName)) { if (deviceKey != null) { using (RegistryKey deviceParams = deviceKey.OpenSubKey("Device Parameters")) { string portName = deviceParams?.GetValue("PortName")?.ToString(); if (!string.IsNullOrEmpty(portName) && portName.StartsWith("COM")) { Console.WriteLine($" - {portName}"); } } } } } } } } } // 读取Serial服务下的串口信息 RegistryKey serialKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Services\Serial\Enum"); if (serialKey != null) { foreach (string valueName in serialKey.GetValueNames()) { string portInfo = serialKey.GetValue(valueName)?.ToString(); if (!string.IsNullOrEmpty(portInfo)) { int comIndex = portInfo.IndexOf("COM"); if (comIndex != -1) { string portName = portInfo.Substring(comIndex); int endIndex = portName.IndexOfAny(new char[] { ' ', '(', ')' }); if (endIndex != -1) portName = portName.Substring(0, endIndex); Console.WriteLine($" - {portName}"); } } } }
注意:此方法需要管理员权限,否则可能无法访问部分注册表项。
方法3:使用SetupAPI枚举设备(最可靠)
通过调用Windows原生SetupAPI可枚举所有设备(含隐藏),无需依赖WMI,可靠性更高:
using System; using System.Runtime.InteropServices; public class SerialPortEnumerator { [DllImport("setupapi.dll", SetLastError = true)] private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr hwndParent, uint Flags); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, uint Property, out uint PropertyRegDataType, IntPtr PropertyBuffer, uint PropertyBufferSize, out uint RequiredSize); [DllImport("setupapi.dll", SetLastError = true)] private static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); private const uint DIGCF_ALLCLASSES = 0x00000004; private const uint DIGCF_PRESENT = 0x00000002; private const uint SPDRP_FRIENDLYNAME = 0x0000000C; [StructLayout(LayoutKind.Sequential)] private struct SP_DEVINFO_DATA { public uint cbSize; public Guid ClassGuid; public uint DevInst; public IntPtr Reserved; } public static void GetAllSerialPorts() { Guid serialPortGuid = new Guid("{4d36e978-e325-11ce-bfc1-08002be10318}"); // 串口类GUID IntPtr deviceInfoSet = SetupDiGetClassDevs(ref serialPortGuid, 0, IntPtr.Zero, DIGCF_ALLCLASSES | DIGCF_PRESENT); if (deviceInfoSet == IntPtr.Zero) { Console.WriteLine("无法获取设备信息集"); return; } SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA(); deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData); uint index = 0; while (SetupDiEnumDeviceInfo(deviceInfoSet, index, ref deviceInfoData)) { uint regDataType; uint requiredSize; SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_FRIENDLYNAME, out regDataType, IntPtr.Zero, 0, out requiredSize); IntPtr buffer = Marshal.AllocHGlobal((int)requiredSize); if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_FRIENDLYNAME, out regDataType, buffer, requiredSize, out requiredSize)) { string friendlyName = Marshal.PtrToStringAuto(buffer); if (!string.IsNullOrEmpty(friendlyName) && friendlyName.Contains("(COM")) { Console.WriteLine($" - {friendlyName}"); } } Marshal.FreeHGlobal(buffer); index++; } SetupDiDestroyDeviceInfoList(deviceInfoSet); } } // 调用示例 SerialPortEnumerator.GetAllSerialPorts();
内容的提问来源于stack exchange,提问作者SiL3NC3
相关产品推荐
相关产品推荐

