简体   繁体   English

获取蓝牙COM端口

[英]Get Bluetooth COM Ports

I am trying to access specific information about Bluetooth serial ports. 我正在尝试访问有关蓝牙串行端口的特定信息。 I was able to find this window in my Bluetooth settings that showed me the port, direction, and name of a Bluetooth device if it was related to a COM Port. 我可以在蓝牙设置中找到此窗口,该窗口向我显示了与COM端口相关的蓝牙设备的端口,方向和名称。

在此处输入图片说明

Currently to try and get this information, I have been using WQL to query some of the Windows Management Classes. 当前,为了尝试获取此信息,我一直在使用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%'"

However, the queries below do not include the name (as it is in the screenshot) and the direction of the COM Ports. 但是,下面的查询不包括名称(如屏幕快照中所示)和COM端口的方向。 Is it possible to get this information using WQL? 是否可以使用WQL获得此信息?

Whilst I'm not sure if this can be done 100% with WQL, I was able to write a small program in C# that handled it. 虽然我不确定是否可以使用WQL做到100%,但是我能够用C#编写一个处理该问题的小程序。

It works by looking for patterns in some of the Win32_PnPEntity properties. 它通过在某些Win32_PnPEntity属性中查找模式来工作。

It extracts an identifier out of all the stored bluetooth devices and compares that identifier with the same identifier on the COM ports. 它从所有存储的蓝牙设备中提取一个标识符,并将该标识符与COM端口上的相同标识符进行比较。 If it finds it, it has found the outgoing COM port. 如果找到它,则找到传出的COM端口。

Both the outgoing and incoming port have a similar hardware ID, where the first part is identical. 传出端口和传入端口都具有相似的硬件ID,其中第一部分相同。 Using this information, the program then identifies incoming COM port if it exists. 然后,使用此信息,程序将识别传入的COM端口(如果存在)。

To get it in the format shown in the image in the question, it is possible to run a powershell command that queries the DEVPKEY_Device_BusReportedDeviceDesc of the outgoing port. 要以问题图像中所示的格式获取它,可以运行一个powershell命令来查询传出端口的DEVPKEY_Device_BusReportedDeviceDesc This is the 'Dev B' part of the name on the outgoing port. 这是传出端口上名称的'Dev B'部分。

This all done asynchronously so the list is populated as the results are found. 所有这些都是异步完成的,因此在找到结果时将填充列表。

在此处输入图片说明


XAML 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# 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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM