对于上位机开发,经常需要对电脑的串口进行操作,本文介绍了如何读取电脑可用的串口号列表以及动态跟踪串口信息的变化,及时更新串口列表信息。

我们使用WPF的HwndSource.AddHook方法添加接收所有窗口消息的事件处理程序。你可以简单地将他理解为添加一个“钩子”。

先看一下效果图:

【技术分享】WPF动态加载串口信息_上位机

【技术分享】WPF动态加载串口信息_串口_02

在运行时,直接将COM2设备拔除,程序自动检测到变化,并更新到Combox。

废话不多说,直接看代码吧。

<Window x:Class="SerialTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SerialTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
    <Grid>
        <ComboBox x:Name="cb_ComName" Grid.Column="1" VerticalAlignment="Center" Width="100"
          HorizontalAlignment="Stretch" />
    </Grid>
</Window>

前端代码就这么简单,只是作为测试使用。

using System;
using System.IO.Ports;
using System.Windows;
using System.Windows.Interop;

namespace SerialTest
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {


        public MainWindow()
        {
            InitializeComponent();
        }

        public const int WM_DEVICECHANGE = 0x219;
        public const int DBT_DEVICEARRIVAL = 0x8000;
        public const int DBT_DEVICEREMOVECOMPLETE = 0x8004;

        private IntPtr DeviceChanged(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_DEVICECHANGE)
            {
                switch (wParam.ToInt32())
                {
                    case DBT_DEVICEREMOVECOMPLETE:
                        Rebind_Cb_COMName();
                        break;
                    case DBT_DEVICEARRIVAL:
                        Rebind_Cb_COMName();
                        break;
                    default:
                        break;
                }
            }
            return IntPtr.Zero;
        }

        private void Rebind_Cb_COMName()
        {
            Dispatcher.Invoke(new Action(() => {
                cb_ComName.ItemsSource = SerialPort.GetPortNames();
            }));
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (PresentationSource.FromVisual(this) is HwndSource hwndSource)
            {
                hwndSource.AddHook(new HwndSourceHook(DeviceChanged));
            }
        }
    }
}

这是全部的后台代码。

在窗口加载事件中,将“DeviceChanged”函数加入到hwndSource中,在“DeviceChanged”中对事件进行筛选,符合条件的事件则调用“ Rebind_Cb_COMName”对Combox的ItemSourc进行重新绑定,以更新串口列表。