// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved // // BTHCxnDemo.cpp - Simple Bluetooth application using Winsock 2.2 and RFCOMM protocol // // // This example demonstrates how to use Winsock-2.2 APIs to connect // between two remote bluetooth devices and transfer data between them. // This example only supports address family of type AF_BTH, // socket of type SOCK_STREAM, protocol of type BTHPROTO_RFCOMM. // // Once this source code is built, the resulting application can be // run either in server mode or in client mode. See command line help // for command-line-examples and detailed explanation about all options. // #include #include #include #include #include #include // {B62C4E8D-62CC-404b-BBBF-BF3E3BBB1374} DEFINE_GUID(g_guidServiceClass, 0xb62c4e8d, 0x62cc, 0x404b, 0xbb, 0xbf, 0xbf, 0x3e, 0x3b, 0xbb, 0x13, 0x74); #define CXN_TEST_DATA_STRING (L"~!@#$%^&*()-_=+?<>1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") #define CXN_TRANSFER_DATA_LENGTH (sizeof(CXN_TEST_DATA_STRING)) #define CXN_BDADDR_STR_LEN 17 // 6 two-digit hex values plus 5 colons #define CXN_MAX_INQUIRY_RETRY 3 #define CXN_DELAY_NEXT_INQUIRY 15 #define CXN_SUCCESS 0 #define CXN_ERROR 1 #define CXN_DEFAULT_LISTEN_BACKLOG 4 wchar_t g_szRemoteName[BTH_MAX_NAME_SIZE + 1] = {0}; // 1 extra for trailing NULL character wchar_t g_szRemoteAddr[CXN_BDADDR_STR_LEN + 1] = {0}; // 1 extra for trailing NULL character int g_ulMaxCxnCycles = 1; ULONG NameToBthAddr(_In_ const LPWSTR pszRemoteName, _Out_ PSOCKADDR_BTH pRemoteBthAddr); ULONG RunClientMode(_In_ SOCKADDR_BTH ululRemoteBthAddr, _In_ int iMaxCxnCycles = 1); ULONG RunServerMode(_In_ int iMaxCxnCycles = 1); void ShowCmdLineHelp(void); ULONG ParseCmdLine(_In_ int argc, _In_reads_(argc) wchar_t * argv[]); int _cdecl wmain(_In_ int argc, _In_reads_(argc)wchar_t *argv[]) { ULONG ulRetCode = CXN_SUCCESS; WSADATA WSAData = {0}; SOCKADDR_BTH RemoteBthAddr = {0}; // // Parse the command line // ulRetCode = ParseCmdLine(argc, argv); if ( CXN_ERROR == ulRetCode ) { // // Command line syntax error. Display cmd line help // ShowCmdLineHelp(); } else if ( CXN_SUCCESS != ulRetCode) { wprintf(L"-FATAL- | Error in parsing command line\n"); } // // Ask for Winsock version 2.2. // if ( CXN_SUCCESS == ulRetCode) { ulRetCode = WSAStartup(MAKEWORD(2, 2), &WSAData); if (CXN_SUCCESS != ulRetCode) { wprintf(L"-FATAL- | Unable to initialize Winsock version 2.2\n"); } } if ( CXN_SUCCESS == ulRetCode) { // // Note, this app "prefers" the name if provided, but it is app-specific // Other applications may provide more generic treatment. // if ( L'\0' != g_szRemoteName[0] ) { // // Get address from the name of the remote device and run the application // in client mode // ulRetCode = NameToBthAddr(g_szRemoteName, &RemoteBthAddr); if ( CXN_SUCCESS != ulRetCode ) { wprintf(L"-FATAL- | Unable to get address of the remote radio having name %s\n", g_szRemoteName); } if ( CXN_SUCCESS == ulRetCode) { ulRetCode = RunClientMode(RemoteBthAddr, g_ulMaxCxnCycles); } } else if ( L'\0' != g_szRemoteAddr[0] ) { // // Get address from formated address-string of the remote device and // run the application in client mode // int iAddrLen = sizeof(RemoteBthAddr); ulRetCode = WSAStringToAddressW(g_szRemoteAddr, AF_BTH, NULL, (LPSOCKADDR)&RemoteBthAddr, &iAddrLen); if ( CXN_SUCCESS != ulRetCode ) { wprintf(L"-FATAL- | Unable to get address of the remote radio having formated address-string %s\n", g_szRemoteAddr); } if ( CXN_SUCCESS == ulRetCode ) { ulRetCode = RunClientMode(RemoteBthAddr, g_ulMaxCxnCycles); } } else { // // No remote name/address specified. Run the application in server mode // ulRetCode = RunServerMode(g_ulMaxCxnCycles); } } return(int)ulRetCode; } // // NameToBthAddr converts a bluetooth device name to a bluetooth address, // if required by performing inquiry with remote name requests. // This function demonstrates device inquiry, with optional LUP flags. // ULONG NameToBthAddr(_In_ const LPWSTR pszRemoteName, _Out_ PSOCKADDR_BTH pRemoteBtAddr) { INT iResult = CXN_SUCCESS; BOOL bContinueLookup = FALSE, bRemoteDeviceFound = FALSE; ULONG ulFlags = 0, ulPQSSize = sizeof(WSAQUERYSET); HANDLE hLookup = NULL; PWSAQUERYSET pWSAQuerySet = NULL; ZeroMemory(pRemoteBtAddr, sizeof(*pRemoteBtAddr)); pWSAQuerySet = (PWSAQUERYSET) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulPQSSize); if ( NULL == pWSAQuerySet ) { iResult = STATUS_NO_MEMORY; wprintf(L"!ERROR! | Unable to allocate memory for WSAQUERYSET\n"); } // // Search for the device with the correct name // if ( CXN_SUCCESS == iResult) { for ( INT iRetryCount = 0; !bRemoteDeviceFound && (iRetryCount < CXN_MAX_INQUIRY_RETRY); iRetryCount++ ) { // // WSALookupService is used for both service search and device inquiry // LUP_CONTAINERS is the flag which signals that we're doing a device inquiry. // ulFlags = LUP_CONTAINERS; // // Friendly device name (if available) will be returned in lpszServiceInstanceName // ulFlags |= LUP_RETURN_NAME; // // BTH_ADDR will be returned in lpcsaBuffer member of WSAQUERYSET // ulFlags |= LUP_RETURN_ADDR; if ( 0 == iRetryCount ) { wprintf(L"*INFO* | Inquiring device from cache...\n"); } else { // // Flush the device cache for all inquiries, except for the first inquiry // // By setting LUP_FLUSHCACHE flag, we're asking the lookup service to do // a fresh lookup instead of pulling the information from device cache. // ulFlags |= LUP_FLUSHCACHE; // // Pause for some time before all the inquiries after the first inquiry // // Remote Name requests will arrive after device inquiry has // completed. Without a window to receive IN_RANGE notifications, // we don't have a direct mechanism to determine when remote // name requests have completed. // wprintf(L"*INFO* | Unable to find device. Waiting for %d seconds before re-inquiry...\n", CXN_DELAY_NEXT_INQUIRY); Sleep(CXN_DELAY_NEXT_INQUIRY * 1000); wprintf(L"*INFO* | Inquiring device ...\n"); } // // Start the lookup service // iResult = CXN_SUCCESS; hLookup = 0; bContinueLookup = FALSE; ZeroMemory(pWSAQuerySet, ulPQSSize); pWSAQuerySet->dwNameSpace = NS_BTH; pWSAQuerySet->dwSize = sizeof(WSAQUERYSET); iResult = WSALookupServiceBegin(pWSAQuerySet, ulFlags, &hLookup); // // Even if we have an error, we want to continue until we // reach the CXN_MAX_INQUIRY_RETRY // if ( (NO_ERROR == iResult) && (NULL != hLookup) ) { bContinueLookup = TRUE; } else if ( 0 < iRetryCount ) { wprintf(L"=CRITICAL= | WSALookupServiceBegin() failed with error code %d, WSAGetLastError = %d\n", iResult, WSAGetLastError()); break; } while ( bContinueLookup ) { // // Get information about next bluetooth device // // Note you may pass the same WSAQUERYSET from LookupBegin // as long as you don't need to modify any of the pointer // members of the structure, etc. // // ZeroMemory(pWSAQuerySet, ulPQSSize); // pWSAQuerySet->dwNameSpace = NS_BTH; // pWSAQuerySet->dwSize = sizeof(WSAQUERYSET); if ( NO_ERROR == WSALookupServiceNext(hLookup, ulFlags, &ulPQSSize, pWSAQuerySet) ) { // // Compare the name to see if this is the device we are looking for. // if ( ( pWSAQuerySet->lpszServiceInstanceName != NULL ) && ( CXN_SUCCESS == _wcsicmp(pWSAQuerySet->lpszServiceInstanceName, pszRemoteName) ) ) { // // Found a remote bluetooth device with matching name. // Get the address of the device and exit the lookup. // CopyMemory(pRemoteBtAddr, (PSOCKADDR_BTH) pWSAQuerySet->lpcsaBuffer->RemoteAddr.lpSockaddr, sizeof(*pRemoteBtAddr)); bRemoteDeviceFound = TRUE; bContinueLookup = FALSE; } } else { iResult = WSAGetLastError(); if ( WSA_E_NO_MORE == iResult ) { //No more data // // No more devices found. Exit the lookup. // bContinueLookup = FALSE; } else if ( WSAEFAULT == iResult ) { // // The buffer for QUERYSET was insufficient. // In such case 3rd parameter "ulPQSSize" of function "WSALookupServiceNext()" receives // the required size. So we can use this parameter to reallocate memory for QUERYSET. // HeapFree(GetProcessHeap(), 0, pWSAQuerySet); pWSAQuerySet = (PWSAQUERYSET) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulPQSSize); if ( NULL == pWSAQuerySet ) { wprintf(L"!ERROR! | Unable to allocate memory for WSAQERYSET\n"); iResult = STATUS_NO_MEMORY; bContinueLookup = FALSE; } } else { wprintf(L"=CRITICAL= | WSALookupServiceNext() failed with error code %d\n", iResult); bContinueLookup = FALSE; } } } // // End the lookup service // WSALookupServiceEnd(hLookup); if ( STATUS_NO_MEMORY == iResult ) { break; } } } if ( NULL != pWSAQuerySet ) { HeapFree(GetProcessHeap(), 0, pWSAQuerySet); pWSAQuerySet = NULL; } if ( bRemoteDeviceFound ) { iResult = CXN_SUCCESS; } else { iResult = CXN_ERROR; } return iResult; } // // RunClientMode runs the application in client mode. It opens a socket, connects it to a // remote socket, transfer some data over the connection and closes the connection. // ULONG RunClientMode(_In_ SOCKADDR_BTH RemoteAddr, _In_ int iMaxCxnCycles) { ULONG ulRetCode = CXN_SUCCESS; int iCxnCount = 0; wchar_t *pszData = NULL; SOCKET LocalSocket = INVALID_SOCKET; SOCKADDR_BTH SockAddrBthServer = RemoteAddr; HRESULT res; pszData = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CXN_TRANSFER_DATA_LENGTH); if ( NULL == pszData ) { ulRetCode = STATUS_NO_MEMORY; wprintf(L"=CRITICAL= | HeapAlloc failed | out of memory, gle = [%d] \n", GetLastError()); } if ( CXN_SUCCESS == ulRetCode ) { // // Setting address family to AF_BTH indicates winsock2 to use Bluetooth sockets // Port should be set to 0 if ServiceClassId is spesified. // SockAddrBthServer.addressFamily = AF_BTH; SockAddrBthServer.serviceClassId = g_guidServiceClass; SockAddrBthServer.port = 0; // // Create a static data-string, which will be transferred to the remote // Bluetooth device // res = StringCbCopyN(pszData, CXN_TRANSFER_DATA_LENGTH, CXN_TEST_DATA_STRING, CXN_TRANSFER_DATA_LENGTH); if ( FAILED(res) ) { wprintf(L"=CRITICAL= | Creating a static data string failed\n"); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { pszData[(CXN_TRANSFER_DATA_LENGTH/sizeof(wchar_t)) - 1] = 0; // // Run the connection/data-transfer for user specified number of cycles // for ( iCxnCount = 0; (0 == ulRetCode) && (iCxnCount < iMaxCxnCycles || iMaxCxnCycles == 0); iCxnCount++ ) { wprintf(L"\n"); // // Open a bluetooth socket using RFCOMM protocol // LocalSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if ( INVALID_SOCKET == LocalSocket ) { wprintf(L"=CRITICAL= | socket() call failed. WSAGetLastError = [%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; break; } // // Connect the socket (pSocket) to a given remote socket represented by address (pServerAddr) // if ( SOCKET_ERROR == connect(LocalSocket, (struct sockaddr *) &SockAddrBthServer, sizeof(SOCKADDR_BTH)) ) { wprintf(L"=CRITICAL= | connect() call failed. WSAGetLastError=[%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; break; } // // send() call indicates winsock2 to send the given data // of a specified length over a given connection. // wprintf(L"*INFO* | Sending following data string:\n%s\n", pszData); if ( SOCKET_ERROR == send(LocalSocket, (char *)pszData, (int)CXN_TRANSFER_DATA_LENGTH, 0) ) { wprintf(L"=CRITICAL= | send() call failed w/socket = [0x%I64X], szData = [%p], dataLen = [%I64u]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, pszData, (ULONG64)CXN_TRANSFER_DATA_LENGTH, WSAGetLastError()); ulRetCode = CXN_ERROR; break; } // // Close the socket // if ( SOCKET_ERROR == closesocket(LocalSocket) ) { wprintf(L"=CRITICAL= | closesocket() call failed w/socket = [0x%I64X]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, WSAGetLastError()); ulRetCode = CXN_ERROR; break; } LocalSocket = INVALID_SOCKET; } } if ( INVALID_SOCKET != LocalSocket ) { closesocket(LocalSocket); LocalSocket = INVALID_SOCKET; } if ( NULL != pszData ) { HeapFree(GetProcessHeap(), 0, pszData); pszData = NULL; } return(ulRetCode); } // // RunServerMode runs the application in server mode. It opens a socket, connects it to a // remote socket, transfer some data over the connection and closes the connection. // #define CXN_INSTANCE_STRING L"Sample Bluetooth Server" ULONG RunServerMode(_In_ int iMaxCxnCycles) { ULONG ulRetCode = CXN_SUCCESS; int iAddrLen = sizeof(SOCKADDR_BTH); int iCxnCount = 0; UINT iLengthReceived = 0; UINT uiTotalLengthReceived; size_t cbInstanceNameSize = 0; char * pszDataBuffer = NULL; char * pszDataBufferIndex = NULL; wchar_t * pszInstanceName = NULL; wchar_t szThisComputerName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwLenComputerName = MAX_COMPUTERNAME_LENGTH + 1; SOCKET LocalSocket = INVALID_SOCKET; SOCKET ClientSocket = INVALID_SOCKET; WSAQUERYSET wsaQuerySet = {0}; SOCKADDR_BTH SockAddrBthLocal = {0}; LPCSADDR_INFO lpCSAddrInfo = NULL; HRESULT res; // // This fixed-size allocation can be on the stack assuming the // total doesn't cause a stack overflow (depends on your compiler settings) // However, they are shown here as dynamic to allow for easier expansion // lpCSAddrInfo = (LPCSADDR_INFO) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CSADDR_INFO) ); if ( NULL == lpCSAddrInfo ) { wprintf(L"!ERROR! | Unable to allocate memory for CSADDR_INFO\n"); ulRetCode = CXN_ERROR; } if ( CXN_SUCCESS == ulRetCode ) { if ( !GetComputerName(szThisComputerName, &dwLenComputerName) ) { wprintf(L"=CRITICAL= | GetComputerName() call failed. WSAGetLastError=[%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; } } // // Open a bluetooth socket using RFCOMM protocol // if ( CXN_SUCCESS == ulRetCode ) { LocalSocket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM); if ( INVALID_SOCKET == LocalSocket ) { wprintf(L"=CRITICAL= | socket() call failed. WSAGetLastError = [%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { // // Setting address family to AF_BTH indicates winsock2 to use Bluetooth port // SockAddrBthLocal.addressFamily = AF_BTH; SockAddrBthLocal.port = BT_PORT_ANY; // // bind() associates a local address and port combination // with the socket just created. This is most useful when // the application is a server that has a well-known port // that clients know about in advance. // if ( SOCKET_ERROR == bind(LocalSocket, (struct sockaddr *) &SockAddrBthLocal, sizeof(SOCKADDR_BTH) ) ) { wprintf(L"=CRITICAL= | bind() call failed w/socket = [0x%I64X]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, WSAGetLastError()); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { ulRetCode = getsockname(LocalSocket, (struct sockaddr *)&SockAddrBthLocal, &iAddrLen); if ( SOCKET_ERROR == ulRetCode ) { wprintf(L"=CRITICAL= | getsockname() call failed w/socket = [0x%I64X]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, WSAGetLastError()); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { // // CSADDR_INFO // lpCSAddrInfo[0].LocalAddr.iSockaddrLength = sizeof( SOCKADDR_BTH ); lpCSAddrInfo[0].LocalAddr.lpSockaddr = (LPSOCKADDR)&SockAddrBthLocal; lpCSAddrInfo[0].RemoteAddr.iSockaddrLength = sizeof( SOCKADDR_BTH ); lpCSAddrInfo[0].RemoteAddr.lpSockaddr = (LPSOCKADDR)&SockAddrBthLocal; lpCSAddrInfo[0].iSocketType = SOCK_STREAM; lpCSAddrInfo[0].iProtocol = BTHPROTO_RFCOMM; // // If we got an address, go ahead and advertise it. // ZeroMemory(&wsaQuerySet, sizeof(WSAQUERYSET)); wsaQuerySet.dwSize = sizeof(WSAQUERYSET); wsaQuerySet.lpServiceClassId = (LPGUID) &g_guidServiceClass; // // Adding a byte to the size to account for the space in the // format string in the swprintf call. This will have to change if converted // to UNICODE // res = StringCchLength(szThisComputerName, sizeof(szThisComputerName), &cbInstanceNameSize); if( FAILED(res) ) { wprintf(L"-FATAL- | ComputerName specified is too large\n"); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { cbInstanceNameSize += sizeof(CXN_INSTANCE_STRING) + 1; pszInstanceName = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbInstanceNameSize); if ( NULL == pszInstanceName ) { wprintf(L"-FATAL- | HeapAlloc failed | out of memory | gle = [%d] \n", GetLastError()); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { StringCbPrintf(pszInstanceName, cbInstanceNameSize, L"%s %s", szThisComputerName, CXN_INSTANCE_STRING); wsaQuerySet.lpszServiceInstanceName = pszInstanceName; wsaQuerySet.lpszComment = L"Example Service instance registered in the directory service through RnR"; wsaQuerySet.dwNameSpace = NS_BTH; wsaQuerySet.dwNumberOfCsAddrs = 1; // Must be 1. wsaQuerySet.lpcsaBuffer = lpCSAddrInfo; // Req'd. // // As long as we use a blocking accept(), we will have a race // between advertising the service and actually being ready to // accept connections. If we use non-blocking accept, advertise // the service after accept has been called. // if ( SOCKET_ERROR == WSASetService(&wsaQuerySet, RNRSERVICE_REGISTER, 0) ) { wprintf(L"=CRITICAL= | WSASetService() call failed. WSAGetLastError=[%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; } } // // listen() call indicates winsock2 to listen on a given socket for any incoming connection. // if ( CXN_SUCCESS == ulRetCode ) { if ( SOCKET_ERROR == listen(LocalSocket, CXN_DEFAULT_LISTEN_BACKLOG) ) { wprintf(L"=CRITICAL= | listen() call failed w/socket = [0x%I64X]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, WSAGetLastError()); ulRetCode = CXN_ERROR; } } if ( CXN_SUCCESS == ulRetCode ) { for ( iCxnCount = 0; (CXN_SUCCESS == ulRetCode) && ( (iCxnCount < iMaxCxnCycles) || (iMaxCxnCycles == 0) ); iCxnCount++ ) { wprintf(L"\n"); // // accept() call indicates winsock2 to wait for any // incoming connection request from a remote socket. // If there are already some connection requests on the queue, // then accept() extracts the first request and creates a new socket and // returns the handle to this newly created socket. This newly created // socket represents the actual connection that connects the two sockets. // ClientSocket = accept(LocalSocket, NULL, NULL); if ( INVALID_SOCKET == ClientSocket ) { wprintf(L"=CRITICAL= | accept() call failed. WSAGetLastError=[%d]\n", WSAGetLastError()); ulRetCode = CXN_ERROR; break; // Break out of the for loop } // // Read data from the incoming stream // BOOL bContinue = TRUE; pszDataBuffer = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CXN_TRANSFER_DATA_LENGTH); if ( NULL == pszDataBuffer ) { wprintf(L"-FATAL- | HeapAlloc failed | out of memory | gle = [%d] \n", GetLastError()); ulRetCode = CXN_ERROR; break; } pszDataBufferIndex = pszDataBuffer; uiTotalLengthReceived = 0; while ( bContinue && (uiTotalLengthReceived < CXN_TRANSFER_DATA_LENGTH) ) { // // recv() call indicates winsock2 to receive data // of an expected length over a given connection. // recv() may not be able to get the entire length // of data at once. In such case the return value, // which specifies the number of bytes received, // can be used to calculate how much more data is // pending and accordingly recv() can be called again. // iLengthReceived = recv(ClientSocket, (char *)pszDataBufferIndex, (CXN_TRANSFER_DATA_LENGTH - uiTotalLengthReceived), 0); switch ( iLengthReceived ) { case 0: // socket connection has been closed gracefully bContinue = FALSE; break; case SOCKET_ERROR: wprintf(L"=CRITICAL= | recv() call failed. WSAGetLastError=[%d]\n", WSAGetLastError()); bContinue = FALSE; ulRetCode = CXN_ERROR; break; default: // // Make sure we have enough room // if ( iLengthReceived > (CXN_TRANSFER_DATA_LENGTH - uiTotalLengthReceived)) { wprintf(L"=CRITICAL= | received too much data\n"); bContinue = FALSE; ulRetCode = CXN_ERROR; break; } pszDataBufferIndex += iLengthReceived; uiTotalLengthReceived += iLengthReceived; break; } } if ( CXN_SUCCESS == ulRetCode ) { if ( CXN_TRANSFER_DATA_LENGTH != uiTotalLengthReceived ) { wprintf(L"+WARNING+ | Data transfer aborted mid-stream. Expected Length = [%I64u], Actual Length = [%d]\n", (ULONG64)CXN_TRANSFER_DATA_LENGTH, uiTotalLengthReceived); } wprintf(L"*INFO* | Received following data string from remote device:\n%s\n", (wchar_t *)pszDataBuffer); // // Close the connection // if ( SOCKET_ERROR == closesocket(ClientSocket) ) { wprintf(L"=CRITICAL= | closesocket() call failed w/socket = [0x%I64X]. WSAGetLastError=[%d]\n", (ULONG64)LocalSocket, WSAGetLastError()); ulRetCode = CXN_ERROR; } else { // // Make the connection invalid regardless // ClientSocket = INVALID_SOCKET; } } } } if ( INVALID_SOCKET != ClientSocket ) { closesocket(ClientSocket); ClientSocket = INVALID_SOCKET; } if ( INVALID_SOCKET != LocalSocket ) { closesocket(LocalSocket); LocalSocket = INVALID_SOCKET; } if ( NULL != lpCSAddrInfo ) { HeapFree(GetProcessHeap(), 0, lpCSAddrInfo); lpCSAddrInfo = NULL; } if ( NULL != pszInstanceName ) { HeapFree(GetProcessHeap(), 0, pszInstanceName); pszInstanceName = NULL; } if ( NULL != pszDataBuffer ) { HeapFree(GetProcessHeap(), 0, pszDataBuffer); pszDataBuffer = NULL; } return(ulRetCode); } // // ShowCmdLineSyntaxHelp displays the command line usage // void ShowCmdLineHelp(void) { wprintf( L"\n Bluetooth Connection Sample application for demonstrating connection and data transfer." L"\n" L"\n" L"\n BTHCxn.exe [-n | -a] " L"\n [-c]" L"\n" L"\n" L"\n Switches applicable for Client mode:" L"\n -n Specifies name of remote BlueTooth-Device." L"\n" L"\n -a Specifies address of remote BlueTooth-Device." L"\n The address is in form XX:XX:XX:XX:XX:XX" L"\n where XX is a hexidecimal byte" L"\n" L"\n One of the above two switches is required for client." L"\n" L"\n" L"\n Switches applicable for both Client and Server mode:" L"\n -c Specifies number of connection cycles." L"\n Default value for this parameter is 1. Specify 0 to " L"\n run infinite number of connection cycles." L"\n" L"\n" L"\n" L"\n Command Line Examples:" L"\n \"BTHCxn.exe -c0\"" L"\n Runs the BTHCxn server for infinite connection cycles." L"\n The application reports minimal information onto the cmd window." L"\n" L"\n \"BTHCxn.exe -nServerDevice -c50\"" L"\n Runs the BTHCxn client connecting to remote device (having name " L"\n \"ServerDevice\" for 50 connection cycles." L"\n The application reports minimal information onto the cmd window." L"\n" ); } // // ParseCmdLine parses the command line and sets the global variables accordingly. // It returns CXN_SUCCESS if successful and CXN_ERROR if it detected a mistake in the // command line parameter used. // ULONG ParseCmdLine (_In_ int argc, _In_reads_(argc) wchar_t * argv[]) { size_t cbStrLen = 0; ULONG ulRetCode = CXN_SUCCESS; HRESULT res; for ( int i = 1; i < argc; i++ ) { wchar_t * pszToken = argv[i]; if ( *pszToken == L'-' || *pszToken == L'/' ) { wchar_t token; // // skip over the "-" or "/" // pszToken++; // // Get the command line option // token = *pszToken; // // Go one past the option the option-data // pszToken++; // // Get the option-data // switch ( token ) { case L'n': cbStrLen = wcslen(pszToken); if ( ( 0 < cbStrLen ) && ( BTH_MAX_NAME_SIZE >= cbStrLen ) ) { res = StringCbCopy(g_szRemoteName, sizeof(g_szRemoteName), pszToken); if ( FAILED(res) ) { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Unable to parse -n, length error (min 1 char, max %d chars)\n", BTH_MAX_NAME_SIZE); } } else { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Unable to parse -n, length error (min 1 char, max %d chars)\n", BTH_MAX_NAME_SIZE); } break; case L'a': cbStrLen = wcslen(pszToken); if ( CXN_BDADDR_STR_LEN == cbStrLen ) { res = StringCbCopy(g_szRemoteAddr, sizeof(g_szRemoteAddr), pszToken); if ( FAILED(res) ) { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Unable to parse -a, Remote bluetooth radio address string length expected %d | Found: %I64u)\n", CXN_BDADDR_STR_LEN, (ULONG64)cbStrLen); } } else { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Unable to parse -a, Remote bluetooth radio address string length expected %d | Found: %I64u)\n", CXN_BDADDR_STR_LEN, (ULONG64)cbStrLen); } break; case L'c': if ( 0 < wcslen(pszToken) ) { swscanf_s(pszToken, L"%d", &g_ulMaxCxnCycles); if ( 0 > g_ulMaxCxnCycles ) { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Must provide +ve or 0 value with -c option\n"); } } else { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Must provide a value with -c option\n"); } break; case L'?': case L'h': case L'H': default: ulRetCode = CXN_ERROR; } } else { ulRetCode = CXN_ERROR; wprintf(L"!ERROR! | cmd line | Bad option prefix, use '/' or '-' \n"); } } return(ulRetCode); }