获取蓝牙COM端口

4
我正在尝试访问有关蓝牙串行端口的特定信息。我能够在我的蓝牙设置中找到这个窗口,如果它与COM端口相关,则会显示蓝牙设备的端口、方向和名称。

enter image description here

目前我尝试使用WQL查询一些Windows管理类来获取这些信息。

# I don't really mind if it is run in a Powershell environment
gwmi -query "SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%COM%' AND PNPDeviceID LIKE '%BTHENUM%' AND PNPClass = 'Ports'"

//or a C# environment
ManagementObjectCollection results = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%COM%' AND PNPDeviceID LIKE 'USB%' AND PNPClass = 'Ports'").Get();

#This is a lot slower but it gets a bit more information about the serial ports
gwmi -query "SELECT * FROM Win32_SerialPort WHERE Name LIKE '%COM%' AND PNPDeviceID LIKE '%BTHENUM%'"

然而,下面的查询并不包括端口名称(如屏幕截图中所示)和COM端口的方向。是否可能使用WQL获取此信息?
2个回答

5

虽然我不确定是否可以完全使用WQL完成,但我能够编写一个小的C#程序来处理它。

它通过查找一些Win32_PnPEntity属性中的模式来工作。

它提取所有存储的蓝牙设备的标识符,并将该标识符与COM端口上的相同标识符进行比较。如果找到它,就找到了输出的COM端口。

出站端口和入站端口都具有类似的硬件ID,其中第一部分是相同的。使用这些信息,程序识别存在的入站COM端口。

要以问题中显示的图像格式获取它,可以运行查询出站端口的DEVPKEY_Device_BusReportedDeviceDesc的powershell命令。这是出站端口名称中'Dev B'的部分。

这一切都是异步完成的,因此在找到结果时会填充列表。

enter image description here


XAML

<Window x:Class="BluetoothInformation.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:BluetoothInformation"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:DirectionConverter x:Key="directionConverter"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <DataGrid ItemsSource="{Binding COMPorts}"
                  CanUserAddRows="False"
                  ColumnWidth="*"
                  AutoGenerateColumns="False"
                  VerticalScrollBarVisibility="Visible"
                  Background="Transparent"
                  RowBackground="Transparent"
                  IsReadOnly="True">
            <DataGrid.CellStyle>
                <Style TargetType="{x:Type DataGridCell}">
                    <Setter Property="Focusable"
                            Value="False"/>
                    <Style.Triggers>
                        <Trigger Property="IsSelected"
                                 Value="True">
                            <Setter Property="Background"
                                    Value="Transparent" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.CellStyle>
            <DataGrid.Columns>
                <DataGridTextColumn Header="COM Port"
                                    Binding="{Binding COMPortPort}"/>
                <DataGridTextColumn Header="Direction"
                                    Binding="{Binding COMPortDirection, Converter={StaticResource directionConverter}}"/>
                <DataGridTextColumn Header="Name"
                                    Binding="{Binding COMPortName}"/>
            </DataGrid.Columns>
        </DataGrid>
        <Button Grid.Row="1"
                Content="Refresh"
                IsEnabled="{Binding CanRefresh}"
                Click="Button_Click"/>
    </Grid>
</Window>

C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Management;
using System.Management.Automation;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace BluetoothInformation
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private ObservableCollection<COMPort> comPorts = new ObservableCollection<COMPort>();
        public ObservableCollection<COMPort> COMPorts {
            get => comPorts;
            set {
                comPorts = value;
                RaisePropertyChanged();
            }
        }

        private bool canRefresh = false;
        public bool CanRefresh {
            get => canRefresh;
            set {
                canRefresh = value;
                RaisePropertyChanged();
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            GetBluetoothCOMPort();
        }

        private string ExtractBluetoothDevice(string pnpDeviceID)
        {
            int startPos = pnpDeviceID.LastIndexOf('_') + 1;
            return pnpDeviceID.Substring(startPos);
        }

        private string ExtractDevice(string pnpDeviceID)
        {
            int startPos = pnpDeviceID.LastIndexOf('&') + 1;
            int length = pnpDeviceID.LastIndexOf('_') - startPos;
            return pnpDeviceID.Substring(startPos, length);
        }

        private string ExtractCOMPortFromName(string name)
        {
            int openBracket = name.IndexOf('(');
            int closeBracket = name.IndexOf(')');
            return name.Substring(openBracket + 1, closeBracket - openBracket - 1);
        }

        private string ExtractHardwareID(string fullHardwareID)
        {
            int length = fullHardwareID.LastIndexOf('_');
            return fullHardwareID.Substring(0, length);
        }

        private bool TryFindPair(string pairsName, string hardwareID, List<ManagementObject> bluetoothCOMPorts, out COMPort comPort)
        {
            foreach (ManagementObject bluetoothCOMPort in bluetoothCOMPorts)
            {
                string itemHardwareID = ((string[])bluetoothCOMPort["HardwareID"])[0];
                if (hardwareID != itemHardwareID && ExtractHardwareID(hardwareID) == ExtractHardwareID(itemHardwareID))
                {
                    comPort = new COMPort(ExtractCOMPortFromName(bluetoothCOMPort["Name"].ToString()), Direction.INCOMING, pairsName);
                    return true;
                }
            }
            comPort = null;
            return false;
        }

        private string GetDataBusName(string pnpDeviceID)
        {
            using (PowerShell PowerShellInstance = PowerShell.Create())
            {
                PowerShellInstance.AddScript($@"Get-PnpDeviceProperty -InstanceId '{pnpDeviceID}' -KeyName 'DEVPKEY_Device_BusReportedDeviceDesc' | select-object Data");

                Collection<PSObject> PSOutput = PowerShellInstance.Invoke();

                foreach (PSObject outputItem in PSOutput)
                {
                    if (outputItem != null)
                    {
                        Console.WriteLine(outputItem.BaseObject.GetType().FullName);
                        foreach (var p in outputItem.Properties)
                        {
                            if (p.Name == "Data")
                            {
                                return p.Value?.ToString();
                            }
                        }
                    }
                }
            }
            return string.Empty;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            GetBluetoothCOMPort();
        }

        private async void GetBluetoothCOMPort()
        {
            CanRefresh = false;
            COMPorts.Clear();

            await Task.Run(() => {
                ManagementObjectCollection results = new ManagementObjectSearcher(@"SELECT PNPClass, PNPDeviceID, Name, HardwareID FROM Win32_PnPEntity WHERE (Name LIKE '%COM%' AND PNPDeviceID LIKE '%BTHENUM%' AND PNPClass = 'Ports') OR (PNPClass = 'Bluetooth' AND PNPDeviceID LIKE '%BTHENUM\\DEV%')").Get();

                List<ManagementObject> bluetoothCOMPorts = new List<ManagementObject>();
                List<ManagementObject> bluetoothDevices = new List<ManagementObject>();

                foreach (ManagementObject queryObj in results)
                {
                    if (queryObj["PNPClass"].ToString() == "Bluetooth")
                    {
                        bluetoothDevices.Add(queryObj);
                    }
                    else if (queryObj["PNPClass"].ToString() == "Ports")
                    {
                        bluetoothCOMPorts.Add(queryObj);
                    }
                }

                foreach (ManagementObject bluetoothDevice in bluetoothDevices)
                {
                    foreach (ManagementObject bluetoothCOMPort in bluetoothCOMPorts)
                    {
                        string comPortPNPDeviceID = bluetoothCOMPort["PNPDeviceID"].ToString();
                        if (ExtractBluetoothDevice(bluetoothDevice["PNPDeviceID"].ToString()) == ExtractDevice(comPortPNPDeviceID))
                        {
                            COMPort outgoingPort = new COMPort(ExtractCOMPortFromName(bluetoothCOMPort["Name"].ToString()), Direction.OUTGOING, $"{bluetoothDevice["Name"].ToString()} \'{GetDataBusName(comPortPNPDeviceID)}\'");

                            Dispatcher.Invoke(() => {
                                COMPorts.Add(outgoingPort);
                            });

                            if (TryFindPair(bluetoothDevice["Name"].ToString(), ((string[])bluetoothCOMPort["HardwareID"])[0], bluetoothCOMPorts, out COMPort incomingPort))
                            {
                                Dispatcher.Invoke(() => {
                                    COMPorts.Add(incomingPort);
                                });
                            }
                        }
                    }
                }
            });

            CanRefresh = true;
        }
    }

    public class COMPort : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private string comPortPort;
        public string COMPortPort {
            get => comPortPort;
            set {
                comPortPort = value;
                RaisePropertyChanged();
            }
        }

        private Direction comPortDirection;
        public Direction COMPortDirection {
            get => comPortDirection;
            set {
                comPortDirection = value;
                RaisePropertyChanged();
            }
        }

        private string comPortName;
        public string COMPortName {
            get => comPortName;
            set {
                comPortName = value;
                RaisePropertyChanged();
            }
        }

        public COMPort(string comPortPort, Direction comPortDirection, string comPortName)
        {
            COMPortPort = comPortPort;
            COMPortDirection = comPortDirection;
            COMPortName = comPortName;
        }
    }

    [ValueConversion(typeof(Direction), typeof(string))]
    public class DirectionConverter : IValueConverter
    {
        private const string UNDEFINED_DIRECTION = "UNDEFINED";
        private const string INCOMING_DIRECTION = "Incoming";
        private const string OUTGOING_DIRECTION = "Outgoing";

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            switch ((Direction)value)
            {
                case Direction.UNDEFINED:
                    return UNDEFINED_DIRECTION;
                case Direction.INCOMING:
                    return INCOMING_DIRECTION;
                case Direction.OUTGOING:
                    return OUTGOING_DIRECTION;
            }

            return string.Empty;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public enum Direction
    {
        UNDEFINED,
        INCOMING,
        OUTGOING
    }
}

1

我试图在我的代码中使用Visual Studio C#代码列出Arduino蓝牙HC-05设备

在找到上面的答案后,我找到了一种适合我的方法 使用WMI(不确定其可靠性) 希望能帮助其他人

// First get all the Ports (this is way quicker then using WMI) 
string[] aryPorts = SerialPort.GetPortNames();
foreach (string strPort in aryPorts) { 
    
    // Find COM ports using WMI
    System.Management.ManagementObjectSearcher searcher; 
    searcher = new System.Management.ManagementObjectSearcher($"SELECT * FROM Win32_PnPEntity WHERE Caption like '%({strPort})%'");

    foreach (System.Management.ManagementObject myComPorts in searcher.Get())
    {
        string strCOMCaption = myComPorts.Properties["Caption"].Value.ToString();
        string strCOMDeviceID = myComPorts.Properties["DeviceID"].Value.ToString();
        string strCOMPNPDeviceID = myComPorts.Properties["PNPDeviceID"].Value.ToString();

        // Find Bluetooth devices
        searcher = new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE PNPClass = 'Bluetooth' AND DeviceID like '%BTHENUM%'");
        foreach (System.Management.ManagementObject myBluetooth in searcher.Get())
        {
            string stBTCaption = myBluetooth.Properties["Caption"].Value.ToString();
            string strBTPNPDeviceID = myBluetooth.Properties["PNPDeviceID"].Value.ToString();

            // Match the Bluetooth Device ID part to part of the COM port ID
            int intWhere = strBTPNPDeviceID.IndexOf("BLUETOOTHDEVICE_");
            if (intWhere > 0)
            {
                string strBluettothIDPart = strBTPNPDeviceID.Substring(intWhere + "strBTPNPDeviceID".Length);

                if (strCOMPNPDeviceID.Contains("&" + strBluettothIDPart))
                {
                    string strBluettothFriendlyName = myBluetooth.Properties["Caption"].Value.ToString();

                    Debug.Print("COM Port: " + strPort +
                                ", Display Name: " + strBluettothFriendlyName +
                                ", BluetoothID: " + strBluettothIDPart +
                                ", COM Name: " + strCOMCaption + "");

                    break;
                }
            }
        }
    }
}

这打印出来

COM端口:COM5,显示名称:BuiltCleverChess,蓝牙ID:001401035465,COM名称:标准蓝牙串行连接(COM5)

COM端口:COM7,显示名称:Scotts Arduino Bluetooth,蓝牙ID:98D331100629,COM名称:标准蓝牙串行连接(COM7)

显示名称是屏幕截图中要求的那个

我稍微解释一下

第一个WMI调用搜索COM端口返回的设备ID如下:

BTHENUM{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0002\7&19E0CC7A&0&001401035465_C00000000

第二个WMI调用返回蓝牙设备,并且有一个PNPDeviceID如下:

BTHENUM\DEV_001401035465\7&3A1F5C64&2&BLUETOOTHDEVICE_001401035465

这个有显示名称,但没有提到COM端口

然后我遍历返回的蓝牙设备,匹配部分ID

这个突出显示的001401035465匹配,所以我猜这是某种蓝牙ID。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接