< Querying Bluetooth Device Example | Winsock2 Supported Protocols Main | Winsock 2 & Bluetooth APIs >
What do we have in this chapter 4 part 15?
|
Publishing a Service
The other side of service discovery is service publication. Bluetooth applications that want to provide a service to other applications must do more than simply create a Bluetooth socket, bind the socket, and call accept as would an IrDA service. In addition to the socket work, the service must publish the details of the service through the SDP API. The actual publication of a service is actually quite simple. All that’s necessary is to call WSASetService(), which is prototyped as:
INT WSASetService (LPWSAQUERYSET lpqsRegInfo, WSAESETSERVICEOP essoperation, DWORD dwControlFlags);
The three parameters are a pointer to a WSAQUERYSET structure; a service operation flag, which needs to be set to RNRSERVICE_REGISTER; and a dwControlFlags parameter set to 0. If only registration were that simple. The problem isn’t calling the function; it’s composing the SDP data that’s placed in the WSAQUERYSET structure. The dwNameSpace field should be set to NS_BTH. And, as with the discovery process, the blobs are involved. The blob used in setting the service is a BTHNS_SETBLOB structure defined as:
typedef struct _BTHNS_SETBLOB { ULONG* pRecordHandle; ULONG fSecurity; ULONG fOptions; ULONG ulRecordLength; UCHAR pRecord[1]; } BTHNS_SETBLOB, *PBTHNS_SETBLOB;
The first parameter points to a ULONG that will receive a handle for the SDP record being created. The fSecurity and fOptions fields (available in Windows CE) are reserved and should be set to 0. The ulRecord-Length parameter should be set to the length of the SDP record to publish, whereas pRecord is the starting byte of the byte array that is the SDP record to publish. The following code demonstrates publishing an SDP record. The routine is passed an SDP record and its size. It then initializes the proper structures and calls WSASetService() to publish the record. |
int PublishRecord (HWND hWnd, PBYTE pSDPRec, int nRecSize, ULONG *pRecord)
{
BTHNS_SETBLOB *pSetBlob;
ULONG ulSdpVersion = BTH_SDP_VERSION;
int rc;
// Zero out the record handle that will be returned by the call
*pRecord = 0;
// Allocate and init the SetBlob
pSetBlob = (BTHNS_SETBLOB *)LocalAlloc (LPTR, sizeof (BTHNS_SETBLOB) + nRecSize);
if (!pSetBlob)
return -1;
pSetBlob->pRecordHandle = pRecord;
pSetBlob->pSdpVersion = &ulSdpVersion;
pSetBlob->fSecurity = 0;
pSetBlob->fOptions = 0;
pSetBlob->ulRecordLength = nRecSize;
memcpy (pSetBlob->pRecord, pSDPRec, nRecSize);
// Init the container blob
BLOB blob;
blob.cbSize = sizeof(BTHNS_SETBLOB) + SDP_RECORD_SIZE - 1;
blob.pBlobData = (PBYTE) pSetBlob;
// Init the WSAQuerySet struct
WSAQUERYSET Service;
memset (&Service, 0, sizeof(Service));
Service.dwSize = sizeof(Service);
Service.lpBlob = &blob;
Service.dwNameSpace = NS_BTH;
// Publish the service
rc = WSASetService(&Service, RNRSERVICE_REGISTER, 0);
if (rc == SOCKET_ERROR)
rc = GetLastError();
// Clean up
LocalFree ((PBYTE)pSetBlob);
return rc;
}
When the application no longer wants to support the service, it needs to remove the record from the SDP database. Removing the record is accomplished by using WSASetService(), specifying the record handle of the service and the flag RNRSERVICE_DELETE. The record handle is passed in the BTHNS_SETBLOB structure. The other fields of this structure are ignored. The following code shows a routine that unregisters a service.
int UnpublishRecord (ULONG hRecord)
{
ULONG ulSdpVersion = BTH_SDP_VERSION;
int rc;
BTHNS_SETBLOB SetBlob;
BLOB blob;
WSAQUERYSET Service;
memset (&SetBlob, 0, sizeof (SetBlob));
SetBlob.pRecordHandle = &hRecord;
SetBlob.pSdpVersion = &ulSdpVersion;
// Init the container blob
blob.cbSize = sizeof(BTHNS_SETBLOB);
blob.pBlobData = (PBYTE) &SetBlob;
// Init the WSAQuerySet struct
memset (&Service, 0, sizeof(Service));
Service.dwSize = sizeof(Service);
Service.lpBlob = &blob;
Service.dwNameSpace = NS_BTH;
// Unpublish the service
rc = WSASetService(&Service, RNRSERVICE_DELETE, 0);
return rc;
}
The format of the SDP information that’s published is so complex that Windows CE provides a special COM control to construct and de-construct SDP records. Even with the control, parsing SDP records isn’t easy. The first problem is knowing what is required in the SDP record. The information in the SDP record is defined by the Bluetooth specification, and a complete explanation of this data far exceeds the space available in this book for such an explanation.
As a shortcut, many Bluetooth applications compose a generic record, either hand-assembling the record or using an example tool named BthNsCreate (available in the Windows CE SDK) that’s provided in the Platform Builder. These hand-generated records are saved as a byte array in the application. The known offsets where the GUID and the RFCOMM channel are stored are known and are updated in the array at run time. The record is then published using WSASetService(), as shown earlier.
The following code shows a routine that uses a canned SDP record with the GUID of the service and the channel stuffed into the appropriate places in the record.
int RegisterService(HWND hWnd, GUID *pguid, byte bChannel, ULONG *pRecord)
{
// SDP dummy record
// GUID goes at offset 8
// Channel goes in last byte of record.
static BYTE bSDPRecord[ ] = {
0x35, 0x27, 0x09, 0x00, 0x01, 0x35, 0x11, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x09, 0x00, 0x04, 0x35, 0x0C, 0x35, 0x03, 0x19, 0x01,
0x00, 0x35, 0x05, 0x19, 0x00, 0x03, 0x08, 0x00};
// Translate guid into net byte order for SDP record
GUID *p = (GUID *)&bSDPRecord[8];
p->Data1 = htonl (pguid->Data1); p->Data2 = htons (pguid->Data2);
p->Data3 = htons (pguid->Data3);
memcpy (p->Data4, pguid->Data4, sizeof (pguid->Data4));
// Copy channel value into record
bSDPRecord[sizeof (bSDPRecord)-1] = bChannel;
return PublishRecord (hWnd, bSDPRecord, sizeof (bSDPRecord), pRecord);
}
To publish a Bluetooth service record, the application must register the service. Microsoft Windows CE ships a tool, Bthnscreate that you can use to generate a blob that represents the SDP record. You can then register the record by using the standard Winsock function, WSASetService(). Before you register a Bluetooth service, you must have the following information:
1. You have a record file that contains the SDP data. SDP data must be placed within a SEQUENCE...END block. Each block contains a series of [type] [value] pairs. The following code example shows a sample record file.
; Simple data file
1 SEQUENCE
; Sequences contain one or more elements.
UUID16 30000
UUID32 4000000
UUID128 12345678-ABCD-AF12-8800-12345678EF12
END
119 UINT16 19
For more information about the elements of a record file, see SDP Search and Record Generator Sample.
2. You have the Bthnscreate executable in the release directory on your device. If not, then build this tool by using the source code for Bthnscreate.
To register a Bluetooth service by using Winsock:
bthnscreate -record <record filename>
Bthnscreate generates an SDP record in raw binary format. The following example shows the output for the sample record file.
const int cSdpRecord = 0x00000043
BYTE rgbSdpRecord[] = {
0x35, 0x41, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,
0x11, 0x06, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35,
0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00,
0x03, 0x08, 0x0a, 0x35, 0x03, 0x19, 0x00, 0x08,
0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e,
0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, 0x00,
0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x06,
0x09, 0x01, 0x00, 0x09, 0x01, 0x00, 0x25, 0x03,
0x46, 0x54, 0x50
};
WSADATA wsd;
WSAStartup (MAKEWORD(2,2), &wsd);
a. Define the SDP record size SDP_RECORD_SIZE and set the record size returned in step 1. The following example code shows how to define the record size.
#define SDP_RECORD_SIZE 0x00000043
b. Identify the byte that represents the channel in the record file, and define the server channel offset as the index of this byte in the array. For example, for the previous sample, the offset is 32. The following example code defines the server offset.
#define SDP_CHANNEL_OFFSET 32
c. Declare a BYTE array, rgbSdpRecord and initialize it with the output values generated by Bthnscreate as the following example shows.
static const BYTE rgbSdpRecord[] = {
0x35, 0x41, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,
0x11, 0x06, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35,
0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00,
0x03, 0x08, 0x0a, 0x35, 0x03, 0x19, 0x00, 0x08,
//Server offset
0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e,
0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, 0x00,
0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x06,
0x09, 0x01, 0x00, 0x09, 0x01, 0x00, 0x25, 0x03,
0x46, 0x54, 0x50
};
struct
{
BTHNS_SETBLOB b;
unsigned char uca[SDP_RECORD_SIZE];
} bigBlob;
BLOB blob;
ULONG ulSdpVersion = BTH_SDP_VERSION;
bigBlob.b.pRecordHandle = &recordHandle;
bigBlob.b.pSdpVersion = &ulSdpVersion;
bigBlob.b.fSecurity = 0;
bigBlob.b.fOptions = 0;
bigBlob.b.ulRecordLength = SDP_RECORD_SIZE;
memcpy (bigBlob.b.pRecord, rgbSdpRecord, SDP_RECORD_SIZE);
blob.cbSize = sizeof(BTHNS_SETBLOB) + SDP_RECORD_SIZE - 1;
blob.pBlobData = (PBYTE) &bigBlob;
In the preceding example, the structure bigblob declares a BTHNS_SETBLOB variable that stores the service record and information about the record such as the SDP version and size of the record. For BTHNS_SETBLOB, set the following members:
a. pRecordHandle: Set to the address of a ULONG variable that contains the record handle. For new records, this handle should be set to zero (0). During registration, SDP assigns a value to this handle and returns it in an out parameter for WSASetService.
b. ulRecordLength: Set to SDP_RECORD_SIZE as defined in step 2.
c. pSdpVersion: Set to the address of a ULONG variable that is assigned the value, BTH_SDP_VERSION, that defines the SDP version and is declared in the header file bthapi.h.
d. pRecord: Points to the buffer that contains the contents of the BYTE array rgbSdpRecord as defined in step 2.
The variable, blob, declared as BLOB type, references bigblob variable and specifies the total size for the record and data.
WSAQUERYSET Service;
memset (&Service, 0, sizeof(Service));
Service.dwSize = sizeof(Service);
Service.lpBlob = &blob;
Service.dwNameSpace = NS_BTH;
int iRet = BthNsSetService (&Service, RNRSERVICE_REGISTER,0);
To terminate the use of Winsock services, call the WSACleanup() function. Call WSACleanup() for every successful call to WSAStartup() made by an application.
Example on how to search SDP and record generator (for platform Builder, Microsoft Windows CE 5.0) can be found at SDP Search and Record Generator Sample.
< Querying Bluetooth Device Example | Winsock2 Supported Protocols Main | Winsock 2 & Bluetooth APIs >