获取蓝牙COM端口 [英] Get Bluetooth COM Ports

查看:145
本文介绍了获取蓝牙COM端口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试访问有关蓝牙串行端口的特定信息.我可以在蓝牙设置中找到此窗口,该窗口向我显示了与COM端口相关的蓝牙设备的端口,方向和名称.

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.

当前,为了获取此信息,我一直在使用WQL查询某些Windows管理类.

Currently to try and get this information, I have been using WQL to query some of the Windows Management Classes.

# 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获得此信息?

However, the queries below do not include the name (as it is in the screenshot) and the direction of the COM Ports. Is it possible to get this information using WQL?

推荐答案

虽然我不确定是否可以使用WQL做到100%,但是我能够用C#编写一个处理该问题的小程序.

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.

通过在某些Win32_PnPEntity属性中查找模式来工作.

It works by looking for patterns in some of the Win32_PnPEntity properties.

它从所有存储的蓝牙设备中提取一个标识符,并将该标识符与COM端口上的相同标识符进行比较.如果找到它,则找到传出的COM端口.

It extracts an identifier out of all the stored bluetooth devices and compares that identifier with the same identifier on the COM ports. If it finds it, it has found the outgoing COM port.

传出端口和传入端口都具有相似的硬件ID,其中第一部分相同.然后,程序将使用此信息来识别传入的COM端口(如果存在).

Both the outgoing and incoming port have a similar hardware ID, where the first part is identical. Using this information, the program then identifies incoming COM port if it exists.

要以问题图像中所示的格式获取它,可以运行一个powershell命令来查询传出端口的DEVPKEY_Device_BusReportedDeviceDesc.这是传出端口上名称的'Dev B'部分.

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. This is the 'Dev B' part of the name on the outgoing port.

所有这些操作都是异步完成的,因此在找到结果时会填充列表.

This all done asynchronously so the list is populated as the results are found.

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

这篇关于获取蓝牙COM端口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆