Searching SDP Attributes Using COM Interfaces
A version of this page is also available for
4/8/2010
Windows Embedded CE provides COM interfaces that you can use to parse SDP records as streams. The application can check the validity of the response and convert the raw binary SDP data to a record stream by using ISdpStream methods. The record is returned as an ISdpRecord array. The array contains attribute identifiers and NodeData structures that contain meta data associated with the attributes. To retrieve attribute information about a service use the ISdpNodeContainer methods.
The Bthnscreate sample that ships with Windows Embedded CE contains source code for creating and searching SDP records.
The following procedure converts binary service data into ISdpRecords and retrieves the RFCOMM channel identifier. For the complete code, see Retrieving the RFCOMM Channel Identifier.
To search SDP attributes by using COM interfaces
Retrieve raw SDP binary data in a BLOB (Windows Sockets) structure that you want to parse. For more information about discovering services of a Bluetooth device, see Querying Service Capability on Remote Bluetooth Devices.
To initialize the Component Object Model (COM) for use by the current thread, call CoInitializeEx, as the following example code shows.
CoInitializeEx(NULL,COINIT_MULTITHREADED);
To parse the buffer that contains the SDP data and return the service data in an ISdpRecord array, use ISdpStream methods. Retrieve the SDP records in a pointer of type ISdpRecord, as the following example code shows.
ISdpRecord **pRecordArg; ISdpRecord *pRecord = NULL; if (ERROR_SUCCESS != ServiceAndAttributeSearchParse(szBuf,sizeof(szBuf),&pRecordArg, &ulRecords)) { ...//Error handling code goes here. }
For information about SDP record parsing, and the implementation of the ServiceAndAttributeSearchParse function, see Parsing an SDP Record Using COM Interfaces.
To store information about a specific attribute, define a class that inherits the NodeData class.
class CNodeDataFreeString : public NodeData { public: CNodeDataFreeString() { type = SDP_TYPE_NIL; } ~CNodeDataFreeString() { if (type == SDP_TYPE_STRING) CoTaskMemFree(u.str.val); else if (type == SDP_TYPE_URL) CoTaskMemFree(u.url.val); } };
NodeData, is defined in the Bthapi.idl file, and stores information about Bluetooth service attributes.
To retrieve the information about the attribute of the service record, iterate through the SDP record array and call the ISdpRecord::GetAttribute method. Pass the attribute identifier and receive the NodeData structure in the pNode parameter. The following code example shows how to call the ISdpRecord::GetAttribute function to retrieve a NodeData structure that is associated with the attribute identifier SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST (4). The NodeData structure that is received contains information about the Bluetooth protocols settings for the service.
ULONG ulRecords = 0; ... for (ULONG i = 0; i < ulRecords; i++) { pRecord = pRecordArg[i]; // Record to be examined. CNodeDataFreeString protocolList; // Contains SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST data, if available. if (ERROR_SUCCESS != pRecord->GetAttribute( SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST, &protocolList) || (protocolList.type != SDP_TYPE_CONTAINER)) { continue; } ... //If GetAttribute is sucessful process attribute. }
Note
When the application calls ISdpRecord::GetAttribute, the result set is returned as a URL or a string that is allocated by the caller. To ensure that the string is automatically released, use the class created in step 4. This class can also be used with ISdpNodeContainer::GetNodeStringData.
In the preceding example, the returned NodeData structure, received in protocolList, may contain Bluetooth protocol information in multiple NodeData child elements. Each one of these elements represents a particular Bluetooth protocol, such as RFCOMM or L2CAP. These protocol elements are composed of NodeData child elements that contain information about the protocol such as the UUID and the channel identifier.
To retrieve information such as the channel for a protocol, from the NodeData structure retrieved in step 5, iterate through the NodeData structure by using ISdpNodeContainer methods such as:
- ISdpNodeContainer::GetNodeCount to get the number of child elements within the NodeData parent element.
- ISdpNodeContainer::GetNode to retrieve the contents of the NodeData structure.
The following code example determines the channel identifier for the RFCOMM protocol. To do this, the application iterates through the child elements of each protocol NodeData structure to find the RFCOMM UUID. As per the Bluetooth specification, the next item in this NodeData structure is an integer that contains the channel identifier. Once this integer value is found, searching is terminated and the channel identifier is returned to the caller.
ISdpNodeContainer *pRecordContainer = protocolList.u.container; DWORD cProtocols = 0; NodeData protocolDescriptor; // Information about a specific protocol (for example, L2CAP, RFCOMM). pRecordContainer->GetNodeCount(&cProtocols); for (j = 0; j < cProtocols; j++) { pRecordContainer->GetNode(j,&protocolDescriptor); if (protocolDescriptor.type != SDP_TYPE_CONTAINER) continue; ISdpNodeContainer *pProtocolContainer = protocolDescriptor.u.container; DWORD cProtocolAtoms; pProtocolContainer->GetNodeCount(&cProtocolAtoms); for (k = 0; k < cProtocolAtoms; k++) { NodeData nodeAtom; // Individual data element, such as what protocol this is or RFCOMM channel ID. pProtocolContainer->GetNode(k,&nodeAtom); if (IsRfcommUuid(&nodeAtom)) { if (k+1 == cProtocolAtoms) { break;// Misformatted response. } NodeData channelID; pProtocolContainer->GetNode(k+1,&channelID); //Call the helper function that returns the channel ID based on specificType. if (GetChannel(&channelID,&ulChannelId)) { fSuccess = TRUE; goto done; } break; // Formatting error. } } }
Perform memory cleanup operations by releasing the ISdpRecord record.
The following code example shows how to release the SDP record by using standard COM methods.
for (i = 0; i < ulRecords; i++) pRecordArg[i]->Release(); CoTaskMemFree(pRecordArg); CoUninitialize(); return 0;