Managing Bluetooth Devices (Part 3)

by May 6, 2022

If you’d like to programmatically unpair a paired Bluetooth device, then there is no built-in cmdlet available. Still PowerShell can do the trick, and it often can even unpair Bluetooth devices that won’t remove via the UI or keeps coming back.

What you need first to remove a Bluetooth device is its hardware address. Here is an example on how to list all Bluetooth devices and return their hardware address:

$Address =     @{
    Name='Address'
Expression={$_.HardwareID | 
ForEach-Object { [uInt64]('0x' + $_.Substring(12))}}
}

Get-PnpDevice -Class Bluetooth |
    Where-Object HardwareID -match 'DEV_' |
    Select-Object FriendlyName, $Address |
    Where-Object Address |
    Out-GridView -Title 'Select Bluetooth Device to Remove' -OutputMode Single

The result looks similar to this and displays in a grid view window where you can select one Bluetooth device:

 
FriendlyName                               Address
------------                               -------
Bamboo Ink Plus                       480816531482
SMA001d SN: 2110109033 SN2110109033   550378395892
MX Master 3                            20919489792
MX Keys                              1089715743697
Bose QC35 II                        44056255752152   
 

On a side note, the code illustrates an easy trick to convert hex numbers programmatically to decimals:

 
PS> $hex = 'A0FD'

PS> [int]"0x$hex"
41213
 

Once you know the hardware address of the Bluetooth device you want to unpair, next you must submit it to an internal Windows API call. The internal method BluetoothRemoveDevice removes it. The code below was inspired by advice given by Keith A. Miller in a Microsoft forum.

Here is a function that wraps the signature of the internal Windows API and takes a hardware address, then unpairs the device:

function Unpair-Bluetooth
{
    # take a UInt64 either directly or as part of an object with a property
    # named "DeviceAddress" or "Address"
    param
    (
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Alias('Address')]
        [UInt64]
        $DeviceAddress
    )

    # tell PowerShell the location of the internal Windows API
    # and define a static helper function named "Unpair" that takes care
    # of creating the needed arguments:
    begin
    {
        Add-Type -Namespace "Devices" -Name 'Bluetooth' -MemberDefinition '
[DllImport("BluetoothAPIs.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U4)]
static extern UInt32 BluetoothRemoveDevice(IntPtr pAddress);
public static UInt32 Unpair(UInt64 BTAddress) {
    GCHandle pinnedAddr = GCHandle.Alloc(BTAddress, GCHandleType.Pinned);
    IntPtr pAddress     = pinnedAddr.AddrOfPinnedObject();
    UInt32 result       = BluetoothRemoveDevice(pAddress);
    pinnedAddr.Free();
    return result;
}'
    }

    # do this for every object that was piped into this function:
    process
    {
        $result = [Devices.Bluetooth]::Unpair($DeviceAddress)
        [PSCustomObject]@{
            Success = $result -eq 0
            ReturnValue = $result
        }
    }
}

Since the new function Unpair-Bluetooth is pipeline-aware, you can simply append it to your previous code to create a Bluetooth-Unpairer:

$Address =     @{
    Name='Address'
Expression={$_.HardwareID | 
ForEach-Object { [uInt64]('0x' + $_.Substring(12))}}
}

Get-PnpDevice -Class Bluetooth |
    Where-Object HardwareID -match 'DEV_' |
    Select-Object FriendlyName, $Address |
    Where-Object Address |
    Out-GridView -Title 'Select Bluetooth Device to Unpair' -OutputMode Single |
    Unpair-Bluetooth

When you run the code, it again shows all Bluetooth devices. Select the one to unpair, then click OK in the bottom right corner of the grid view Window. Note that unpairing fails if the device is not a “remembered device”. When the unpairing succeeds, the return value is 0, and the device is removed from the list of Bluetooth devices.


Twitter This Tip! ReTweet this Tip!