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. 原遍历代码修正要点
- 第一次调用
BluetoothSdpGetContainerElementData获取容器句柄和第一个子元素 - 循环调用
BluetoothSdpGetNextContainerElementData获取后续子元素,而非重复调用初始函数 - 遍历结束后必须调用
BluetoothSdpCloseContainerElement释放容器句柄,避免内存泄漏
内容的提问来源于stack exchange,提问作者A LaMark
相关产品推荐
相关产品推荐

