Click here to Skip to main content
15,881,424 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I am a newbie in Windows application development. Currently I'm working on a project to write a communication layer using Bluetooth low energy which should work on both windows 8.1 and windows 10. Below is the code that I used to run to list the BLE services,

C++
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
#include <bthdef.h>
#include <bluetoothleapis.h>
#pragma comment(lib, "SetupAPI")
#pragma comment(lib, "BluetoothApis.lib")
#define TO_SEARCH_DEVICE_UUID "{0000b81d-0000-1000-8000-00805f9b34fb}" 


void EventNotif(BTH_LE_GATT_EVENT_TYPE EventType, PVOID EventOutParameter, PVOID Context)
{
    printf("notification obtained ");
    PBLUETOOTH_GATT_VALUE_CHANGED_EVENT ValueChangedEventParameters = (PBLUETOOTH_GATT_VALUE_CHANGED_EVENT)EventOutParameter;

    HRESULT hr;
    if (0 == ValueChangedEventParameters->CharacteristicValue->DataSize) {
        hr = E_FAIL;
        printf("datasize 0\n");
    }
    else {
        printf("char ");
        unsigned heart_rate;
        if (0x01 == (ValueChangedEventParameters->CharacteristicValue->Data[0] & 0x01)) {
            heart_rate = ValueChangedEventParameters->CharacteristicValue->Data[1] * 256 + ValueChangedEventParameters->CharacteristicValue->Data[2];
        }
        else {
            heart_rate = ValueChangedEventParameters->CharacteristicValue->Data[1];
        }
        printf("%d\n", heart_rate);
    }
}

HANDLE GetBLEHandle(__in GUID AGuid)
{
    HDEVINFO hDI;
    SP_DEVICE_INTERFACE_DATA did;
    SP_DEVINFO_DATA dd;
    GUID BluetoothInterfaceGUID = AGuid;
    HANDLE hComm = NULL;

    hDI = SetupDiGetClassDevs(&BluetoothInterfaceGUID, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);

    if (hDI == INVALID_HANDLE_VALUE) return NULL;

    did.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
    dd.cbSize = sizeof(SP_DEVINFO_DATA);

    for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDI, NULL, &BluetoothInterfaceGUID, i, &did); i++)
    {
        SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;

        DeviceInterfaceDetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

        DWORD size = 0;

        if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, NULL, 0, &size, 0))
        {
            int err = GetLastError();

            if (err == ERROR_NO_MORE_ITEMS) break;

            PSP_DEVICE_INTERFACE_DETAIL_DATA pInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)GlobalAlloc(GPTR, size);

            pInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

            if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, pInterfaceDetailData, size, &size, &dd))
                break;

            hComm = CreateFile(
                pInterfaceDetailData->DevicePath,
                GENERIC_WRITE | GENERIC_READ,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                NULL,
                OPEN_EXISTING,
                0,
                NULL);

            GlobalFree(pInterfaceDetailData);
        }
    }

    SetupDiDestroyDeviceInfoList(hDI);
    return hComm;
}


int main(int argc, char *argv[], char *envp[])
{

    GUID AGuid;
    CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);

    HANDLE hLEDevice = GetBLEHandle(AGuid);


    USHORT serviceBufferCount;


    HRESULT hr = BluetoothGATTGetServices(
        hLEDevice,
        0,
        NULL,
        &serviceBufferCount,
        BLUETOOTH_GATT_FLAG_NONE);

    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) {
        printf("BluetoothGATTGetServices - Buffer Size %d", hr);
    }

    PBTH_LE_GATT_SERVICE pServiceBuffer = (PBTH_LE_GATT_SERVICE)
        malloc(sizeof(BTH_LE_GATT_SERVICE) * serviceBufferCount);

    if (NULL == pServiceBuffer) {
        printf("pServiceBuffer out of memory\r\n");
    }
    else {
        RtlZeroMemory(pServiceBuffer,
            sizeof(BTH_LE_GATT_SERVICE) * serviceBufferCount);
    }



    USHORT numServices;
    hr = BluetoothGATTGetServices(
        hLEDevice,
        serviceBufferCount,
        pServiceBuffer,
        &numServices,
        BLUETOOTH_GATT_FLAG_NONE);

    if (S_OK != hr) {
        printf("BluetoothGATTGetServices - Buffer Size %d", hr);
    }





    USHORT charBufferSize;
    hr = BluetoothGATTGetCharacteristics(
        hLEDevice,
        pServiceBuffer,
        0,
        NULL,
        &charBufferSize,
        BLUETOOTH_GATT_FLAG_NONE);

    if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) {
        printf("BluetoothGATTGetCharacteristics - Buffer Size %d", hr);
    }

    PBTH_LE_GATT_CHARACTERISTIC pCharBuffer;
    if (charBufferSize > 0) {
        pCharBuffer = (PBTH_LE_GATT_CHARACTERISTIC)
            malloc(charBufferSize * sizeof(BTH_LE_GATT_CHARACTERISTIC));

        if (NULL == pCharBuffer) {
            printf("pCharBuffer out of memory\r\n");
        }
        else {
            RtlZeroMemory(pCharBuffer,
                charBufferSize * sizeof(BTH_LE_GATT_CHARACTERISTIC));
        }

        USHORT numChars;
        hr = BluetoothGATTGetCharacteristics(
            hLEDevice,
            pServiceBuffer,
            charBufferSize,
            pCharBuffer,
            &numChars,
            BLUETOOTH_GATT_FLAG_NONE);

        if (S_OK != hr) {
            printf("BluetoothGATTGetCharacteristics - Actual Data %d", hr);
        }

        if (numChars != charBufferSize) {
            printf("buffer size and buffer size actual size mismatch\r\n");
        }
    }




    PBTH_LE_GATT_CHARACTERISTIC currGattChar;
    for (int ii = 0; ii <charBufferSize; ii++) {
        currGattChar = &pCharBuffer[ii];
        USHORT charValueDataSize;
        PBTH_LE_GATT_CHARACTERISTIC_VALUE pCharValueBuffer;



        USHORT descriptorBufferSize;
        hr = BluetoothGATTGetDescriptors(
            hLEDevice,
            currGattChar,
            0,
            NULL,
            &descriptorBufferSize,
            BLUETOOTH_GATT_FLAG_NONE);

        if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) {
            printf("BluetoothGATTGetDescriptors - Buffer Size %d", hr);
        }

        PBTH_LE_GATT_DESCRIPTOR pDescriptorBuffer;
        if (descriptorBufferSize > 0) {
            pDescriptorBuffer = (PBTH_LE_GATT_DESCRIPTOR)
                malloc(descriptorBufferSize
                    * sizeof(BTH_LE_GATT_DESCRIPTOR));

            if (NULL == pDescriptorBuffer) {
                printf("pDescriptorBuffer out of memory\r\n");
            }
            else {
                RtlZeroMemory(pDescriptorBuffer, descriptorBufferSize);
            }

            ////////////////////////////////////////////////////////////////////////////
            // Retrieve Descriptors
            ////////////////////////////////////////////////////////////////////////////

            USHORT numDescriptors;
            hr = BluetoothGATTGetDescriptors(
                hLEDevice,
                currGattChar,
                descriptorBufferSize,
                pDescriptorBuffer,
                &numDescriptors,
                BLUETOOTH_GATT_FLAG_NONE);

            if (S_OK != hr) {
                printf("BluetoothGATTGetDescriptors - Actual Data %d", hr);
            }

            if (numDescriptors != descriptorBufferSize) {
                printf("buffer size and buffer size actual size mismatch\r\n");
            }

            for (int kk = 0; kk<numDescriptors; kk++) {
                PBTH_LE_GATT_DESCRIPTOR  currGattDescriptor = &pDescriptorBuffer[kk];
                ////////////////////////////////////////////////////////////////////////////
                // Determine Descriptor Value Buffer Size
                ////////////////////////////////////////////////////////////////////////////
                USHORT descValueDataSize;
                hr = BluetoothGATTGetDescriptorValue(
                    hLEDevice,
                    currGattDescriptor,
                    0,
                    NULL,
                    &descValueDataSize,
                    BLUETOOTH_GATT_FLAG_NONE);

                if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) {
                    printf("BluetoothGATTGetDescriptorValue - Buffer Size %d", hr);
                }

                PBTH_LE_GATT_DESCRIPTOR_VALUE pDescValueBuffer = (PBTH_LE_GATT_DESCRIPTOR_VALUE)malloc(descValueDataSize);

                if (NULL == pDescValueBuffer) {
                    printf("pDescValueBuffer out of memory\r\n");
                }
                else {
                    RtlZeroMemory(pDescValueBuffer, descValueDataSize);
                }

                ////////////////////////////////////////////////////////////////////////////
                // Retrieve the Descriptor Value
                ////////////////////////////////////////////////////////////////////////////

                hr = BluetoothGATTGetDescriptorValue(
                    hLEDevice,
                    currGattDescriptor,
                    (ULONG)descValueDataSize,
                    pDescValueBuffer,
                    NULL,
                    BLUETOOTH_GATT_FLAG_NONE);
                if (S_OK != hr) {
                    printf("BluetoothGATTGetDescriptorValue - Actual Data %d", hr);
                }

                if (currGattDescriptor->AttributeHandle < 255) {
                    BTH_LE_GATT_DESCRIPTOR_VALUE newValue;

                    RtlZeroMemory(&newValue, sizeof(newValue));

                    newValue.DescriptorType = ClientCharacteristicConfiguration;
                    newValue.ClientCharacteristicConfiguration.IsSubscribeToNotification = TRUE;

                    hr = BluetoothGATTSetDescriptorValue(
                        hLEDevice,
                        currGattDescriptor,
                        &newValue,
                        BLUETOOTH_GATT_FLAG_NONE);
                    if (S_OK != hr) {
                        printf("BluetoothGATTGetDescriptorValue - Actual Data %d", hr);
                    }
                    else {
                        printf("setting notification for serivice handle %d\n", currGattDescriptor->ServiceHandle);
                    }

                }

            }
        }


        BLUETOOTH_GATT_EVENT_HANDLE EventHandle;

        if (currGattChar->IsNotifiable) {
            printf("Setting Notification for ServiceHandle %d\n", currGattChar->ServiceHandle);
            BTH_LE_GATT_EVENT_TYPE EventType = CharacteristicValueChangedEvent;

            BLUETOOTH_GATT_VALUE_CHANGED_EVENT_REGISTRATION EventParameterIn;
            EventParameterIn.Characteristics[0] = *currGattChar;
            EventParameterIn.NumCharacteristics = 1;
            //hr = BluetoothGATTRegisterEvent(
            //hLEDevice,
            //EventType,
            //&EventParameterIn,
            //EventNotif,
            //NULL,
            //&EventHandle,
            //BLUETOOTH_GATT_FLAG_NONE);

            if (S_OK != hr) {
                printf("BluetoothGATTRegisterEvent - Actual Data %d", hr);
            }
        }


        if (currGattChar->IsReadable) {
            hr = BluetoothGATTGetCharacteristicValue(
                hLEDevice,
                currGattChar,
                0,
                NULL,
                &charValueDataSize,
                BLUETOOTH_GATT_FLAG_NONE);

            if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) != hr) {
                printf("BluetoothGATTGetCharacteristicValue - Buffer Size %d", hr);
            }

            pCharValueBuffer = (PBTH_LE_GATT_CHARACTERISTIC_VALUE)malloc(charValueDataSize);

            if (NULL == pCharValueBuffer) {
                printf("pCharValueBuffer out of memory\r\n");
            }
            else {
                RtlZeroMemory(pCharValueBuffer, charValueDataSize);
            }

            hr = BluetoothGATTGetCharacteristicValue(
                hLEDevice,
                currGattChar,
                (ULONG)charValueDataSize,
                pCharValueBuffer,
                NULL,
                BLUETOOTH_GATT_FLAG_NONE);

            if (S_OK != hr) {
                printf("BluetoothGATTGetCharacteristicValue - Actual Data %d", hr);
            }

            //print the characeteristic Value

            printf("\n Printing a read (not notifiable) characterstic (maybe) body sensor value");
            for (int iii = 0; iii< pCharValueBuffer->DataSize; iii++) {// ideally check ->DataSize before printing
                printf("%d", pCharValueBuffer->Data[iii]);
            }
            printf("\n");


            free(pCharValueBuffer);
            pCharValueBuffer = NULL;
        }

    }


    while (1) {
        Sleep(1000);

        //printf("look for notification\n");
        UCHAR dataBuffer[sizeof(int)];
        BTH_LE_GATT_CHARACTERISTIC_VALUE newValue;

        newValue.DataSize = 4;
        newValue.Data[0] = 1234;



            if (hr != S_OK) {
                printf("Failed to write characteristic\n");
            }
        }

    CloseHandle(hLEDevice);



    if (GetLastError() != NO_ERROR &&
        GetLastError() != ERROR_NO_MORE_ITEMS)
    {

        return 1;
    }

    return 0;
}



The code compiled successfully and runs in debug mode. However the near by BLE services are not listed. Also when I close the application from debug mode I get the below errors in the debug console,

onecore\drivers\bluetooth\legacy\win32leadapter\client\lib\gattlegacyapiclient.cpp(164)\BluetoothApis.dll!5177642F: (caller: 51773B3A) ReturnHr(1) tid(2094) 80070006 The handle is invalid. onecore\drivers\bluetooth\legacy\win32leadapter\client\lib\gattlegacyapiclient.cpp(164)\BluetoothApis.dll!5177642F: (caller: 51773B3A) ReturnHr(2) tid(2094) 80070006 The handle is invalid. onecore\drivers\bluetooth\legacy\win32leadapter\client\lib\gattlegacyapiclient.cpp(308)\BluetoothApis.dll!51775956: (caller: 517739ED) ReturnHr(3) tid(2094) 80070006 The handle is invalid.


What I have tried:

My development environment is VS 2017 on Windows 10.

Please help me to sort out this issue
Posted
Updated 26-Jun-17 18:17pm
v4

You will probably never get a valid handle from GetBLEHandle. Your code in main() does not check the return value and the function itself is not working as expected:
if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, NULL, 0, &size, 0))
{
int err = GetLastError();
if (err == ERROR_NO_MORE_ITEMS) break;
// ...
In the above snippet you are checking for errors (SetupDiGetDeviceInterfaceDetail returns FALSE) but there is no else condition for the case of success.

You would need something like this:
C++
DWORD dwErr = 0;
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDI, NULL, &BluetoothInterfaceGUID, i, &did); i++)
{
    SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData;
    DeviceInterfaceDetailData.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
    DWORD size = 0;
    if (!SetupDiGetDeviceInterfaceDetail(hDI, &did, NULL, 0, &size, 0))
    {
        DWORD err = GetLastError();
        // Set main error if not just no more items
        if (err != ERROR_NO_MORE_ITEMS)
        {
            dwErr = err;
            TRACE1("SetupDiGetDeviceInterfaceDetail failed with error %d\n", err);
        }
        break;
    }
    // Further processing goes here
}
Note the TRACE statement that will inform you about errors when running from the debugger. Such are really helpful when something is not working as expected and printing out error codes. Alternatively run your application step by step from within the debugger and inspect your variables to see where something unexpected happens.

Note also that once you have fixed this bug your loop will open all found interfaces and returns only the handle for the last one. You should break the loop after having opened the first interface or store the names in an array and provide an oprtion to choose from the found interfaces.

There is also ne need to use GlobalAlloc here. Just use new and delete.

I have not checked all of the remaining code because the above errors are in the initial code portions and must be therefore checked first. If there are more use the debugger and/or enhanced error checks. While you are printing some error messages you should also return upon errors to avoid calling following functions with invalid parameters.
 
Share this answer
 
There is some bug in the API. Read this article at Stack overflow with some additional hints for an update.

Your code flow is buggy. Never re-use a handle multiple times, it is a unique sysetm resource.
And dont close a handle multiple time, but set the value to NULL when done!!!
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900