modWinUSB Source Code

'Purpose: 

'   Windows DDK WinUSB API and Setup API Converted to Visual Basic 5/6

'       Note:   WinUSB Index represents CurrentControlSet device index

'               MultiUSB Index represents Index of stored WinUsbDevice for multiple DPP devices


Option Explicit 

'#Const bEnableDeviceNotification = 0
#Const bEnableDeviceNotification = 1 

'Purpose: MultiUSB Index to DPP Serial Number storage type.
Public Type MUSB_SN_TO_INDEX 
    Index As Long   'WinUSB Device Information Array Index for Device
    SerNum As Long  'DPP Device Serial Number
End Type 

Public MUSB_MAX_INDEX As Long           'max index is 4 for five devices 0-4
Public MUSB_LAST_INDEX As Long          'last index is read fron vbDP5_MultiUSB.ini file
Public MUSB_SN(4) As Long               'multiusb serial numbers read from file
Public MUSB_Idx(4) As MUSB_SN_TO_INDEX  'multiusb serial numbers read from file

Public CurrentUSBDevice As Long         'WinUSB
Public NumUSBDevices As Long 

'Purpose: Enumerates the error levels returned by ProcessError.
Public Type Message 
    hwnd As Long    'Handle of window.
    Msg As Long     'Message.
    wParam As Long  'First message parameter.
    lParam As Long  'Second message parameter.
End Type 

Public Enum ERRORLEVEL 
    elevCodeReturn          'Return error code.
    elevDescReturn          'Return error description string.
    elevSrcDescReturn       'Return error description string and error source string.
    elevMsgBox              'Return/Display error description string and error source string.
End Enum 

'Purpose: CopyMemory (RtlMoveMemory) moves memory either forward or backward, _
'         aligned or unaligned, in 4-byte blocks, followed by any remaining bytes.
'

'

'Remarks: The (Source + Length) can overlap the Destination range passed in to CopyMemory (RtlMoveMemory).
'

'

'Parameter: Destination Points to the destination of the move.
'Parameter: Source      Points to the memory to be copied.
'Parameter: Length      Specifies the number of bytes to be copied.
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As AnyByVal Length As Long)
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As LongByVal hwnd As LongByVal Msg As LongByVal wParam As LongByVal lParam As LongAs Long 
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As LongByVal nIndex As LongAs Long 
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As LongByVal nIndex As LongByVal dwNewLong As LongAs Long 
Public Const GWL_WNDPROC = (-4)
Public glngPrevWndProc As Long 
Public Declare Function RtlMoveMemory Lib "kernel32" (Dest As Any, src As AnyByVal Count As LongAs Long 
Public Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As LongByRef lpSource As AnyByVal dwMessageId As LongByVal dwLanguageZId As LongByVal lpBuffer As StringByVal nSize As LongByVal Arguments As LongAs Long 

Public Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 

Public Const DBT_DEVICEARRIVAL As Long = 32768 
Public Const DBT_DEVICEREMOVECOMPLETE As Long = 32772 
Public Const DBT_DEVTYP_DEVICEINTERFACE     As Long = 5 
Public Const DEVICE_NOTIFY_WINDOW_HANDLE As Long = 0 
Public Const WM_DEVICECHANGE As Long = &H219 
Public Const DIGCF_PRESENT As Integer = &H2 
Public Const DIGCF_DEVICEINTERFACE As Integer = &H10 

Public Type Guid 
    D1 As Long 
    D2 As Integer 
    D3 As Integer 
    D4(7) As Byte 
End Type 

Public Type DEV_BROADCAST_DEVICEINTERFACE 
    dbcc_size As Long 
    dbcc_devicetype As Long 
    dbcc_reserved As Long 
    dbcc_classguid As Guid 
    dbcc_name As Long 
End Type 

Public Type DEV_BROADCAST_DEVICEINTERFACE2 
  dbcc_size As Long 
  dbcc_devicetype As Long 
  dbcc_reserved As Long 
  dbcc_classguid As Guid 
  dbcc_name As String * 1024 
End Type 

Public Type DEV_BROADCAST_HDR 
    dbch_size As Long 
    dbch_devicetype As Long 
    dbch_reserved As Long 
End Type 

Public Type SP_DEVICE_INTERFACE_DATA 
    cbSize As Long 
    InterfaceClassGuid As Guid 
    Flags As Long 
    Reserved As Long 
End Type 

Public Type SP_DEVICE_INTERFACE_DETAIL_DATA 
    cbSize As Long 
    DevicePath As Byte 
    'DataPath(256) As Byte
End Type 

Public Declare Function SetupDiDestroyDeviceInfoList Lib "setupapi.dll" (ByVal DeviceInfoSet As LongAs Long 
Public Declare Function SetupDiEnumDeviceInterfaces Lib "setupapi.dll" (ByVal DeviceInfoSet As LongByVal DeviceInfoData As LongByRef InterfaceClassGuid As Guid, ByVal MemberIndex As LongByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) As Boolean 
Public Declare Function SetupDiGetClassDevs Lib "setupapi.dll" Alias "SetupDiGetClassDevsA" (ByRef ClassGuid As Guid, ByVal Enumerator As StringByVal hwndParent As LongByVal Flags As LongAs Long 
Public Declare Function SetupDiGetDeviceInterfaceDetail Lib "setupapi.dll" Alias "SetupDiGetDeviceInterfaceDetailA" (ByVal DeviceInfoSet As LongByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, ByVal DeviceInterfaceDetailData As LongByVal DeviceInterfaceDetailDataSize As LongByRef RequiredSize As LongByVal DeviceInfoData As LongAs Boolean 
Public Declare Function RegisterDeviceNotification Lib "user32.dll" Alias "RegisterDeviceNotificationA" (ByVal hRecipient As Long, NotificationFilter As AnyByVal Flags As LongAs Long 
Public Declare Function UnregisterDeviceNotification Lib "user32.dll" (ByVal Handle As LongAs Boolean 

Public Const FILE_ATTRIBUTE_NORMAL As Integer = &H80 
Public Const FILE_FLAG_OVERLAPPED As Long = &H40000000 
Public Const FILE_SHARE_READ As Integer = &H1 
Public Const FILE_SHARE_WRITE As Integer = &H2 
Public Const GENERIC_READ As Long = &H80000000 
Public Const GENERIC_WRITE As Long = &H40000000 
Public Const OPEN_EXISTING As Integer = 3 

Public Type SECURITY_ATTRIBUTES 
    nLength As Long 
    lpSecurityDescriptor As Long 
    bInheritHandle As Long 
End Type 

Public Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As LongAs Boolean 
Public Declare Function CreateFile Lib "kernel32.dll" Alias "CreateFileA" (ByVal lpFileName As StringByVal dwDesiredAccess As LongByVal dwShareMode As LongByRef lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As LongByVal dwFlagsAndAttributes As LongByVal hTemplateFile As LongAs Long 

Public Enum PIPE_POLICY_TYPE 
    SHORT_PACKET_TERMINATE = &H1 
    AUTO_CLEAR_STALL 
    PIPE_TRANSFER_TIMEOUT 
    IGNORE_SHORT_PACKETS 
    ALLOW_PARTIAL_READS 
    AUTO_FLUSH 
    RAW_IO 
    MAXIMUM_TRANSFER_SIZE 
End Enum 

Public Type WINUSB_PIPE_INFORMATION 
    PipeType As USBD_PIPE_TYPE 
    PipeId As Byte 
    MaximumPacketSize As Integer 
    Interval As Byte 
End Type 

Public Enum USBD_PIPE_TYPE 
    UsbdPipeTypeControl 
    UsbdPipeTypeIsochronous 
    UsbdPipeTypeBulk 
    UsbdPipeTypeInterrupt 
End Enum 

Public Type USB_INTERFACE_DESCRIPTOR 
    bLength As Byte 
    bDescriptorType As Byte 
    bInterfaceNumber As Byte 
    bAlternateSetting As Byte 
    bNumEndpoints As Byte 
    bInterfaceClass As Byte 
    bInterfaceSubClass As Byte 
    bInterfaceProtocol As Byte 
    iInterface As Byte 
End Type 

Public Declare Function WinUsb_Initialize Lib "winusb.dll" (ByVal DeviceHandle As LongByRef InterfaceHandle As LongAs Boolean 
Public Declare Function WinUsb_Free Lib "winusb.dll" (ByVal InterfaceHandle As LongAs Boolean 
Public Declare Function WinUsb_QueryInterfaceSettings Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal AlternateInterfaceNumber As ByteByRef UsbAltInterfaceDescriptor As USB_INTERFACE_DESCRIPTOR) As Boolean 
Public Declare Function WinUsb_QueryPipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal AlternateInterfaceNumber As ByteByVal PipeIndex As ByteByRef PipeInformation As WINUSB_PIPE_INFORMATION) As Boolean 
Public Declare Function WinUsb_SetPipePolicy Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteByVal PolicyType As LongByVal ValueLength As LongByRef Value As ByteAs Boolean 
Public Declare Function WinUsb_SetPipePolicy1 Lib "winusb.dll" Alias "WinUsb_SetPipePolicy" (ByVal InterfaceHandle As LongByVal PipeId As ByteByVal PolicyType As LongByVal ValueLength As LongByRef Value As LongAs Boolean 
Public Declare Function WinUsb_ReadPipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteByRef Buffer As ByteByVal BufferLength As LongByRef LengthTransferred As LongByVal Overlapped As LongAs Boolean 
Public Declare Function WinUsb_WritePipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteByRef Buffer As ByteByVal BufferLength As LongByRef LengthTransferred As LongByVal Overlapped As LongAs Boolean 
Public Declare Function WinUsb_ResetPipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteAs Boolean 
Public Declare Function WinUsb_AbortPipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteAs Boolean 
Public Declare Function WinUsb_FlushPipe Lib "winusb.dll" (ByVal InterfaceHandle As LongByVal PipeId As ByteAs Boolean 

'Purpose: DPP WinUSB device information data type.
Public Type WinUsbDevice 
    DeviceHandle As Long        'Communications device resource handle.
    winUsbHandle As Long        'WinUSB interface handle.
    bulkInPipe As Long          'Bulk IN pipe id.
    bulkOutPipe As Long         'Bulk OUT pipe id.
    interruptInPipe As Long     'Interrupt IN pipe id.
    interruptOutPipe As Long    'Interrupt OUT pipe id.
    devicespeed As Long         'Device speed indicator.
    SerNum As Long              'dpp serial number
    idxUSB As Long              'WinUSB device index (1-NumDevices)
    idxMUSB As Long             'MUSB array index
End Type 

Public DppWinUSB As WinUsbDevice     'DppWinUSB device information
Public EmptyWinUSB As WinUsbDevice     'DppWinUSB device information
Public MultiWinUSB(4) As WinUsbDevice     'Multiple WinUSB device information, 0-4
Public NumMultiDpp As Long                  'number of multiusb devices

Public USBDeviceNotificationHandle As Long 
Public USBDeviceConnected As Boolean 
Public USBDevicePathName As String 

'Registry functions for counting WinUSB devices
Public Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As LongByVal lpSubKey As StringByVal ulOptions As LongByVal samDesired As Long, phkResult As LongAs Long 
Public Declare Function RegEnumValue Lib "advapi32.dll" Alias "RegEnumValueA" (ByVal hKey As LongByVal dwIndex As LongByVal lpValueName As String, lpcbValueName As LongByVal lpReserved As Long, lpType As Long, lpData As Any, lpcbData As LongAs Long 

Public Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As LongAs Long 
Public Const HKEY_LOCAL_MACHINE = &H80000002 
Public Const KEY_QUERY_VALUE = &H1 
Public Const ERROR_SUCCESS = 0& 
Public Const MAX_PATH = 260 
Public Const MAXREGBUFFER = 128 
Public Const MAXERRBUFFER = 256 
Public Const MAX_DEVPATH_LENGTH = 256 
Public Const NUM_ASYNCH_IO = 100 
Public Const BUFFER_SIZE = 1024 

Public Const MAXDP5S = 128              ' max number of devices
Public Const WinUSBService = "SYSTEM\CurrentControlSet\Services\WinUSB\Enum" 
Public Const WinUsbDP5 = "USB\Vid_10c4&Pid_842a" 
Public Const WinUsbDP5Size = 21 
Public Const ERROR_NO_MORE_ITEMS = 259& 

Public Const REG_NONE = 0                       ' No value type
Public Const REG_DWORD = 4                      ' 32-bit number

Public Function OpenDevice(hwnd As LongByRef DppWinUSB As WinUsbDevice, ByRef DeviceConnected As Boolean, DevicePathName As String, MemberIndex As LongAs Boolean 
    Dim deviceFound As Boolean 
    Dim NewDevicePathName As String 
    Dim success As Boolean 
    Dim winUsbDemoGuid As Guid 
    OpenDevice = False 
    NewDevicePathName = "" 
    On Error GoTo OpenDeviceErr 
    If Not DeviceConnected Then 
        winUsbDemoGuid.D1 = &H5A8ED6A1 
        winUsbDemoGuid.D2 = &H7FC3 
        winUsbDemoGuid.D3 = &H4B6A 
        winUsbDemoGuid.D4(0) = &HA5 
        winUsbDemoGuid.D4(1) = &H36 
        winUsbDemoGuid.D4(2) = &H95 
        winUsbDemoGuid.D4(3) = &HDF 
        winUsbDemoGuid.D4(4) = &H35 
        winUsbDemoGuid.D4(5) = &HD0 
        winUsbDemoGuid.D4(6) = &H34 
        winUsbDemoGuid.D4(7) = &H48 
        'Fill an array with the device path names of all attached devices with matching GUIDs.
        deviceFound = FindDeviceFromGuid(winUsbDemoGuid, NewDevicePathName, MemberIndex)
        If deviceFound = True Then 
            success = GetDeviceHandle(DppWinUSB, NewDevicePathName)
            success = VbBool(success)
            If (success) Then 
                DeviceConnected = True 
                DevicePathName = NewDevicePathName   'Save NewDevicePathName so OnDeviceChange() knows which name is Test device.
            Else    'There was a problem in retrieving the information.
                DeviceConnected = False 
                CloseDeviceHandle DppWinUSB 
            End If 
        End If 
        If DeviceConnected Then    'The device was detected.
            #If (bEnableDeviceNotification) Then 
            '(Re)Register to receive notifications if the device is removed or attached.
            If USBDeviceNotificationHandle <> 0 Then   'Unregister old notification before creating new
                Call UnregisterDeviceNotification(USBDeviceNotificationHandle) 'Stop receiving notification messages.
            End If 
            SetWindowLong hwnd, GWL_WNDPROC, glngPrevWndProc 'pass control back to previous windows
            success = RegisterForDeviceNotifications(DevicePathName, hwnd, winUsbDemoGuid, USBDeviceNotificationHandle)
            success = VbBool(success)
            If success Then 
                glngPrevWndProc = GetWindowLong(hwnd, GWL_WNDPROC)
                SetWindowLong hwnd, GWL_WNDPROC, AddressOf WndProc 
                InitializeDevice DppWinUSB 
            End If 
            #Else 
                InitializeDevice DppWinUSB 
            #End If 
        End If 
    End If 
    OpenDevice = DeviceConnected 
    Exit Function 
OpenDeviceErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Copies WinUsbDevice storage.
'Parameter: Source WinUsbDevice storage to be saved
'Parameter: Destination WinUsbDevice storage copy
Public Sub CopyWinUsbDevice(Source As WinUsbDevice, Destination As WinUsbDevice)
    Destination.DeviceHandle = Source.DeviceHandle             'Communications device resource handle.
    Destination.winUsbHandle = Source.winUsbHandle             'WinUSB interface handle.
    Destination.bulkInPipe = Source.bulkInPipe                 'Bulk IN pipe id.
    Destination.bulkOutPipe = Source.bulkOutPipe               'Bulk OUT pipe id.
    Destination.interruptInPipe = Source.interruptInPipe       'Interrupt IN pipe id.
    Destination.interruptOutPipe = Source.interruptOutPipe     'Interrupt OUT pipe id.
    Destination.devicespeed = Source.devicespeed               'Device speed indicator.
    
    Destination.SerNum = Source.SerNum                          'dpp serial number
    Destination.idxUSB = Source.idxUSB                          'WinUSB device index (1-NumDevices)
    Destination.idxMUSB = Source.idxMUSB                        'MUSB array index
End Sub 

Public Sub DeviceConnectedDisplay(rtbStatus As RichTextLib.RichTextBox, isDetected As Boolean)
    If (isDetected) Then 
        UpdateStatusList rtbStatus, "Device connected.", RGB(0, 128, 0)
    Else 
        UpdateStatusList rtbStatus, "Could not connect to device.", vbRed 
    End If 
End Sub 

Public Sub UpdateStatusList(rtbStatus As RichTextLib.RichTextBox, strStatus As StringOptional clrColor As Long = vbBlack)
    On Error GoTo UpdateStatusListErr 
    StatusList rtbStatus, strStatus, clrColor 
    Exit Sub 
UpdateStatusListErr: 
    Call ProcessError(Err)
End Sub 

Public Sub StatusList(rtbStatus As RichTextLib.RichTextBox, strStatus As String, clrColor As LongOptional bNL As Boolean = True)
    Dim prvlen As Long 
    Dim strNL As String 
On Error GoTo 0 
    prvlen = Len(rtbStatus.Text)
    rtbStatus.SelStart = Len(rtbStatus.Text) + 1 
    strNL = "" 
    If (bNL) Then strNL = vbNewLine 
    rtbStatus.SelText = strStatus & strNL 
    rtbStatus.SelStart = prvlen 
    rtbStatus.SelLength = Len(strStatus & strNL)
    rtbStatus.SelColor = clrColor 
    rtbStatus.SelStart = Len(rtbStatus.Text) + 1 
End Sub 

'Purpose: Closes the device handle obtained with CreateFile and frees resources.
Public Sub CloseDeviceHandle(DppWinUSB As WinUsbDevice)
    Dim success As Boolean 
    On Error GoTo CloseDeviceHandleErr 
        success = WinUsb_Free(DppWinUSB.winUsbHandle)
        success = VbBool(success)
        If Not (DppWinUSB.DeviceHandle = 0) Then 
            If Not (DppWinUSB.DeviceHandle) Then 
                CloseHandle (DppWinUSB.DeviceHandle)
            End If 
        End If 
    Exit Sub 
CloseDeviceHandleErr: 
    Call ProcessError(Err)
End Sub 

'Purpose: Requests a handle with CreateFile.
'

' Returns The handle.
'

'

'Parameter: devicePathName Returned by SetupDiGetDeviceInterfaceDetail
' in an SP_DEVICE_INTERFACE_DETAIL_DATA structure.
Public Function GetDeviceHandle(ByRef DppWinUSB As WinUsbDevice, ByVal DevicePathName As StringAs Boolean 
    Dim security As SECURITY_ATTRIBUTES 
    security.lpSecurityDescriptor = 0 
    security.bInheritHandle = 1 
    security.nLength = Len(security)
    DppWinUSB.DeviceHandle = CreateFile(DevicePathName, _ 
                                GENERIC_WRITE Or GENERIC_READ, FILE_SHARE_READ Or FILE_SHARE_WRITE, _ 
                                security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_OVERLAPPED, 0)
    If Not (DppWinUSB.DeviceHandle) Then 
        GetDeviceHandle = True 
    Else 
        MsgBox GetErrorString(Err.LastDllError)
        GetDeviceHandle = False 
    End If 
End Function 

'Purpose: Initializes a device interface and obtains information about it.  Calls these
' winusb API functions: WinUsb_Initialize,WinUsb_QueryInterfaceSettings, WinUsb_QueryPipe
'

' Returns True on success, False on failure.
'

'

'Parameter: DppWinUSB DPP WinUSB device information
Public Function InitializeDevice(ByRef DppWinUSB As WinUsbDevice) As Boolean 
    Dim ifaceDescriptor As USB_INTERFACE_DESCRIPTOR 
    Dim pipeInfo As WINUSB_PIPE_INFORMATION 
    Dim pipeTimeout As Long 
    Dim success As Boolean 
    Dim success1 As Boolean 
    Dim i As Long 

    pipeTimeout = 500   ' 2/4/10 - seems like 500mS should be plenty?
    'Erase FPGA' takes 4s, worst case! Change timeout just for that command
    On Error GoTo InitializeDeviceErr 
    success = WinUsb_Initialize(DppWinUSB.DeviceHandle, DppWinUSB.winUsbHandle)
    success = VbBool(success)
    If success Then 
        success = WinUsb_QueryInterfaceSettings(DppWinUSB.winUsbHandle, 0, ifaceDescriptor)
        success = VbBool(success)
        If success Then 
            ' Get the transfer type, endpoint number, and direction for the interface's
            ' bulk and interrupt endpoints. Set pipe policies.
            For i = 0 To ifaceDescriptor.bNumEndpoints - 1 
                success1 = WinUsb_QueryPipe(DppWinUSB.winUsbHandle, 0, CByte(i), pipeInfo)
                success1 = VbBool(success1)
                If ((pipeInfo.PipeType = USBD_PIPE_TYPE.UsbdPipeTypeBulk) And _ 
                    UsbEndpointDirectionIn(pipeInfo.PipeId)) Then 
                    DppWinUSB.bulkInPipe = pipeInfo.PipeId 
                    Call SetPipePolicy(DppWinUSB, CByte(DppWinUSB.bulkInPipe), CInt(PIPE_POLICY_TYPE.IGNORE_SHORT_PACKETS), False)   ' this is the default, but set it anyway
                    Call SetPipePolicy(DppWinUSB, CByte(DppWinUSB.bulkInPipe), CInt(PIPE_POLICY_TYPE.AUTO_CLEAR_STALL), True)    ' new - 2/5/2010
                    Call SetPipePolicy(DppWinUSB, CByte(DppWinUSB.bulkInPipe), CInt(PIPE_POLICY_TYPE.ALLOW_PARTIAL_READS), False)    ' new - 5/25/2010
                    Call SetPipePolicy1(DppWinUSB, CByte(DppWinUSB.bulkInPipe), CInt(PIPE_POLICY_TYPE.PIPE_TRANSFER_TIMEOUT), pipeTimeout)
                ElseIf ((pipeInfo.PipeType = USBD_PIPE_TYPE.UsbdPipeTypeBulk) And _ 
                    UsbEndpointDirectionOut(pipeInfo.PipeId)) Then 
                    DppWinUSB.bulkOutPipe = pipeInfo.PipeId 
                    'Call SetPipePolicy(CByte(DppWinUSB.bulkOutPipe), CInt(PIPE_POLICY_TYPE.IGNORE_SHORT_PACKETS), False)
                    Call SetPipePolicy(DppWinUSB, CByte(DppWinUSB.bulkOutPipe), CInt(PIPE_POLICY_TYPE.SHORT_PACKET_TERMINATE), True)
                    Call SetPipePolicy(DppWinUSB, CByte(DppWinUSB.bulkOutPipe), CInt(PIPE_POLICY_TYPE.AUTO_CLEAR_STALL), True)
                    Call SetPipePolicy1(DppWinUSB, CByte(DppWinUSB.bulkOutPipe), CInt(PIPE_POLICY_TYPE.PIPE_TRANSFER_TIMEOUT), pipeTimeout)
                End If 
            Next i 
        Else 
            success = False 
        End If 
    End If 
    success = VbBool(success)
    InitializeDevice = success 
    Exit Function 
InitializeDeviceErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Sets pipe policy.
' Used when the value parameter is a byte (all except PIPE_TRANSFER_TIMEOUT).
'

' Returns True on success, False on failure.
'

'

'Parameter: PipeId Pipe to set a policy for.
'Parameter: PolicyType PIPE_POLICY_TYPE member.
'Parameter: Value Policy value.
Private Function SetPipePolicy(ByRef DppWinUSB As WinUsbDevice, ByVal PipeId As ByteByVal PolicyType As LongByVal Value As BooleanAs Boolean 
    Dim success As Boolean 
    On Error GoTo SetPipePolicyErr 
    success = WinUsb_SetPipePolicy(DppWinUSB.winUsbHandle, PipeId, PolicyType, 1, CByte(Value))
    success = VbBool(success)
    SetPipePolicy = success 
    Exit Function 
SetPipePolicyErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Sets pipe policy.
' Used when the value parameter is a long (PIPE_TRANSFER_TIMEOUT only).
'

' Returns True on success, False on failure.
'

'

'Parameter: PipeId     Pipe to set a policy for.
'Parameter: PolicyType PIPE_POLICY_TYPE member.
'Parameter: Value      Policy value.
Public Function SetPipePolicy1(ByRef DppWinUSB As WinUsbDevice, ByVal PipeId As ByteByVal PolicyType As LongByVal Value As LongAs Boolean 
    Dim success As Boolean 
    SetPipePolicy1 = False 
    On Error GoTo SetPipePolicy1Err 
    success = WinUsb_SetPipePolicy1(DppWinUSB.winUsbHandle, PipeId, PolicyType, 4, Value)
    success = VbBool(success)
    SetPipePolicy1 = success 
    Exit Function 
SetPipePolicy1Err: 
    Call ProcessError(Err)
End Function 

'Purpose: Is the endpoint's direction IN (device to host)
'

' Returns True if IN (device to host), False if OUT (host to device)
'

'

'Parameter: addr The endpoint address.
Private Function UsbEndpointDirectionIn(ByVal addr As LongAs Boolean 
    On Error GoTo UsbEndpointDirectionInErr 
        If ((addr And &H80) = &H80) Then 
            UsbEndpointDirectionIn = True 
        Else 
            UsbEndpointDirectionIn = False 
        End If 
    Exit Function 
UsbEndpointDirectionInErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Is the endpoint's direction OUT (host to device)
'

' Returns True if OUT (host to device, False if IN (device to host)
'

'

'Parameter: addr The endpoint address.
Private Function UsbEndpointDirectionOut(ByVal addr As LongAs Boolean 
    On Error GoTo UsbEndpointDirectionOutErr 
        If ((addr And &H80) = 0) Then 
            UsbEndpointDirectionOut = True 
        Else 
            UsbEndpointDirectionOut = False 
        End If 
    Exit Function 
UsbEndpointDirectionOutErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Compares two device path names. Used to find out if the device name
'       of a recently attached or removed device matches the name of a
'       device the application is communicating with.
'

'

' Returns True if the names match, False if not.
'

'

'Parameter: m              A WM_DEVICECHANGE message. A call to RegisterDeviceNotification
'                          causes WM_DEVICECHANGE messages to be passed to an OnDeviceChange routine..
'Parameter: DevicePathName A device pathname returned by SetupDiGetDeviceInterfaceDetail
'                          in an SP_DEVICE_INTERFACE_DETAIL_DATA structure.
Public Function DeviceNameMatch(m As Message, ByVal DevicePathName As StringAs Boolean 
    Dim deviceNameString As String 
    Dim stringSize As Long 
    deviceNameString = "" 
    On Error GoTo DeviceNameMatchErr 
    Dim devBroadcastDeviceInterface As DEV_BROADCAST_DEVICEINTERFACE2 
    Dim devBroadcastHeader As DEV_BROADCAST_HDR 
    CopyMemory devBroadcastHeader, ByVal (m.lParam), Len(devBroadcastHeader)
    If (devBroadcastHeader.dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE) Then 
        CopyMemory devBroadcastDeviceInterface, ByVal (m.lParam), ByVal (devBroadcastHeader.dbch_size)
        stringSize = CInt((devBroadcastHeader.dbch_size - 32) / 2) + 1 
        deviceNameString = CStr(Left(devBroadcastDeviceInterface.dbcc_name, stringSize)) 'Store the device name in a String.
        If (StrComp(deviceNameString, DevicePathName, vbTextCompare) = 0) Then    'Set ignorecase True.
            DeviceNameMatch = True 
        Else 
            DeviceNameMatch = False 
        End If 
    End If 
    Exit Function 
DeviceNameMatchErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Use SetupDi (Setup Device Installation) API functions to retrieve the device path name of an
'           attached device that belongs to a device interface class.
'

'

' Returns True if a device is found, False if not.
'

'

'Parameter: TestGuid         Interface class GUID.
'Parameter: devicePathName A pointer to the device path name of an attached device.
Public Function FindDeviceFromGuid(ByRef TestGuid As Guid, ByRef DevicePathName As String, MemberIndex As LongAs Boolean 
    Dim bufferSize As Long 
    Dim bufferSizeReq As Long 
    Dim detailDataBuffer() As Byte 
    Dim DevDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA 
    Dim deviceFound As Boolean 
    Dim DeviceInfoSet As Long 
    Dim lastDevice As Boolean 
    Dim TestDeviceInterfaceData As SP_DEVICE_INTERFACE_DATA 
    'Dim pDevicePathName As Long
    Dim success As Boolean 
    Dim pDevicePathName As String 

    On Error GoTo FindDeviceFromGuidErr 
    DeviceInfoSet = SetupDiGetClassDevs(TestGuid, vbNullString, 0, DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)
    deviceFound = False 
    'MemberIndex = 0
    'Do
        TestDeviceInterfaceData.cbSize = Len(TestDeviceInterfaceData)   'The size is 28 bytes.
        success = SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, TestGuid, MemberIndex, TestDeviceInterfaceData)
        success = VbBool(success)
        If (Not CBool(success)) Then    'Find out if a device information set was retrieved.
            lastDevice = True 
        Else    'A device is present.
            success = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, TestDeviceInterfaceData, 0, 0, bufferSize, 0)
            success = VbBool(success)
            DevDetailData.cbSize = Len(DevDetailData)
            ReDim detailDataBuffer(bufferSize)  '
            Call RtlMoveMemory(detailDataBuffer(0), DevDetailData, 4)
            success = SetupDiGetDeviceInterfaceDetail _ 
                        (DeviceInfoSet, _ 
                        TestDeviceInterfaceData, _ 
                        VarPtr(detailDataBuffer(0)), _ 
                        bufferSize, _ 
                        bufferSizeReq, _ 
                        0)
            success = VbBool(success)
            pDevicePathName = CStr(detailDataBuffer())                          'Convert the byte array to a string.
            pDevicePathName = StrConv(pDevicePathName, vbUnicode)               'Convert to Unicode.
            pDevicePathName = Right$(pDevicePathName, Len(pDevicePathName) - 4) 'Strip cbSize (4 bytes) from the beginning.
            DevicePathName = pDevicePathName    'Get the String containing the devicePathName.
            deviceFound = True 
        End If 
        'MemberIndex = MemberIndex + 1
    'Loop Until (lastDevice = True)
    SetupDiDestroyDeviceInfoList (DeviceInfoSet)
    FindDeviceFromGuid = deviceFound 
    Exit Function 
FindDeviceFromGuidErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Requests to receive a notification when a device is attached or removed.
'

'

' Returns True on success, False otherwise.
'

'

'Parameter: devicePathName           Handle to a device.
'Parameter: formHandle               Handle to the window that will receive device events.
'Parameter: classGuid                Device interface GUID.
'Parameter: deviceNotificationHandle Returned device notification handle.
Public Function RegisterForDeviceNotifications(ByVal DevicePathName As StringByVal formHandle As LongByRef ClassGuid As Guid, ByRef deviceNotificationHandle As LongAs Boolean 
    Dim devBroadcastDeviceInterface As DEV_BROADCAST_DEVICEINTERFACE 
    Dim devBroadcastDeviceInterfaceBuffer As Long 
    Dim size As Long 
    On Error GoTo RegisterForDeviceNotificationsErr 
    size = Len(devBroadcastDeviceInterface)         'Set the size.
    devBroadcastDeviceInterface.dbcc_size = size 
    devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE 
    devBroadcastDeviceInterface.dbcc_reserved = 0 
    devBroadcastDeviceInterface.dbcc_classguid = ClassGuid  'Specify the interface class to receive notifications about.
    #If (bEnableDeviceNotification) Then 
    deviceNotificationHandle = RegisterDeviceNotification(formHandle, devBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE)
    If (deviceNotificationHandle = 0#) Then 
        RegisterForDeviceNotifications = False 
    Else 
        RegisterForDeviceNotifications = True 
    End If 
    #Else 
        RegisterForDeviceNotifications = False 
    #End If 
    Exit Function 
RegisterForDeviceNotificationsErr: 
    Call ProcessError(Err)
End Function 

'Purpose: Returns and/or displays process error messages
Public Function ProcessError(errError As ErrObject, Optional errSource As String = "", Optional errControl As ERRORLEVEL = elevSrcDescReturn) As Variant 
    Select Case errControl 
        Case elevCodeReturn 
            ProcessError = errError.Number 
        Case elevDescReturn 
            ProcessError = errError.Description 
        Case elevSrcDescReturn 
            ProcessError = errSource & " : " & errError.Description 
        Case elevMsgBox 
            ProcessError = errSource & " : " & errError.Description 
            MsgBox errError.Description, vbCritical, errSource 
        Case Else 
            ProcessError = errSource & " : " & errError.Description 
    End Select 
    MsgBox errError.Description & " " & errError.LastDllError 
End Function 

'Purpose: Converts any C boolean type to a VB boolean type
Public Function VbBool(bBool As VariantAs Boolean 
    On Error GoTo VbBoolErr 
    VbBool = False 
    If (bBool = 0) Then 
        VbBool = False 
    Else 
        VbBool = True 
    End If 
    Exit Function 
VbBoolErr: 
End Function 

'Purpose: Returns the error message for the last error.
'Adapted from Dan Appleman's "Win32 API Puzzle Book"
Public Function GetErrorString(ByVal LastError As LongAs String 
    Dim Bytes As Long 
    Dim ErrorString As String 
    ErrorString = String$(129, 0)
    Bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, LastError, 0, ErrorString$, 128, 0)
    'Subtract two characters from the message to strip the CR and LF.
    If Bytes > 2 Then 
        GetErrorString = Left$(ErrorString, Bytes - 2)
    End If 
End Function 

#If (bEnableDeviceNotification) Then 
'Purpose: Overrides WndProc to enable checking for and handling WM_DEVICECHANGE messages.
'Parameter: Windows message info
Public Function WndProc(ByVal hwnd As LongByVal Msg As LongByVal wParam As LongByVal lParam As LongAs Long 
    On Error GoTo WndProcErr 
    If Msg = WM_DEVICECHANGE Then   'The OnDeviceChange routine processes WM_DEVICECHANGE messages.
        'frmDP5.OnDeviceChange hWnd, Msg, wParam, lParam
        frmDP5.OnDeviceChange hwnd, Msg, wParam, lParam 
    End If 
    Debug.Print Msg 
    'Pass the messages onto VB's own Window Procedure to let the base form process the message.
    WndProc = CallWindowProc(glngPrevWndProc, hwnd, Msg, wParam, lParam)
    Exit Function 
WndProcErr: 
    Call ProcessError(Err)
End Function 
#End If 

'Purpose: Counts the number of WinUSB DPP device connected
Public Function CountDP5WinusbDevices()
    Dim hKey As Long 
    Dim retCode As Long 
    Dim lRet As Long 
    Dim idxDP5 As Long 
    Dim DevicePath(MAXREGBUFFER - 1) As Byte 
    Dim cbDevicePath As Long 
    Dim KeyName As String 
    Dim cbKeyName As Long 
    Dim ErrMsg As String 
    Dim cbErrMsg As Long 
    Dim strDevicePath As String 

    NumUSBDevices = 0 
    retCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WinUSBService, 0&, KEY_QUERY_VALUE, hKey)
    If (retCode <> ERROR_SUCCESS) Then 
        CountDP5WinusbDevices = 0 
        Exit Function 
    End If 
    KeyName = Space(MAX_PATH)
    ' Test ALL Keys (0,1,... are device paths, Count,NextInstance,(Default) have other info)
    For idxDP5 = 0 To (MAXDP5S + 3) - 1 'devs + 3 other keys
        cbKeyName = MAX_PATH 
        cbDevicePath = MAXREGBUFFER 
        retCode = RegEnumValue(hKey, idxDP5, ByVal KeyName, cbKeyName, 0&, REG_NONE, DevicePath(0), cbDevicePath)
        If (retCode = ERROR_SUCCESS) Then 
            strDevicePath = ByteArrayToString(DevicePath)
            If (StrComp(Left(KeyName, 5), "Count", vbTextCompare) = 0) Then 
                'do nothing
            ElseIf (StrComp(Left(strDevicePath, WinUsbDP5Size), WinUsbDP5, vbTextCompare) = 0) Then ' DP5 device path found
                'TRACE("DP5 device [%d]: %s=%s\r\n", (NumUSBDevices + 1), KeyName, DevicePath);
                NumUSBDevices = NumUSBDevices + 1 
            End If 
        ElseIf (retCode = ERROR_NO_MORE_ITEMS) Then    ' no more values to read
            Exit For 
        Else                                        ' error reading values
            cbErrMsg = MAXERRBUFFER 
            ErrMsg = Space(MAXERRBUFFER)
            lRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0&, retCode, 0&, ErrMsg, cbErrMsg, 0&)
            If (lRet > 0) Then ErrMsg = Left(ErrMsg, lRet) Else ErrMsg = "" 
            'MsgBox ErrMsg
            Exit For 
        End If 
    Next 
    Call RegCloseKey(hKey)
    CountDP5WinusbDevices = NumUSBDevices      '/* return number of devices */
End Function 

Public Function ByteArrayToString(byteArray() As ByteAs String 
    Dim str As String 
    Dim PosTerminator As String 
    str = StrConv(byteArray, vbUnicode)
    PosTerminator = InStr(str, Chr(0))
    If PosTerminator > 0 Then str = Left(str, PosTerminator - 1)
    ByteArrayToString = str 
End Function