新 COM 端口可用事件

Posted

技术标签:

【中文标题】新 COM 端口可用事件【英文标题】:New COM port available event 【发布时间】:2012-05-20 00:10:46 【问题描述】:

我的 C# 应用程序使用 COM 端口。我遇到了一些大多数程序应该常见的困难。当端口名列表更改时,我需要获取一个事件。我有一个选择框,用户可以在其中从可用端口名称列表中进行选择。有没有人有这个的sn-p代码?谢谢。

【问题讨论】:

我猜您可以观看一般的“新 USB 设备”或“新 PNP 设备”系统事件,但您可能需要等待一秒钟让设备在新 COM 之前完成初始化不过,端口出现了。 @Rup - 你是对的。设备通知与 System.IO.Ports.SerialPort.GetPortNames() 反映更改的时间之间存在延迟。这就是为什么在该列表更新时收到通知真的很好。 【参考方案1】:

也可以借助“ManagementEventWatcher”来完成:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Management;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;

namespace HmxFlashLoader

    /// <summary>
    /// Make sure you create this watcher in the UI thread if you are using the com port list in the UI
    /// </summary>
    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public sealed class SerialPortWatcher : IDisposable
    
        public SerialPortWatcher()
        
            _taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            ComPorts = new ObservableCollection<string>(SerialPort.GetPortNames().OrderBy(s => s));

            WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent");

            _watcher = new ManagementEventWatcher(query);           
            _watcher.EventArrived += (sender, eventArgs) => CheckForNewPorts(eventArgs);
            _watcher.Start();       
        

        private void CheckForNewPorts(EventArrivedEventArgs args)
        
            // do it async so it is performed in the UI thread if this class has been created in the UI thread
            Task.Factory.StartNew(CheckForNewPortsAsync, CancellationToken.None, TaskCreationOptions.None, _taskScheduler);
        

        private void CheckForNewPortsAsync()
        
            IEnumerable<string> ports = SerialPort.GetPortNames().OrderBy(s => s);

            foreach (string comPort in ComPorts)
            
                if (!ports.Contains(comPort))
                
                    ComPorts.Remove(comPort);
                
            

            foreach (var port in ports)
                       
                if (!ComPorts.Contains(port))
                
                    AddPort(port);
                
            
        

        private void AddPort(string port)
        
            for (int j = 0; j < ComPorts.Count; j++)
            
                if (port.CompareTo(ComPorts[j]) < 0)
                
                    ComPorts.Insert(j, port);
                    break;
                
            

        

        public ObservableCollection<string> ComPorts  get; private set; 

        #region IDisposable Members

        public void Dispose()
        
            _watcher.Stop();    
        

        #endregion

        private ManagementEventWatcher _watcher;
        private TaskScheduler _taskScheduler;
    

【讨论】:

非常简洁的解决方案!谢谢老哥! 我认为当 ComPorts 在迭代 ComPorts 时更新时,这段代码会抛出异常...... @GTAE86 你能确认你的猜测是否属实吗?了解它会很有帮助。 @Sunburst275 仅从分析代码来看,我会说是的 - 在 CheckForNewPortsAsync 中,第一个 foreach 正在迭代 ComPorts,但 if-test 中的代码会更新 ComPorts。一般是禁止的。如果你快速编写代码,你会看到它产生“System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'” @GTAE86 啊,是的,我明白了。这确实是相当有问题的。应该看到的。谢谢!【参考方案2】:

COM 端口更改是罕见事件,而不是常见事件。

最简单的方法是设置一个计时器,每 10-30 秒枚举一次 COM 端口列表,如果发生更改,更新列表。

更好的是,提供一个“刷新列表”按钮 - 该列表基本上只有在用户插入 USB 串行适配器时才会改变。

【讨论】:

codeproject.com/Articles/60579/… 将向您展示如何监听 USB 连接/分离事件。此外,10-30 秒对用户来说可能太长了,为刷新按钮 +1 投票。【参考方案3】:

创建一个简单的表单应用程序,并将以下代码放入表单中:

protected override void WndProc(ref Message m)

    switch (m.Msg)
    
        case 537: //WM_DEVICECHANGE
            var ports = SerialPort.GetPortNames().OrderBy(name => name);

            foreach (var portName in ports)
            
                Debug.Print(portName);
            
            break;
    
    base.WndProc(ref m);

【讨论】:

这可以在 Windows 服务消息泵中工作吗?操作系统:Win7。如果没有,那么有什么解决方法吗? @AdrianSalazar:不知道,没试过。但我看不出它不应该工作的任何理由。只需尝试一下,如果不起作用,请针对您的问题提出一个新问题。 这个小小的“覆盖”关键字是我担心的。从未在 Windows 服务中看到过这种特定方法,因此,请注意覆盖。 @AdrianSalazar:你是对的。在这种情况下,请查看this SO question。

以上是关于新 COM 端口可用事件的主要内容,如果未能解决你的问题,请参考以下文章

记一次2000端口灵异事件

侦听 COM 端口时跨线程操作无效[重复]

使用 node.js 访问虚拟串行端口会给出错误或没有打开事件

关于问题解决与事件解决的思考

将事件侦听器添加到在新窗口中打开的文档中

如何自动检测串行 COM 端口 c# 的连接