我在查找关于用C# 监控USB转串口 的插入和拨出消息时 ,网上找了很多资料基本都是下边这一个代码
1 #region 检测USB串口的热拔插 2 // usb消息定义 3 public const int WM_DEVICE_CHANGE = 0x219; 4 public const int DBT_DEVICEARRIVAL = 0x8000; 5 public const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004; 6 public const UInt32 DBT_DEVTYP_PORT = 0x00000003; 7 [StructLayout(LayoutKind.Sequential)] 8 struct DEV_BROADCAST_HDR 9 { 10 public UInt32 dbch_size; 11 public UInt32 dbch_devicetype; 12 public UInt32 dbch_reserved; 13 } 14 15 [StructLayout(LayoutKind.Sequential)] 16 protected struct DEV_BROADCAST_PORT_Fixed 17 { 18 public uint dbcp_size; 19 public uint dbcp_devicetype; 20 public uint dbcp_reserved; 21 // Variable?length field dbcp_name is declared here in the C header file. 22 } 23 24 /// <summary> 25 /// 检测USB串口的拔插 26 /// </summary> 27 /// <param name="m"></param> 28 protected override void WndProc(ref Message m) 29 { 30 DEV_BROADCAST_HDR dbhdr; 31 if(m.Msg == WM_DEVICE_CHANGE) // 捕获USB设备的拔出消息WM_DEVICECHANGE 32 { 33 switch(m.WParam.ToInt32()) 34 { 35 case DBT_DEVICE_REMOVE_COMPLETE: // USB拔出 36 dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR)); 37 if(dbhdr.dbch_devicetype == DBT_DEVTYP_PORT) 38 { 39 string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed)))); 40 Console.WriteLine("Port '" + portName + "' removed."); 41 42 if(portName.CompareTo(serial.PortName) == 0) 43 { 44 serial_port_close(); 45 } 46 } 47 break; 48 case DBT_DEVICEARRIVAL: // USB插入获取对应串口名称 49 dbhdr = (DEV_BROADCAST_HDR)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HDR)); 50 if(dbhdr.dbch_devicetype == DBT_DEVTYP_PORT) 51 { 52 string portName = Marshal.PtrToStringUni((IntPtr)(m.LParam.ToInt32() + Marshal.SizeOf(typeof(DEV_BROADCAST_PORT_Fixed)))); 53 Console.WriteLine("Port '" + portName + "' arrived."); 54 } 55 break; 56 } 57 } 58 base.WndProc(ref m); 59 } 60 #endregion
这段代码我在笔记本上测试的,发现只能捕获到WM_DEVICE_CHANGE消息,并且m.WParam.ToInt32()的值始终为7,目前仍然没有找到原因,有知道原因的朋友麻烦告诉我一下,所以这种方法没办法监测USB转串口。
第2种办法是能获取USB转串口的一些信息,不再是单单的COM多少多少,而是能分辨出那个USB转的串口 代码如下
1 ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From " + "Win32_SerialPort"); 2 foreach (ManagementObject mo in searcher.Get()) 3 { 4 txt_show_recv.AppendText("============" + "串口" + "开始======\r\n"); 5 foreach (PropertyData pd in mo.Properties) 6 { 7 txt_show_recv.Text += pd.Name + " : "; 8 if (pd.Value != null) 9 { 10 txt_show_recv.Text += pd.Value.ToString(); 11 } 12 txt_show_recv.Text += "\r\n"; 13 } 14 txt_show_recv.AppendText("============" + "串口" + "结束======\r\n\r\n\"); 15 16 }
上边这段代码打印出的信息如下 从这些信息里取自己想要的即可
============串口信息开始====== Availability : 2 Binary : True Capabilities : CapabilityDescriptions : Caption : USB Serial Port (COM3) ConfigManagerErrorCode : 0 ConfigManagerUserConfig : False CreationClassName : Win32_SerialPort Description : USB Serial Port DeviceID : COM3 ErrorCleared : ErrorDescription : InstallDate : LastErrorCode : MaxBaudRate : 115200 MaximumInputBufferSize : 0 MaximumOutputBufferSize : 0 MaxNumberControlled : Name : USB Serial Port (COM3) OSAutoDiscovered : True PNPDeviceID : USB\VID_04D8&PID_00DF&MI_00\6&248B9296&0&0000 PowerManagementCapabilities : System.UInt16[] PowerManagementSupported : False ProtocolSupported : ProviderType : Modem Device SettableBaudRate : True SettableDataBits : True SettableFlowControl : True SettableParity : True SettableParityCheck : True SettableRLSD : True SettableStopBits : True Status : OK StatusInfo : 3 Supports16BitMode : False SupportsDTRDSR : True SupportsElapsedTimeouts : True SupportsIntTimeouts : True SupportsParityCheck : True SupportsRLSD : True SupportsRTSCTS : False SupportsSpecialCharacters : False SupportsXOnXOff : False SupportsXOnXOffSet : False SystemCreationClassName : Win32_ComputerSystem SystemName : PC-20161106GRIO TimeOfLastReset : ============串口信息结束======
第3种办法 下边这个办法就类似于设备管理器那样,从系统里枚举出设备信息 然后筛选出所有的串口设备,不过我测试稍 在获取列表时稍为有点时间长,有差不1S左右,不过可以把那个enum HardwareEnum里的信息删除一部分不需要的 速度就快了。
/// <summary> /// 枚举win32 api /// </summary> public enum HardwareEnum { // 硬件 Win32_Processor, // CPU 处理器 Win32_PhysicalMemory, // 物理内存条 Win32_Keyboard, // 键盘 Win32_PointingDevice, // 点输入设备,包括鼠标。 Win32_FloppyDrive, // 软盘驱动器 Win32_DiskDrive, // 硬盘驱动器 Win32_CDROMDrive, // 光盘驱动器 Win32_BaseBoard, // 主板 Win32_BIOS, // BIOS 芯片 Win32_ParallelPort, // 并口 Win32_SerialPort, // 串口 Win32_SerialPortConfiguration, // 串口配置 Win32_SoundDevice, // 多媒体设置,一般指声卡。 Win32_SystemSlot, // 主板插槽 (ISA & PCI & AGP) Win32_USBController, // USB 控制器 Win32_NetworkAdapter, // 网络适配器 Win32_NetworkAdapterConfiguration, // 网络适配器设置 Win32_Printer, // 打印机 Win32_PrinterConfiguration, // 打印机设置 Win32_PrintJob, // 打印机任务 Win32_TCPIPPrinterPort, // 打印机端口 Win32_POTSModem, // MODEM Win32_POTSModemToSerialPort, // MODEM 端口 Win32_DesktopMonitor, // 显示器 Win32_DisplayConfiguration, // 显卡 Win32_DisplayControllerConfiguration, // 显卡设置 Win32_VideoController, // 显卡细节。 Win32_VideoSettings, // 显卡支持的显示模式。 // 操作系统 Win32_TimeZone, // 时区 Win32_SystemDriver, // 驱动程序 Win32_DiskPartition, // 磁盘分区 Win32_LogicalDisk, // 逻辑磁盘 Win32_LogicalDiskToPartition, // 逻辑磁盘所在分区及始末位置。 Win32_LogicalMemoryConfiguration, // 逻辑内存配置 Win32_PageFile, // 系统页文件信息 Win32_PageFileSetting, // 页文件设置 Win32_BootConfiguration, // 系统启动配置 Win32_ComputerSystem, // 计算机信息简要 Win32_OperatingSystem, // 操作系统信息 Win32_StartupCommand, // 系统自动启动程序 Win32_Service, // 系统安装的服务 Win32_Group, // 系统管理组 Win32_GroupUser, // 系统组帐号 Win32_UserAccount, // 用户帐号 Win32_Process, // 系统进程 Win32_Thread, // 系统线程 Win32_Share, // 共享 Win32_NetworkClient, // 已安装的网络客户端 Win32_NetworkProtocol, // 已安装的网络协议 Win32_PnPEntity,//all device } /// <summary> /// WMI取硬件信息 /// </summary> /// <param name="hardType"></param> /// <param name="propKey"></param> /// <returns></returns> public static string[] MulGetHardwareInfo(HardwareEnum hardType, string propKey) { List<string> strs = new List<string>(); try { using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + hardType)) { var hardInfos = searcher.Get(); foreach (var hardInfo in hardInfos) {
if (hardInfo.Properties[propKey].Value != null) {
if (hardInfo.Properties[propKey].Value.ToString().Contains("COM")) { strs.Add(hardInfo.Properties[propKey].Value.ToString()); } } } searcher.Dispose(); } return strs.ToArray(); } catch { return null; } finally { strs = null; } } //通过WMI获取COM端口 string[] ss = MulGetHardwareInfo(HardwareEnum.Win32_PnPEntity, "Name");
要想更快直接指定Win32_SerialPort,这样就比较快了,但是这样虚拟串口无法检测到,不过这样也不影响使用,毕竟实际环境中也不会用虚拟串口的
public string[] MulGetHardwareInfo(string hardType, string propKey) { List<string> strs = new List<string>(); try { using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + hardType)) { var hardInfos = searcher.Get(); foreach (var hardInfo in hardInfos) {
if (hardInfo.Properties[propKey].Value != null) {
if (hardInfo.Properties[propKey].Value.ToString().Contains("COM")) { strs.Add(hardInfo.Properties[propKey].Value.ToString()); } }
}
searcher.Dispose();
}
return strs.ToArray();
}
catch { return null; }
finally { strs = null; }
} //通过WMI获取COM端口
string[] port_names = MulGetHardwareInfo("Win32_SerialPort", "Name");
第四种方法 就是直接调用C#封装好的类库的
private void cbo_port_name_DropDown(object sender, EventArgs e) { string port_name_old = cbo_port_name.Text;string[] port_names = SerialPort.GetPortNames(); Array.Sort(port_names, new CustomComparer()); cbo_port_name.Items.Clear(); cbo_port_name.Items.AddRange(port_names); cbo_port_name.Text = port_name_old; if((cbo_port_name.Items.Count > 0) && (cbo_port_name.SelectedIndex < 0)) { cbo_port_name.SelectedIndex = 0; } }
第五种方法 直接调用open方法来依次打开端口,能打开说明端口存在,否则不存在
for (int i = 0; i < 20; i++)//启动时搜索可用串口 { try { SerialPort sport = new SerialPort("COM" + (i + 1).ToString()); sport.Open(); sport.Close(); cmbBox_Port.Items.Add("COM" + (i + 1).ToString()); ComPort_Exit = true; } catch { continue; } if (ComPort_Exit == true) { cmbBox_Port.SelectedIndex = 0;//默认选择COM1; } else { MessageBox.Show("没有找到可用串口", "错误提示"); } }