📄 bthutils.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
#include "stdafx.h"
#include "BthUtils.h"
//Function: ReadThread
//Purpose: Receives messages from the peer device
DWORD WINAPI BthUtils::ReadData(LPVOID voidArg)
{
int iSize=0, cbBytesRecd=0 ;
WCHAR szMessage[MAX_MESSAGE_SIZE];
char pbuf[MAX_MESSAGE_SIZE];
BthUtils *functionInfo = (BthUtils*) voidArg;
iSize = sizeof(functionInfo->m_saClient);
BTH_REMOTE_NAME remotename;
memset(&remotename, sizeof(remotename), 0);
SOCKET s = accept (functionInfo->m_socketServer, (SOCKADDR*)&(functionInfo->m_saClient), &iSize);
remotename.bt = functionInfo->m_saClient.btAddr;
setsockopt(functionInfo->m_socketServer,SOL_RFCOMM, SO_BTH_SET_READ_REMOTE_NAME, (char*)&remotename, sizeof(remotename));
if (s != INVALID_SOCKET)
{
for ( ; ; )
{
//receive data in pbuf
cbBytesRecd = recv (s, pbuf, MAX_MESSAGE_SIZE, 0);
//if error occured in receiving, return error code
if (cbBytesRecd == SOCKET_ERROR)
{
return WSAGetLastError();
}
else
{
// something was received, then copy the contents in szMessage
if(cbBytesRecd>0)
{
StringCchPrintf (szMessage, ARRAYSIZE(szMessage), L"%s", pbuf);
(*functionInfo->pCallBackFunction)(szMessage);
}
}
}
}
return 0;
}
//Function: BthUtils (constructor)
//Purpose: Initialize: Winsock and class data members
// Turn on Bluetooth and set it discoverable mode, if not on already
BthUtils::BthUtils()
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 2 );
WSAStartup( wVersionRequested, &wsaData );
m_pDeviceList = NULL;
m_pStart = NULL;
m_pEnd = NULL;
m_pCurrentDevice = NULL;
m_iNumDevices = 0;
pCallBackFunction = NULL;
m_hReadThread = NULL;
m_socketServer = INVALID_SOCKET;
m_socketClient = INVALID_SOCKET;
BthGetMode(&m_dwBluetoothMode);
if(m_dwBluetoothMode==BTH_POWER_OFF)
{
BthSetMode(BTH_DISCOVERABLE);
}
}
//Function: ~BthUtils (destructor)
//Purpose: Set radio mode back to original state
// release the linked list, sockets
// terminate the ReadThread thread
BthUtils::~BthUtils()
{
//Set radio mode back to original state
BthSetMode(m_dwBluetoothMode);
if(m_pStart)
{
for(m_pCurrentDevice = m_pStart;m_pCurrentDevice;)
{
DeviceList *temp = m_pCurrentDevice;
m_pCurrentDevice = m_pCurrentDevice->NextDevice;
free(temp);
}
m_pStart=NULL;
}
if(m_socketClient)
closesocket (m_socketClient);
if(m_socketServer)
closesocket (m_socketServer);
//Terminate the read thread that receives chat messages from the client
if(m_hReadThread)
{
DWORD dwExitCode = 0;
TerminateThread(m_hReadThread, dwExitCode);
}
WSACleanup();
}
//Function: DiscoverDevices
//Purpose: Searches Bluetooth devices in range
// Populates the link list with the name and address of the devices found
//Return: If error occurs, returns the appropriate WSAGetLastError, otherwise returns zero.
int BthUtils::DiscoverDevices()
{
WSAQUERYSET wsaq;
HANDLE hLookup;
DeviceList * tempDevice;
union {
CHAR buf[5000];
double __unused; // ensure proper alignment
};
LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf;
DWORD dwSize = sizeof(buf);
BOOL bHaveName;
ZeroMemory(&wsaq, sizeof(wsaq));
wsaq.dwSize = sizeof(wsaq);
wsaq.dwNameSpace = NS_BTH;
wsaq.lpcsaBuffer = NULL;
if (ERROR_SUCCESS != WSALookupServiceBegin (&wsaq, LUP_CONTAINERS, &hLookup))
{
return WSAGetLastError();
}
ZeroMemory(pwsaResults, sizeof(WSAQUERYSET));
pwsaResults->dwSize = sizeof(WSAQUERYSET);
pwsaResults->dwNameSpace = NS_BTH;
pwsaResults->lpBlob = NULL;
if(m_pStart)
{
for(m_pCurrentDevice=m_pStart;m_pCurrentDevice;)
{
DeviceList *temp=m_pCurrentDevice;
m_pCurrentDevice=m_pCurrentDevice->NextDevice;
free(temp);
}
}
m_pEnd=m_pStart=NULL;
m_iNumDevices=0;
while (true)
{
if(WSALookupServiceNext (hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults)!=ERROR_SUCCESS)
break;
ASSERT (pwsaResults->dwNumberOfCsAddrs == 1);
//Populate the link list
tempDevice=(DeviceList*)malloc(sizeof(DeviceList));
tempDevice->NextDevice=NULL;
if(m_pStart==NULL)
{
m_pStart = tempDevice;
m_pEnd=m_pStart;
}
else
{
m_pEnd->NextDevice =tempDevice;
m_pEnd=tempDevice;
}
m_iNumDevices++;
m_pEnd->bthAddress = ((SOCKADDR_BTH *)pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr;
bHaveName = pwsaResults->lpszServiceInstanceName && *(pwsaResults->lpszServiceInstanceName);
//If device name is available, add to node
StringCchPrintf(m_pEnd->bthName, ARRAYSIZE(m_pEnd->bthName),L"%s",bHaveName ? pwsaResults->lpszServiceInstanceName : L"");
}
WSALookupServiceEnd(hLookup);
// LeaveCriticalSection(&criticalSection);
return 0;
}
//Function: GetDeviceInfo
//Purpose: Returns name and address of all the devices in the link list in DeviceInfo. This is used by the UI to display the names and addresses of the devices found
//Output: DeviceInfo: name and address
//Return: Success returns zero.
int BthUtils::GetDeviceInfo(DeviceInfo *pPeerDevicesInfo)
{
int iCtr=0;
for (m_pCurrentDevice = m_pStart;(m_pCurrentDevice);m_pCurrentDevice=m_pCurrentDevice->NextDevice,iCtr++)
{
StringCchPrintf(pPeerDevicesInfo[iCtr].szDeviceNameAddr, ARRAYSIZE(pPeerDevicesInfo[iCtr].szDeviceNameAddr), L"%s:(%04x%08x)", m_pCurrentDevice->bthName, GET_NAP(m_pCurrentDevice->bthAddress), GET_SAP(m_pCurrentDevice->bthAddress));
}
return 0;
}
//Function: OpenServerConnection
//Purpose: Opens a server socket for listening. Registers the service. Creates a thread, ReadThread for reading incoming messages.
//Input: The SDP record of the service to register, size of the SDP record, channel offset in the record, pointer to the UI function that displays the messages in the UI
//Return: If error occurs, returns the appropriate WSAGetLastError, otherwise returns zero.
int BthUtils::OpenServerConnection(BYTE *rgbSdpRecord, int cSdpRecord, int iChannelOffset, void (*funcPtr)(WCHAR *))
{
int iNameLen=0;
if(m_socketServer==INVALID_SOCKET)
{
m_socketServer = socket (AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM);
if (m_socketServer == INVALID_SOCKET)
{
return WSAGetLastError ();
}
SOCKADDR_BTH sa;
memset (&sa, 0, sizeof(sa));
sa.addressFamily = AF_BT;
sa.port = 0;
if (bind (m_socketServer, (SOCKADDR *)&sa, sizeof(sa)))
{
return WSAGetLastError ();
}
iNameLen = sizeof(sa);
if (getsockname(m_socketServer, (SOCKADDR *)&sa, &iNameLen))
{
return WSAGetLastError ();
}
if(RegisterService(rgbSdpRecord, cSdpRecord, iChannelOffset, (UCHAR)sa.port)!=0)
return WSAGetLastError();
if (listen (m_socketServer, SOMAXCONN))
{
return WSAGetLastError ();
}
}
pCallBackFunction=funcPtr;
m_hReadThread= CreateThread(NULL, 0, ReadData, (LPVOID)this, 0, NULL);
return 0;
}
//Function: RegisterService
//Purpose: Publishes the SDP record.
//Input: The SDP record of the service to register, size of the SDP record, channel offset in the record, channel number assigned automatically by OpenServerConnection
//Return: If error occurs, returns the appropriate WSAGetLastError, otherwise returns zero.
int BthUtils::RegisterService(BYTE *rgbSdpRecord, int cSdpRecord, int iChannelOffset, UCHAR channel)
{
ULONG recordHandle = 0;
struct bigBlob
{
BTHNS_SETBLOB b;
}*pBigBlob;
pBigBlob = (bigBlob *)malloc(sizeof(struct bigBlob)+cSdpRecord);
ULONG ulSdpVersion = BTH_SDP_VERSION;
pBigBlob->b.pRecordHandle = &recordHandle;
pBigBlob->b.pSdpVersion = &ulSdpVersion;
pBigBlob->b.fSecurity = 0;
pBigBlob->b.fOptions = 0;
pBigBlob->b.ulRecordLength = cSdpRecord;
memcpy (pBigBlob->b.pRecord, rgbSdpRecord, cSdpRecord);
pBigBlob->b.pRecord[iChannelOffset] = (unsigned char)channel;
BLOB blob;
blob.cbSize = sizeof(BTHNS_SETBLOB) + cSdpRecord - 1;
blob.pBlobData = (PBYTE) pBigBlob;
WSAQUERYSET Service;
memset (&Service, 0, sizeof(Service));
Service.dwSize = sizeof(Service);
Service.lpBlob = &blob;
Service.dwNameSpace = NS_BTH;
if (WSASetService(&Service,RNRSERVICE_REGISTER,0) == SOCKET_ERROR)
{
free(pBigBlob);
return WSAGetLastError();
}
else
{
free(pBigBlob);
return 0;
}
}
//Function: SendMessageToServer
//Purpose: Opens a client socket to connect to the server. Called when the local device initiates the chat.
//Input: string containing the GUID of the service running on the server that the client wants to connect.
// iSelectedDeviceIndex is the selected device in the UI that the local device wants to connect. If the peer device initiates the chat, the this parameter is set to -1.
//Return: If error occurs, returns the appropriate WSAGetLastError, otherwise returns zero.
int BthUtils::SendMessageToServer(WCHAR *strGUID, WCHAR *szMessage, int iSelectedDeviceIndex)
{
int iRetVal=0, iLenMessage=0, iBytesSent=0 ;
if(m_socketClient==INVALID_SOCKET)
{
iRetVal=OpenClientConnection(strGUID, iSelectedDeviceIndex);
if(iRetVal!=0)
{
return iRetVal;
}
}
iLenMessage = (wcslen (szMessage) + 1) * sizeof(WCHAR);
if (iLenMessage > sizeof (WCHAR))
{
iBytesSent = send (m_socketClient, (char *)szMessage, iLenMessage, 0);
if (iBytesSent != iLenMessage)
{
return WSAGetLastError ();
}
}
return 0;
}
//Function: OpenClientConnection
//Purpose: Opens a client socket to connect to the server.
//Input: string containing the GUID of the service running on the server that the client wants to connect.
// iSelectedDeviceIndex is the selected device in the UI that the local device wants to connect. If the peer device initiates the chat, the this parameter is set to -1.
//Return: If error occurs, returns the appropriate WSAGetLastError, otherwise returns zero.
int BthUtils::OpenClientConnection(WCHAR *strGUID, int iSelectedDeviceIndex)
{
if (m_socketClient==INVALID_SOCKET)
{
GUID ServerGuid;
if(GetGUID(strGUID, &ServerGuid))
return -1;
m_socketClient = socket (AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM);
if (m_socketClient == INVALID_SOCKET)
{
return WSAGetLastError();
}
SOCKADDR_BTH sa;
memset (&sa, 0, sizeof(sa));
sa.addressFamily = AF_BT;
//Search for the selected device in the list box in the link list
m_pCurrentDevice=m_pStart;
sa.serviceClassId=ServerGuid;
if(iSelectedDeviceIndex==-1)
{
sa.btAddr=m_saClient.btAddr;
}
else
{
for (int iCount = 0 ;(m_pCurrentDevice)&&iCount!=iSelectedDeviceIndex;m_pCurrentDevice=m_pCurrentDevice->NextDevice,iCount++);
sa.btAddr = m_pCurrentDevice->bthAddress;
}
if (connect (m_socketClient, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR)
{
m_socketClient=INVALID_SOCKET;
return WSAGetLastError();
}
}
return 0;
}
//Function: GetGUID
//Purpose: Conversts a string containing the GUID into a GUID datatype.
//Input: string cotaining the GUID
//Output: GUID type
//Return: Returns -1 in case of an error, otherwise returns zero.
int BthUtils::GetGUID(WCHAR *psz, GUID *pGUID)
{
int data1, data2, data3;
int data4[8];
if (11 == swscanf(psz, L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
&data1, &data2, &data3,
&data4[0], &data4[1], &data4[2], &data4[3],
&data4[4], &data4[5], &data4[6], &data4[7])) {
pGUID->Data1 = data1;
pGUID->Data2 = data2 & 0xffff;
pGUID->Data3 = data3 & 0xffff;
for (int i = 0 ; i < 8 ; ++i)
pGUID->Data4[i] = data4[i] & 0xff;
return 0;
}
return -1;
}
//Function: GetLocalDeviceName
//Purpose: Returns the name of the owner set in the registry
//Output: DeviceInfo: (only)name
//Return: Returns -1 in case of an error, otherwise returns zero.
int BthUtils::GetLocalDeviceName(DeviceInfo *pLocalDeviceInfo)
{
HKEY hKey;
DWORD dwRegType, dwRegSize;
if(RegOpenKeyEx(HKEY_CURRENT_USER,L"ControlPanel\\Owner",0,0,&hKey)==ERROR_SUCCESS)
{
if(RegQueryValueEx(hKey,L"Name",0,&dwRegType,(LPBYTE)pLocalDeviceInfo->szDeviceNameAddr,&dwRegSize)==ERROR_SUCCESS)
{
if (dwRegSize>MAX_NAME_SIZE)
{
RegCloseKey(hKey);
return -1;
}
RegCloseKey(hKey);
}
RegCloseKey(hKey);
}
return 0;
}
//Function: GetDeviceInfo
//Purpose: Searches the link list for the specified device and returns address and name in DeviceInfo
//Input: The current device index selected in the UI
//Output: DeviceInfo: name and address
//Return: Returns -1 in case of an error, otherwise returns zero.
int BthUtils::GetDeviceInfo(DeviceInfo *pPeerDeviceInfo, int iSelectedItem)
{
int iCtr=0;
for (m_pCurrentDevice = m_pStart;(m_pCurrentDevice);m_pCurrentDevice=m_pCurrentDevice->NextDevice,iCtr++)
{
if(iCtr==iSelectedItem)
{
StringCchPrintf(pPeerDeviceInfo[0].szDeviceNameAddr, ARRAYSIZE(pPeerDeviceInfo[0].szDeviceNameAddr), L"%s:(%04x%08x)", m_pCurrentDevice->bthName, GET_NAP(m_pCurrentDevice->bthAddress), GET_SAP(m_pCurrentDevice->bthAddress));
return 0;
}
}
return -1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -