You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows蓝牙客户端解析Android设备SDP_TYPE_SEQUENCE的C++实现

Windows蓝牙SDP序列属性解析问题

核心疑问

  • 从Android设备获取的以下SDP_TYPE_SEQUENCE类型属性是否均为UUID(Windows对应GUID):
    • ServiceClassIds(0x1)
    • ProtocolDescriptorList(0x4)
    • BrowseGroupList(0x5)
    • BluetoothProfileDescriptorList(0x9)
    • AdditionalProtocolDescriptorList(0xD)
  • 如何在用户态C++中解析这些序列类型数据?
  • 尝试引入驱动层头文件<bthsdpddi.h>失败,求解析示例代码
  • 已尝试用BluetoothSdpGetContainerElementData遍历,需确认实现方式是否正确

现有代码片段

初始序列处理代码

case SDP_TYPE_SEQUENCE: {
    ULONG l_ULONGSequenceLength = SDP_ELEMENT_DATA_in.data.sequence.length + 1;
    BYTE* l_pBYTESequence = new BYTE[l_ULONGSequenceLength];
    ZeroMemory(l_pBYTESequence, l_ULONGSequenceLength);
    memcpy(l_pBYTESequence,
           SDP_ELEMENT_DATA_in.data.sequence.value,
           SDP_ELEMENT_DATA_in.data.sequence.length);

    // TODO: handle the memory

    delete[] l_pBYTESequence;
    break;
}

遍历尝试代码

case SDP_TYPE_SEQUENCE: {
    HBLUETOOTH_CONTAINER_ELEMENT l_HBLUETOOTH_CONTAINER_ELEMENT = NULL;
    SDP_ELEMENT_DATA l_SDP_ELEMENT_DATA;
    DWORD l_DWORD =
        BluetoothSdpGetContainerElementData(
            SDP_ELEMENT_DATA_in.data.sequence.value,
            SDP_ELEMENT_DATA_in.data.sequence.length,
            &l_HBLUETOOTH_CONTAINER_ELEMENT,
            &l_SDP_ELEMENT_DATA);
    while (l_DWORD == ERROR_SUCCESS)
    {
        // recursive method call...
        const _variant_t l_variant_t =
            ConvertAttributeToVariant(
                ULONGAttributeId_in,
                pTCHARAttributeName_in,
                l_SDP_ELEMENT_DATA);

        l_DWORD =
            BluetoothSdpGetContainerElementData(
                SDP_ELEMENT_DATA_in.data.sequence.value,
                SDP_ELEMENT_DATA_in.data.sequence.length,
                &l_HBLUETOOTH_CONTAINER_ELEMENT,
                &l_SDP_ELEMENT_DATA);
    }
    break;
}

解答

1. 属性是否均为UUID?

不全是,具体区分:

  • ServiceClassIds/BrowseGroupList:是,序列内每个元素都是UUID(服务类/浏览组标识)
  • ProtocolDescriptorList/AdditionalProtocolDescriptorList:不是,序列内是协议描述符子序列,每个子序列包含协议UUID + 协议参数(如L2CAP的PSM值、RFCOMM的通道号)
  • BluetoothProfileDescriptorList:不是,序列内是Profile描述符子序列,包含Profile UUID + Profile版本号

2. 用户态解析方案(无需驱动头文件)

用户态只能用Win32公开蓝牙API完成解析,<bthsdpddi.h>是驱动层专用头文件,用户态无法引入。正确的序列解析需要用BluetoothSdpGetContainerElementData配合BluetoothSdpGetNextContainerElementData递归遍历,原遍历代码的问题是每次都从起始地址读取,会陷入死循环。

正确递归解析示例代码

#include <bluetoothapis.h>
#include <initguid.h>
#include <guiddef.h>
#include <wchar.h>

// 递归解析单个SDP元素
void ParseSdpElement(const SDP_ELEMENT_DATA& element)
{
    switch (element.type)
    {
        case SDP_TYPE_UUID16:
        {
            GUID uuid;
            BluetoothConvertShortUuidToGuid(element.data.uuid16, &uuid);
            WCHAR guidStr[40];
            StringFromGUID2(uuid, guidStr, ARRAYSIZE(guidStr));
            wprintf(L"  UUID16转换后: %s\n", guidStr);
            break;
        }
        case SDP_TYPE_UUID32:
        {
            GUID uuid;
            BluetoothConvertLongUuidToGuid(element.data.uuid32, &uuid);
            WCHAR guidStr[40];
            StringFromGUID2(uuid, guidStr, ARRAYSIZE(guidStr));
            wprintf(L"  UUID32转换后: %s\n", guidStr);
            break;
        }
        case SDP_TYPE_UUID128:
        {
            WCHAR guidStr[40];
            StringFromGUID2(element.data.uuid128, guidStr, ARRAYSIZE(guidStr));
            wprintf(L"  UUID128: %s\n", guidStr);
            break;
        }
        case SDP_TYPE_UINT:
            wprintf(L"  UINT值: %lu\n", element.data.uint32);
            break;
        case SDP_TYPE_SEQUENCE:
        case SDP_TYPE_ALTERNATIVE:
        {
            HBLUETOOTH_CONTAINER_ELEMENT hContainer = NULL;
            SDP_ELEMENT_DATA subElement;
            DWORD result = BluetoothSdpGetContainerElementData(
                element.data.sequence.value,
                element.data.sequence.length,
                &hContainer,
                &subElement);

            while (result == ERROR_SUCCESS)
            {
                ParseSdpElement(subElement);
                // 推进到下一个子元素
                result = BluetoothSdpGetNextContainerElementData(hContainer, &subElement);
            }
            // 释放容器句柄
            if (hContainer != NULL)
            {
                BluetoothSdpCloseContainerElement(hContainer);
            }
            break;
        }
        default:
            wprintf(L"  不支持的元素类型: 0x%X\n", element.type);
            break;
    }
}

// SDP属性枚举回调函数
BOOL CALLBACK SdpAttributeCallback(
    ULONG attributeId,
    LPCTSTR attributeName,
    PSDP_ELEMENT_DATA pElementData,
    LPVOID context)
{
    switch (attributeId)
    {
        case SDP_ATTRIBUTE_ID_SERVICE_CLASS_ID_LIST:
        case SDP_ATTRIBUTE_ID_PROTOCOL_DESCRIPTOR_LIST:
        case SDP_ATTRIBUTE_ID_BROWSE_GROUP_LIST:
        case SDP_ATTRIBUTE_ID_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
        case SDP_ATTRIBUTE_ID_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST:
            if (pElementData->type == SDP_TYPE_SEQUENCE)
            {
                wprintf(L"\n解析属性 0x%X (%s):\n", attributeId, attributeName);
                ParseSdpElement(*pElementData);
            }
            break;
    }
    return TRUE;
}

3. 原遍历代码修正要点

  1. 第一次调用BluetoothSdpGetContainerElementData获取容器句柄和第一个子元素
  2. 循环调用BluetoothSdpGetNextContainerElementData获取后续子元素,而非重复调用初始函数
  3. 遍历结束后必须调用BluetoothSdpCloseContainerElement释放容器句柄,避免内存泄漏

内容的提问来源于stack exchange,提问作者A LaMark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:54:48