November 26, 2014
Hot Topics:

Get Familiar: Microsoft Bluetooth Stack on Windows Mobile

  • October 27, 2006
  • By Alex Gusev
  • Send Email »
  • More Articles »

If the lookup was successful, FindRFCOMMChannel tries to get the associated channel number. You will find the details below. Note that you need to link your application with two libs (see the #pragma statements). This function looks ugly at first glance, but actually there is nothing special. All the black job is done for you by the ISdpStream interface, which verifies the data obtained from a service lookup (see the snippet above) and loads ISdpRecords to the allocated memory pool. The snippet then loops through all records in the table and tries to detect the RFCOMM protocol and associated channel number:

#include <winsock2.h>
#include <ws2bth.h>
#include <bt_sdp.h>
#include <bthapi.h>
#include <bt_api.h>

#pragma comment(lib,"ws2.lib")
#pragma comment(lib,"bthguid.lib")
...
int IsRfcommUuid(NodeData *pNode)
{
   if (pNode->type != SDP_TYPE_UUID)
      return FALSE;

   if (pNode->specificType      == SDP_ST_UUID16)
      return (pNode->u.uuid16   == RFCOMM_PROTOCOL_UUID16);
   else if (pNode->specificType == SDP_ST_UUID32)
      return (pNode->u.uuid32   == RFCOMM_PROTOCOL_UUID16);
   else if (pNode->specificType == SDP_ST_UUID128)
      return (0 == memcmp(&RFCOMM_PROTOCOL_UUID,&pNode->
              u.uuid128,sizeof(GUID)));

   return FALSE;
}

HRESULT FindRFCOMMChannel (unsigned char *pStream, int cStream,
                           unsigned char& nChannel)
{
   ISdpRecord **pRecordArg = NULL;
   int cRecordArg          = 0;
   ISdpStream *pIStream    = NULL;
   HRESULT hr              = 0;
   ULONG ulError           = 0;

   nChannel = 0;

   hr = CoCreateInstance(__uuidof(SdpStream),NULL,
                         CLSCTX_INPROC_SERVER,
                         __uuidof(ISdpStream),(LPVOID *)
                         &pIStream);

   if ( FAILED(hr) || pIStream == NULL )
      return hr;

   hr = pIStream->Validate (pStream, cStream,&ulError);

   if (SUCCEEDED(hr))
   {
      hr = pIStream->VerifySequenceOf(pStream, cStream,
                                      SDP_TYPE_SEQUENCE,NULL,
                                      (ULONG *)&cRecordArg);

      if (SUCCEEDED(hr) && cRecordArg > 0)
      {
         pRecordArg =
            (ISdpRecord **) CoTaskMemAlloc(sizeof(ISdpRecord*)
                                           * cRecordArg);

         if (pRecordArg != NULL)
         {
            hr =
               pIStream->RetrieveRecords(pStream, cStream,
                                         pRecordArg,(ULONG *)
                                         &cRecordArg);

            if ( FAILED(hr) )
            {
               CoTaskMemFree(pRecordArg);
               pRecordArg = NULL;
               cRecordArg = 0;
            }
         }
         else
         {
            hr = E_OUTOFMEMORY;
         }
     }
   }

   if (pIStream != NULL)
   {
      pIStream->Release();
      pIStream = NULL;
   }

   if ( FAILED(hr) )
      return hr;

   for (int i = 0; (nChannel == 0) && (i < cRecordArg); i++)
   {
      ISdpRecord *pRecord = pRecordArg[i];
      // contains SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST data,
      // if available
      NodeData protocolList;

      if (ERROR_SUCCESS !=
          pRecord->GetAttribute(SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST,
                                &protocolList) ||
                                (protocolList.type !=
                                 SDP_TYPE_CONTAINER))
      {
         if (protocolList.type == SDP_TYPE_STRING)
            CoTaskMemFree(protocolList.u.str.val);
         else if (protocolList.type == SDP_TYPE_URL)
            CoTaskMemFree(protocolList.u.url.val);
         continue;
      }

      ISdpNodeContainer *pRecordContainer = protocolList.u.container;
      int cProtocols = 0;
      NodeData protocolDescriptor;

      pRecordContainer->GetNodeCount((DWORD *)&cProtocols);
      for (int j = 0; (nChannel == 0) && (j < cProtocols); j++)
      {
         pRecordContainer->GetNode(j,&protocolDescriptor);

         if (protocolDescriptor.type != SDP_TYPE_CONTAINER)
            continue;

         ISdpNodeContainer *pProtocolContainer =
            protocolDescriptor.u.container;
         int cProtocolAtoms = 0;
         pProtocolContainer->GetNodeCount((DWORD *)&cProtocolAtoms);

         for (int k = 0; (nChannel == 0) && (k < cProtocolAtoms); k++)
         {
            NodeData nodeAtom;
            pProtocolContainer->GetNode(k,&nodeAtom);

            if (IsRfcommUuid(&nodeAtom))
            {
               if (k+1 == cProtocolAtoms)
               {
                  // Error: Channel ID should follow RFCOMM uuid
                  break;
               }

               NodeData channelID;
               pProtocolContainer->GetNode(k+1,&channelID);

               switch(channelID.specificType)
               {
               case SDP_ST_UINT8:
                  nChannel = channelID.u.uint8;
                  break;
               case SDP_ST_INT8:
                  nChannel = channelID.u.int8;
                  break;
               case SDP_ST_UINT16:
                  nChannel = channelID.u.uint16;
                  break;
               case SDP_ST_INT16:
                  nChannel = channelID.u.int16;
                  break;
               case SDP_ST_UINT32:
                  nChannel = channelID.u.uint32;
                  break;
               case SDP_ST_INT32:
                  nChannel = channelID.u.int32;
                  break;
               default:
                  nChannel = 0;
               }
               break;
            }
         }
      }
      if (protocolList.type == SDP_TYPE_STRING)
         CoTaskMemFree(protocolList.u.str.val);
      else if (protocolList.type == SDP_TYPE_URL)
         CoTaskMemFree(protocolList.u.url.val);
   }

   // cleanup
   for (i = 0; i < cRecordArg; i++)
      pRecordArg[i]->Release();
   CoTaskMemFree(pRecordArg);

   return (nChannel != 0) ? S_OK : S_FALSE;
}

The preceding code doesn't pretend to be too elegant, so you're invited to polish it. The main thing is that you fulfilled your goal: a channel number associated with a service is successfully detected, so you can use BT sockets just as normal ones:

...
sockAddr.port = nChannel & 0xff;
int nResult   = connect(btSocket, (SOCKADDR*)&sockAddr,
                        sizeof(sockAddr));

Conclusion

In the last two articles, you have see two of the most popular BT stacks: from Widcomm and Microsoft. They are different, but nevertheless may be wrapped together. I hope that the sample snippets provide enough basic information to use them in your own applications with both stacks. But, as usual, the task of studying tons of technical documentation is inevitable if you need more details than were given here...

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. After working almost a decade for an international retail software company as a team leader of the Windows Mobile R department, he has decided to dive into Symbian OS ™ Core development.





Page 3 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel