【问题标题】:see what is attached to a com port and show in combo box查看连接到 com 端口的内容并显示在组合框中
【发布时间】:2023-02-12 00:11:31
【问题描述】:

使用下面的代码,我可以创建一个带有显示当前 com 端口的组合框的框 我需要做的是显示连接到 com 端口的内容,例如我希望它列出 COM PORT1 FTDI USB 串行适配器,原因是让用户知道在单击另一个按钮时运行的批处理文件中输入哪个端口(我已经删除了那部分代码,因为它不重要) 我已经完成了一些谷歌工作并找到了这个链接http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/331a26c1-0f42-4cf1-8adb-32fb09a18953/ 但那只是错误

    Imports System
    Imports System.Threading
    Imports System.IO.Ports
    Imports System.ComponentModel


    Public Class Form1
    '------------------------------------------------
    Dim myPort As Array
    Delegate Sub SetTextCallback(ByVal [text] As String) 'Added to prevent threading                                  
    errors during receiveing of data
    '------------------------------------------------
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles    MyBase.Load

    myPort = IO.Ports.SerialPort.GetPortNames()
    ComboBox1.Items.AddRange(myPort)
    End Sub
    End Class

【问题讨论】:

  • 我需要做的是显示连接到 com 端口的内容......“——您似乎混淆了本地 COM 端口本身与连接的远程设备。对于“COM PORT1 FTDI USB 串行适配器",USB是将(本地)串口设备连接到主机系统的本地总线。FTDI恰好是USB串口适配器芯片的制造商。"串口1" 是(冗余措辞)仅用于 Windows 操作系统用于识别该设备的设备名称。
  • 您无法获得有关远程设备的任何信息,即实际上是“随附的“(或连接)通过串行链路连接到串行端口,除非 (a) 串行链路正常运行,并且 (b) 存在某种消息协议来请求和接收此类设备标识。
  • @sawdust 感谢您的建议,如您所知,我对此并不陌生。

标签: vb.net serial-port com-port


【解决方案1】:

下面显示了如何在 VS 2022 中为 .NET 6 和 .NET Framework 4.8 获取 VB.NET 中的 COM 设备列表。如果添加/删除 USB COM(串行端口)设备,将更新 ComboBox。

Windows 窗体应用程序: (.NET 6)

创建一个新项目Windows Forms App(名称:SerialPortGetComDevices)

下载/安装以下 NuGet 包:

  • System.IO.Ports
  • System.Management

Windows 窗体应用程序(.NET 框架)- v4.8:

创建一个新项目Windows Forms App (.NET Framework)(名称:SerialPortGetComDevices)

添加参考:

  • 在 VS 菜单中,单击项目
  • 选择添加参考...
  • 选择组件
  • 检查系统管理
  • 点击好的

以下说明对于Windows Forms AppWindows Forms App (.NET Framework) 都是相同的。

创建班级(名称:ComPortInfo.vb)

Public Class ComPortInfo
    Public Property Caption As String
    Public Property PortName As String
End Class

打开解决方案资源管理器:

  • 在 VS 菜单中,单击看法
  • 选择解决方案资源管理器

打开属性窗口:

  • 在 VS 菜单中,单击看法
  • 选择属性窗口

添加Load事件处理器

  • 在解决方案资源管理器中,右键单击 Form1.vb 并选择 View Designer
  • 在属性窗口中,单击
  • 双击加载

添加FormClosing事件处理器

  • 在解决方案资源管理器中,右键单击 Form1.vb 并选择 View Designer
  • 在属性窗口中,单击
  • 双击关闭表格

向窗体添加组合框(名称:ComboBoxComPorts)

  • 在 VS 菜单中,单击看法
  • 选择工具箱
  • 在工具箱中,选择组合框, 并将其拖到窗体中。
  • 在“属性”窗口中,将(Name) 更改为ComboBoxComPorts
  • 在“属性”窗口中,将DropDownStyle 更改为DropDownList

选择以下选项之一。第一个选项使用 ManagementEventWatcher 来检测 USB 设备的插入和移除。第二个选项覆盖WndProc

笔记: WndProc版本(选项2)的性能似乎稍好一些。


选项1(管理事件观察者)

笔记: 检测USB设备插拔的代码,改编自here

Form1.vb

Imports System.ComponentModel
Imports System.Management
Imports System.IO.Ports

Public Class Form1

    'create new instance
    Private _comPorts As BindingList(Of ComPortInfo) = New BindingList(Of ComPortInfo)
    Private _managementEventWatcher1 As ManagementEventWatcher = New ManagementEventWatcher()

    Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        InitializeManagementEventWatcher()
        UpdateCOM()

        'set properties
        ComboBoxComPorts.DataSource = _comPorts
        ComboBoxComPorts.DisplayMember = "Caption"
        ComboBoxComPorts.ValueMember = "PortName"
    End Sub

    Private Sub GetComPorts()
        'this method Is only called from 'UpdateCOM'
        '_comPorts' is only modified in this method

        Dim portDict As Dictionary(Of String, String) = New Dictionary(Of String, String)

        'clear existing data
        _comPorts.Clear()

        'get port names
        For Each pName As String In SerialPort.GetPortNames()
            If Not portDict.ContainsKey(pName) Then
                portDict.Add(pName, pName) 'add to Dictionary
            End If
        Next

        'get USB COM ports - this may result in a more descriptive name than 'COM1' 
        Using searcherPnPEntity As ManagementObjectSearcher = New ManagementObjectSearcher("SELECT Name FROM Win32_PnPEntity WHERE PNPClass = 'Ports'")
            For Each objPnPEntity As ManagementObject In searcherPnPEntity.Get()
                If objPnPEntity Is Nothing Then
                    Continue For
                End If

                'get name
                Dim name As String = objPnPEntity("Name")?.ToString()

                If Not String.IsNullOrEmpty(name) AndAlso name.ToUpper().Contains("COM") Then
                    Dim portName As String = name.Substring(name.IndexOf("(") + 1, name.IndexOf(")") - name.IndexOf("(") - 1)

                    If Not portDict.ContainsKey(portName) Then
                        portDict.Add(portName, name) 'add to Dictionary
                    Else
                        portDict(portName) = name 'update value
                    End If
                End If
            Next
        End Using

        'add items from Dictionary to BindingList
        For Each kvp As KeyValuePair(Of String, String) In portDict
            _comPorts.Add(New ComPortInfo() With {.Caption = kvp.Value, .PortName = kvp.Key}) 'add
        Next
    End Sub

    Private Sub InitializeManagementEventWatcher()
        'see https:'learn.microsoft.com/en-us/windows/win32/wmisdk/within-clause
        'WITHIN sets the polling interval in seconds
        'polling too frequently may reduce performance
        Dim query As WqlEventQuery = New WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_PnPEntity'")
        'Dim query As WqlEventQuery = New WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PnPEntity'")

        'set property
        _managementEventWatcher1.Query = query

        'subscribe to event
        AddHandler _managementEventWatcher1.EventArrived, AddressOf ManagementEventWatcher_EventArrived

        'start
        _managementEventWatcher1.Start()
    End Sub

    Private Sub LogMsg(msg As String, Optional includeTimestamp As Boolean = True)
        If includeTimestamp Then
            msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff")} - {msg}"
        End If

        Debug.WriteLine(msg)
    End Sub

    Public Sub UpdateCOM()
        If ComboBoxComPorts.InvokeRequired Then
            'LogMsg("ComboBoxComPorts.InvokeRequired")
            ComboBoxComPorts.Invoke(New MethodInvoker(Sub()
                                                          GetComPorts()
                                                      End Sub))
        Else
            GetComPorts()
        End If
    End Sub

    Public Sub ManagementEventWatcher_EventArrived(sender As Object, e As EventArrivedEventArgs)
        Dim obj As ManagementBaseObject = DirectCast(e.NewEvent, ManagementBaseObject)
        Dim target As ManagementBaseObject = If(obj("TargetInstance") IsNot Nothing, DirectCast(obj("TargetInstance"), ManagementBaseObject), Nothing)

        Dim usbEventType As String = String.Empty

        Select Case target.ClassPath.ClassName
            Case "__InstanceCreationEvent"
                usbEventType = "added"
            Case "__InstanceDeletionEvent"
                usbEventType = "removed"
            Case Else
                usbEventType = target.ClassPath.ClassName
        End Select

        If target("PNPClass") IsNot Nothing AndAlso target("PNPClass").ToString() = "Ports" Then
            'update COM ports
            UpdateCOM()
        End If
    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        'stop
        _managementEventWatcher1.Stop()

        'unsubscribe from event
        RemoveHandler _managementEventWatcher1.EventArrived, AddressOf ManagementEventWatcher_EventArrived
    End Sub
End Class

选项 2(覆盖 WndProc)

笔记: 检测USB设备插拔的代码,改编自here

添加模块(名称:UsbDeviceNotification.vb)

Imports System.Runtime.InteropServices

Module UsbDeviceNotification
    Public Const DbtDeviceArrival As Integer = &H8000 'device added
    Public Const DbtDeviceRemoveComplete As Integer = &H8004 'device removed
    Public Const WM_DEVICECHANGE As Integer = &H219 'device change event
    Public Const DBT_DEVTYP_DEVICEINTERFACE As Integer = 5

    Private ReadOnly _guidDevInterfaceUSBDevice As Guid = New Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED") 'USB devices
    Private _notificationHandle As IntPtr

    Declare Auto Function RegisterDeviceNotification Lib "user32" (recipient As IntPtr, notificationFilter As IntPtr, flags As Integer) As IntPtr
    Declare Auto Function UnregisterDeviceNotification Lib "user32" (hwnd As IntPtr) As Boolean

    <StructLayout(LayoutKind.Sequential)>
    Private Structure DEV_BROADCAST_DEVICEINTERFACE
        Dim Size As Integer
        Dim DeviceType As Integer
        Dim Reserved As Integer
        Dim ClassGuid As Guid
        Dim Name As Short
    End Structure

    Public Sub RegisterUsbDeviceNotification(hwnd As IntPtr)
        'Registers a window to receive notifications when USB devices are plugged or unplugged.

        Dim dbi As DEV_BROADCAST_DEVICEINTERFACE = New DEV_BROADCAST_DEVICEINTERFACE() With
            {
                .DeviceType = DBT_DEVTYP_DEVICEINTERFACE,
                .ClassGuid = _guidDevInterfaceUSBDevice
            }

        dbi.Size = Marshal.SizeOf(dbi)
        Dim buffer As IntPtr = Marshal.AllocHGlobal(dbi.Size)
        Marshal.StructureToPtr(dbi, buffer, True)

        _notificationHandle = RegisterDeviceNotification(hwnd, buffer, 0)
    End Sub

    Public Sub UnregisterUsbDeviceNotification()
        UnregisterDeviceNotification(_notificationHandle)
    End Sub
End Module

Form1.vb

Imports System.ComponentModel
Imports System.Management
Imports System.IO.Ports
Imports System.Threading

Public Class Form1

    'create new instance
    Private _comPorts As BindingList(Of ComPortInfo) = New BindingList(Of ComPortInfo)

    Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        UpdateCOM()

        'set properties
        ComboBoxComPorts.DataSource = _comPorts
        ComboBoxComPorts.DisplayMember = "Caption"
        ComboBoxComPorts.ValueMember = "PortName"
    End Sub

    Private Sub GetComPorts()
        'this method Is only called from 'UpdateCOM'
        '_comPorts' is only modified in this method

        Dim portDict As Dictionary(Of String, String) = New Dictionary(Of String, String)

        'clear existing data
        _comPorts.Clear()

        'get port names
        For Each pName As String In SerialPort.GetPortNames()
            If Not portDict.ContainsKey(pName) Then
                portDict.Add(pName, pName) 'add to Dictionary
            End If
        Next

        'get USB COM ports - this may result in a more descriptive name than 'COM1' 
        Using searcherPnPEntity As ManagementObjectSearcher = New ManagementObjectSearcher("SELECT Name FROM Win32_PnPEntity WHERE PNPClass = 'Ports'")

            If searcherPnPEntity IsNot Nothing Then
                For Each objPnPEntity As ManagementBaseObject In searcherPnPEntity.Get()
                    If objPnPEntity Is Nothing Then
                        Continue For
                    End If

                    'get name
                    Dim name As String = objPnPEntity("Name")?.ToString()

                    If Not String.IsNullOrEmpty(name) AndAlso name.ToUpper().Contains("COM") Then
                        Dim portName As String = name.Substring(name.IndexOf("(") + 1, name.IndexOf(")") - name.IndexOf("(") - 1)

                        If Not portDict.ContainsKey(portName) Then
                            portDict.Add(portName, name) 'add to Dictionary
                        Else
                            portDict(portName) = name 'update value
                        End If
                    End If
                Next
            End If
        End Using

        'add items from Dictionary to BindingList
        For Each kvp As KeyValuePair(Of String, String) In portDict
            _comPorts.Add(New ComPortInfo() With {.Caption = kvp.Value, .PortName = kvp.Key}) 'add
        Next
    End Sub

    Private Sub LogMsg(msg As String, Optional includeTimestamp As Boolean = True)
        If includeTimestamp Then
            msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.fff")} - {msg}"
        End If

        Debug.WriteLine(msg)
    End Sub

    Public Sub UpdateCOM()
        'since this method/Sub is called from WndProc, 
        'it needs to run on a new thread
        Dim threadProc As System.Threading.Thread = New System.Threading.Thread(Sub()
                                                                                    If ComboBoxComPorts.InvokeRequired Then
                                                                                        'LogMsg("ComboBoxComPorts.InvokeRequired")
                                                                                        ComboBoxComPorts.Invoke(New MethodInvoker(Sub()
                                                                                                                                      GetComPorts()
                                                                                                                                  End Sub))
                                                                                    Else
                                                                                        GetComPorts()
                                                                                    End If
                                                                                End Sub)

        threadProc.SetApartmentState(System.Threading.ApartmentState.STA)
        threadProc.Start()
    End Sub

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        If m.Msg = UsbDeviceNotification.WM_DEVICECHANGE Then
            Select Case CInt(m.WParam)
                Case UsbDeviceNotification.DbtDeviceRemoveComplete
                    UpdateCOM()

                Case UsbDeviceNotification.DbtDeviceArrival
                    UpdateCOM()
            End Select
        End If

        MyBase.WndProc(m)

    End Sub

    Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing

    End Sub
End Class

以下 PowerShell 命令也可能提供有用的信息。

电源外壳:

  • Get-CimInstance -Namespace RootCimv2 -Query "Select * From Win32_SerialPort Where Name like '%COM%'"
  • Get-CimInstance -Namespace RootCimv2 -Query "Select * From Win32_SerialPortConfiguration"
  • Get-CimInstance -Namespace RootCimv2 -Query "Select * From Win32_PnPEntity where PnPClass = 'Ports' and Name like '%COM%'"
  • mode

资源:

【讨论】:

  • 我无法让你的解决方案工作,我确信它工作正常,但我收到错误,现在已经晚了所以会在圣诞节后重试,不过感谢你的帮助
  • 打开 PowerShell,然后运行命令:Get-CimInstance -Namespace RootCimv2 -Query "Select * From Win32_SerialPort Where Name like '%COM%'"。如果设备没有出现,则它不是 COM(串行端口)设备。
猜你喜欢
  • 2021-12-27
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 2014-06-28
相关资源
最近更新 更多