USB programming in C++
<p>I'm trying to build a DLL for a particular USB device. This DLL exports class by using abstract interface. I'm using setupapi.dll for USB device recognition, receiving and sending packets to device. I'm getting run time check error 0 for the imported functions. Multi Byte Character Set is being used by the way. Here is the relevant code (it's tad long so apologies):</p>

<pre><code>//Bikedll.h 
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;Windows.h&gt;    //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc.
#include &lt;Dbt.h&gt;        //Need this for definitions of WM_DEVICECHANGE messages
#include &lt;setupapi.h&gt;   
#include &lt;string&gt;
#include "AbstrClass.h"

using namespace std;

#define Cihaz_ID  "Vid_a0a1&amp;Pid_4147"    
#define __YAZI__ "Esetron USB Kart V1.0"

typedef GUID* LPGUID; 

typedef HDEVINFO ( *SetupDiGetClassDevsUMPTR)(LPGUID, PCTSTR, HWND, DWORD);
typedef WINSETUPAPI BOOL ( *SetupDiEnumDeviceInterfacesUMPTR)(HDEVINFO, PSP_DEVINFO_DATA, LPGUID, DWORD, PSP_DEVICE_INTERFACE_DATA);
typedef WINSETUPAPI BOOL ( *SetupDiDestroyDeviceInfoListUMPTR)(HDEVINFO);
typedef WINSETUPAPI BOOL ( *SetupDiEnumDeviceInfoUMPTR)(HDEVINFO, DWORD, PSP_DEVINFO_DATA);
typedef WINSETUPAPI BOOL ( *SetupDiGetDeviceRegistryPropertyUMPTR)(HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD);
typedef WINSETUPAPI BOOL ( *SetupDiSetDeviceRegistryPropertyUMPTR)(HDEVINFO, PSP_DEVINFO_DATA, DWORD, const BYTE*, DWORD);
typedef BOOL ( *SetupDiGetDeviceInterfaceDetailUMPTR)(HDEVINFO, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA, DWORD, PDWORD, PSP_DEVINFO_DATA);
typedef HDEVNOTIFY ( *RegisterDeviceNotificationUMPTR)(HANDLE, LPVOID, DWORD);

//Globally Unique Identifier (GUID) for HID class devices.  Windows uses GUIDs to identify things.
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; 

//USB variables
BOOL AttachedState = false;     
BOOL AttachedButBroken = false;                 
PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA;
HANDLE WriteHandleToUSBDevice = INVALID_HANDLE_VALUE;
HANDLE ReadHandleToUSBDevice = INVALID_HANDLE_VALUE;

unsigned char LED_durum = 0;
HANDLE  EP1INHandle = INVALID_HANDLE_VALUE;
HANDLE  EP1OUTHandle = INVALID_HANDLE_VALUE;
bool durum = false;

unsigned char gond_paket[65];   //Number of elements, including '/0'
unsigned char gelen_paket[65];      
DWORD Gond_say;             
DWORD Gelen_say;

class Bikedll : public AbstrClass
{
public:     
        SetupDiGetClassDevsUMPTR _SetupDiGetClassDevsUMPTR;
        SetupDiEnumDeviceInterfacesUMPTR _SetupDiEnumDeviceInterfacesUMPTR;
        SetupDiDestroyDeviceInfoListUMPTR _SetupDiDestroyDeviceInfoListUMPTR;
        SetupDiEnumDeviceInfoUMPTR _SetupDiEnumDeviceInfoUMPTR;
        SetupDiGetDeviceRegistryPropertyUMPTR _SetupDiGetDeviceRegistryPropertyUMPTR;
        SetupDiSetDeviceRegistryPropertyUMPTR _SetupDiSetDeviceRegistryPropertyUMPTR;
        SetupDiGetDeviceInterfaceDetailUMPTR _SetupDiGetDeviceInterfaceDetailUMPTR;
        RegisterDeviceNotificationUMPTR _RegisterDeviceNotificationUMPTR;

    DWORD ErrorStatusWrite;
    DWORD ErrorStatusRead;

    //Explicit Linking
    HINSTANCE hSetUpApi; 
    HINSTANCE hUser32;

    string get_POT();
    string get_RPM();

    Bikedll();
    BOOL CheckIfPresentAndGetUSBDevicePath(void)
    {
        HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE;
        PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA;
        SP_DEVINFO_DATA DevInfoData;

        DWORD InterfaceIndex = 0;
        DWORD StatusLastError = 0;
        DWORD dwRegType;
        DWORD dwRegSize;
        DWORD StructureSize = 0;
        PBYTE PropertyValueBuffer;
        bool MatchFound = false;
        DWORD ErrorStatus;
        BOOL BoolStatus = FALSE;
        DWORD LoopCounter = 0;

        string DeviceIDToFind = Cihaz_ID;   
        //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. 
        DeviceInfoTable = _SetupDiGetClassDevsUMPTR(&amp;InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); // Run Time Check Error 0 (AKA RTCE 0) type of errors start here

        //Now look through the list we just populated.  We are trying to see if any of them match our device. 
        while(true)
        {                                                                           
            InterfaceDataStructure-&gt;cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
            if(_SetupDiEnumDeviceInterfacesUMPTR(DeviceInfoTable, NULL, &amp;InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure)) //RTCE 0
            {
                ErrorStatus = GetLastError();
                if(ERROR_NO_MORE_ITEMS == GetLastError())   //Did we reach the end of the list of matching devices in the DeviceInfoTable?
                {   //Cound not find the device.  Must not have been attached.
                    _SetupDiDestroyDeviceInfoListUMPTR(DeviceInfoTable);    //Clean up the old structure we no longer need. Still RTCE 0
                    return FALSE;       
                }
            }
        else    //Else some other kind of unknown error ocurred...
        {
            ErrorStatus = GetLastError();
            _SetupDiDestroyDeviceInfoListUMPTR(DeviceInfoTable);    //Clean up the old structure we no longer need. RTCE 0
            return FALSE;   
        }
        //Now retrieve the hardware ID from the registry.  The hardware ID contains the VID and PID, which we will then 
        //check to see if it is the correct device or not.

        //Initialize an appropriate SP_DEVINFO_DATA structure.  We need this structure for SetupDiGetDeviceRegistryProperty().
        DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
        _SetupDiEnumDeviceInfoUMPTR(DeviceInfoTable, InterfaceIndex, &amp;DevInfoData); //RTCE 0

        //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data.
        _SetupDiGetDeviceRegistryPropertyUMPTR(DeviceInfoTable, &amp;DevInfoData, SPDRP_HARDWAREID, &amp;dwRegType, NULL, 0, &amp;dwRegSize); //RTCE 0

        //Allocate a buffer for the hardware ID.
        PropertyValueBuffer = (BYTE *) malloc (dwRegSize);
        if(PropertyValueBuffer == NULL) //if null, error, couldn't allocate enough memory
        {   //Can't really recover from this situation, just exit instead.
            _SetupDiDestroyDeviceInfoListUMPTR(DeviceInfoTable);    //Clean up the old structure we no longer need. RTCE 0
            return FALSE;       
        }

        //Retrieve the hardware IDs for the current device we are looking at.  PropertyValueBuffer gets filled with a 
        //REG_MULTI_SZ (array of null terminated strings).  To find a device, we only care about the very first string in the
        //buffer, which will be the "device ID".  The device ID is a string which contains the VID and PID, in the example 
        //format "Vid_04d8&amp;Pid_003f".
        _SetupDiGetDeviceRegistryPropertyUMPTR(DeviceInfoTable, &amp;DevInfoData, SPDRP_HARDWAREID, &amp;dwRegType, PropertyValueBuffer, dwRegSize, NULL); // RTCE 0

        //Now check if the first string in the hardware ID matches the device ID of my USB device.
        string* DeviceIDFromRegistry = new string((char *)PropertyValueBuffer);
        free(PropertyValueBuffer);      //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks

        //Now check if the hardware ID we are looking at contains the correct VID/PID
        if (std::string::npos != DeviceIDFromRegistry-&gt;find(DeviceIDToFind))
        {
            MatchFound = true;
        }
        if(MatchFound == true)
        {
            //Device must have been found.  Open WinUSB interface handle now.  In order to do this, we will need the actual device path first.
            //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice:  The first
            //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually 
            //get the structure (after we have allocated enough memory for the structure.)
            DetailedInterfaceDataStructure-&gt;cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
            //First call populates "StructureSize" with the correct value
            _SetupDiGetDeviceInterfaceDetailUMPTR(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &amp;StructureSize, NULL);   
            DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize));     //Allocate enough memory
            if(DetailedInterfaceDataStructure == NULL)  //if null, error, couldn't allocate enough memory
            {   //Can't really recover from this situation, just exit instead.
                _SetupDiDestroyDeviceInfoListUMPTR(DeviceInfoTable);    //Clean up the old structure we no longer need.
                return FALSE;       
            }
            DetailedInterfaceDataStructure-&gt;cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
            //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods.  
            _SetupDiGetDeviceInterfaceDetailUMPTR(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); 
            // Set Device Name
            BYTE *pbuf = new BYTE[sizeof(TEXT(__YAZI__))];
            pbuf = (BYTE*)TEXT(__YAZI__);
            _SetupDiSetDeviceRegistryPropertyUMPTR(DeviceInfoTable, &amp;DevInfoData, SPDRP_FRIENDLYNAME , pbuf, sizeof(TEXT(__YAZI__)));

            //We now have the proper device path, and we can finally open a device handle to the device.
            //WinUSB requires the device handle to be opened with the FILE_FLAG_OVERLAPPED attribute.
            _SetupDiDestroyDeviceInfoListUMPTR(DeviceInfoTable);    //Clean up the old structure we no longer need.
            return TRUE;
        }

            InterfaceIndex++;   
            //Keep looping until we either find a device with matching VID and PID, or until we run out of devices to check.
            //However, just in case some unexpected error occurs, keep track of the number of loops executed.
            //If the number of loops exceeds a very large number, exit anyway, to prevent inadvertent infinite looping.             
            LoopCounter++;
            if(LoopCounter == 10000000) //Surely there aren't more than 10 million devices attached to any forseeable PC...
            {
                return FALSE;
            }
        }

    }

protected:
/// &lt;summary&gt;
/// Clean up any resources being used.
/// &lt;/summary&gt;
~Bikedll()
{
    //Close the read/write handles, if they are currently open.
    if(AttachedState == TRUE)
    {
        CloseHandle(WriteHandleToUSBDevice);
        CloseHandle(ReadHandleToUSBDevice);
    }
}

};


string Bikedll::get_RPM(){
    gond_paket[0] = 0;      
    gond_paket[1] = 160;
    gond_paket[2] = 0;
    gond_paket[3] = 0;

    string str;

    for(unsigned char i = 4; i &lt;65; i++)
        gond_paket[i] = 0xFF;

    WriteFile(WriteHandleToUSBDevice, &amp;gond_paket, 65, &amp;Gond_say, 0);

    if (ReadFile(ReadHandleToUSBDevice,&amp;gelen_paket,65,&amp;Gelen_say,0))
    {
        if (gelen_paket[1] == 0xBA)
        {
            return str = gelen_paket[3];
        }
    }
}
string  Bikedll::get_POT(){
    gond_paket[0] = 0;      
    gond_paket[1] = 160;
    gond_paket[2] = 0;
    gond_paket[3] = 0;

    string str;

    for(unsigned char i = 4; i &lt;65; i++)
        gond_paket[i] = 0xFF;

    WriteFile(WriteHandleToUSBDevice, &amp;gond_paket, 65, &amp;Gond_say, 0);

    if (ReadFile(ReadHandleToUSBDevice,&amp;gelen_paket,65,&amp;Gelen_say,0))
    {
        if (gelen_paket[1] == 0xBA)
        {
            return str = gelen_paket[5];
        }
    }
}


Bikedll::Bikedll(){
hSetUpApi = LoadLibrary("setupapi.dll");
hUser32 = LoadLibrary("user32.dll");

_SetupDiGetClassDevsUMPTR = (SetupDiGetClassDevsUMPTR)GetProcAddress(hSetUpApi, "SetupDiGetClassDevsA");
_SetupDiEnumDeviceInterfacesUMPTR = (SetupDiEnumDeviceInterfacesUMPTR)GetProcAddress(hSetUpApi, "SetupDiEnumDeviceInterfaces");
_SetupDiDestroyDeviceInfoListUMPTR = (SetupDiDestroyDeviceInfoListUMPTR)GetProcAddress(hSetUpApi, "SetupDiDestroyDeviceInfoList");
_SetupDiEnumDeviceInfoUMPTR = (SetupDiEnumDeviceInfoUMPTR)GetProcAddress(hSetUpApi, "SetupDiEnumDeviceInfo");
_SetupDiGetDeviceRegistryPropertyUMPTR = (SetupDiGetDeviceRegistryPropertyUMPTR)GetProcAddress(hSetUpApi, "SetupDiGetDeviceRegistryPropertyA");
_SetupDiSetDeviceRegistryPropertyUMPTR = (SetupDiSetDeviceRegistryPropertyUMPTR)GetProcAddress(hSetUpApi, "SetupDiSetDeviceRegistryPropertyA");
_SetupDiGetDeviceInterfaceDetailUMPTR = (SetupDiGetDeviceInterfaceDetailUMPTR)GetProcAddress(hSetUpApi, "SetupDiGetDeviceInterfaceDetailA");

DEV_BROADCAST_DEVICEINTERFACE MyDeviceBroadcastHeader;// = new DEV_BROADCAST_HDR;
MyDeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
MyDeviceBroadcastHeader.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
MyDeviceBroadcastHeader.dbcc_reserved = 0;  //Reserved says not to use...
MyDeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;

if(CheckIfPresentAndGetUSBDevicePath()) //Look for the device
{
    DWORD ErrorStatusWrite;
    DWORD ErrorStatusRead;

    WriteHandleToUSBDevice = CreateFile(DetailedInterfaceDataStructure-&gt;DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
    ErrorStatusWrite = GetLastError();
    ReadHandleToUSBDevice = CreateFile(DetailedInterfaceDataStructure-&gt;DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
    ErrorStatusRead = GetLastError();

    if((ErrorStatusWrite == ERROR_SUCCESS) &amp;&amp; (ErrorStatusRead == ERROR_SUCCESS))
    {
        AttachedState = TRUE;       
        AttachedButBroken = FALSE;
        //okay
    }

    else //reading or writing operations failed
    {
        AttachedState = FALSE;      
        AttachedButBroken = TRUE;   
        if(ErrorStatusWrite == ERROR_SUCCESS)//Açık bağlantıları kapat
            CloseHandle(WriteHandleToUSBDevice);
        if(ErrorStatusRead == ERROR_SUCCESS)
            CloseHandle(ReadHandleToUSBDevice);
        //err1();                    
    }
}
else    
{
    AttachedState = FALSE;
    AttachedButBroken = FALSE;
    //err2();
}
}

//Interface to be exported with usable virtual functions
#include &lt;string&gt;

class AbstrClass{
public:
virtual std::string get_POT() = 0;
virtual std::string get_RPM() = 0;
};

// Bikedll.cpp : Defines the exported functions for the DLL application.

#include "stdafx.h"
#include "Bikedll.h"

extern "C" __declspec(dllexport) AbstrClass* __cdecl create_AbstrClass()
{
    return new Bikedll;
}

// TestdllBike.cpp : Program to test Bikedll
#include "stdafx.h"
#include "AbstrClass.h"

#include &lt;Windows.h&gt;
#include &lt;iostream&gt;

using namespace std;

typedef AbstrClass* (__cdecl *abstrClass_factory)();

int main()
{
HINSTANCE dll_handle = ::LoadLibrary(TEXT("Bikedll.dll"));
if(!dll_handle){
    cerr &lt;&lt; "Installation failed!";
    return 1;
}

abstrClass_factory factory_func = reinterpret_cast&lt;abstrClass_factory&gt;(::GetProcAddress(dll_handle, "create_AbstrClass"));

if (!factory_func) {
    cerr &lt;&lt; "function was not imported from dll!\n";
    ::FreeLibrary(dll_handle);
    return 1;
}

AbstrClass* instance = factory_func();

string t = instance-&gt;get_POT();
cout &lt;&lt; "POT : " &lt;&lt; t &lt;&lt; endl;
string v = instance-&gt;get_RPM();
cout &lt;&lt; "RPM : " &lt;&lt; v &lt;&lt; endl;

return 0;
}
</code></pre>
