I've spent a fairly tremendous amount of time working with USB in VB6, specifically with USB printers.
Here's a snippet that has to do with generating a list of all the USB printers currently on the system using only built-in system APIs. It may be missing a few declares since its a snippet of a much much larger tool, but it should give the idea.
The output of the function is a 2d array containing a magic path you can use with createfile, readfile and writefile in the 0 element, and the driver key (aka software key) in the 1 element. The output would be something like:
\\?\usb#vid_0a5f&pid_00bd#xxqlj124500138#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
for the first string (which has the vid, the pid, the serial number and the port's GUID), and
{36fc9e60-c465-11cf-8056-444553540000}\0152
for the second.
This particular code also does other things, like determine which USB stack is responsible for managing the printer device (in windows you can have more than one USB stack installed), and also who is the parent device (the hub) of the printer (though some of that is commented out).
If it would be useful I could boil this down into an example but it seems you are already pretty close to a working solution.
Code:
Private Const DIGCF_PRESENT = &H2
Private Const DIGCF_DEVICEINTERFACE = &H10
Const DIGCF_ALLCLASSES As Long = &H4
Const OVERLAPPED_IO_PENDING = 997
Const STATUS_PENDING = 259
Private Type OVERLAPPED
lpInternal As Long
lpInternalHigh As Long
offset As Long
OffsetHigh As Long
hEvent As Long
End Type
Private Type SP_DEVICE_INTERFACE_DETAIL_DATA
cbSize As Long
DataPath(256) As Byte
End Type
Private Type SP_DEVICE_INTERFACE_DATA
cbSize As Long
InterfaceClassGuid As GUID
Flags As Long
ReservedPtr As Long
End Type
Private Type SP_DEVINFO_DATA
cbSize As Long
InterfaceClassGuid As GUID
hDevInst As Long
ReservedPtr As Long
End Type
Private Declare Function SetupDiGetClassDevs Lib "setupapi.dll" Alias "SetupDiGetClassDevsA" (HidGuid As GUID, ByVal EnumPtr As Long, ByVal hwndParent As Long, ByVal Flags As Long) As Long
Private Declare Function SetupDiDestroyDeviceInfoList Lib "setupapi.dll" (ByVal DeviceInfoSet As Long) As Boolean
Private Declare Function SetupDiEnumDeviceInterfaces Lib "setupapi.dll" (ByVal Handle As Long, ByVal InfoPtr As Long, HidGuid As GUID, ByVal MemberIndex As Long, DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) As Boolean
Private Declare Function SetupDiEnumDeviceInterfacesWithInfoPtr Lib "setupapi.dll" Alias "SetupDiEnumDeviceInterfaces" (ByVal Handle As Long, ByRef InfoPtr As SP_DEVINFO_DATA, HidGuid As GUID, ByVal MemberIndex As Long, DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) As Boolean
Private Declare Function SetupDiGetDeviceInterfaceDetail Lib "setupapi.dll" Alias "SetupDiGetDeviceInterfaceDetailA" (ByVal Handle As Long, DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, FunctionClassDeviceData As SP_DEVICE_INTERFACE_DETAIL_DATA, ByVal DetailLength As Long, lpReturnedLength As Long, deviceInfoData As SP_DEVINFO_DATA) As Boolean
Private Declare Function SetupDiOpenDeviceInfo Lib "setupapi.dll" Alias "SetupDiOpenDeviceInfoA" (ByVal DeviceInfoSet As Long, ByRef DeviceInstanceId As Any, ByVal hwndParent As Long, ByVal OpenFlags As Long, ByRef deviceInfoData As SP_DEVINFO_DATA) As Long
Private Declare Function CM_Get_Device_ID_Size Lib "cfgmgr32.dll" (ByRef pulLen As Long, ByVal dnDevInst As Long, ByVal ulFlags As Long) As Long
Private Declare Function CM_Get_Device_ID Lib "cfgmgr32.dll" Alias "CM_Get_Device_IDA" (ByVal lDnDevInst As Long, pBuffer As Any, lLength As Long, ByVal lFlags As Long) As Long
Private Declare Function CM_Get_Parent Lib "cfgmgr32.dll" (lDnDevInst As Long, ByVal lDnDevInst As Long, ByVal lFlags As Long) As Long
Private Declare Function CM_Get_Sibling Lib "cfgmgr32.dll" (lDnDevInst As Long, ByVal lDnDevInst As Long, ByVal lFlags As Long) As Long
Private PrinterGUID As GUID 'Used for enumerating USB Devices
Private USBStackGUID As GUID 'Used for enumerating USB Devices
Function Init()
PrinterGUID.DataL = &H28D78FAD
PrinterGUID.data1 = &H5A12
PrinterGUID.data2 = &H11D1
PrinterGUID.DataB(0) = &HAE
PrinterGUID.DataB(1) = &H5B
PrinterGUID.DataB(2) = 0
PrinterGUID.DataB(3) = 0
PrinterGUID.DataB(4) = &HF8
PrinterGUID.DataB(5) = 3
PrinterGUID.DataB(6) = &HA8
PrinterGUID.DataB(7) = &HC2
USBStackGUID.DataL = &HF18A0E88
USBStackGUID.data1 = &HC30C
USBStackGUID.data2 = &H11D0
USBStackGUID.DataB(0) = &H88
USBStackGUID.DataB(1) = &H15
USBStackGUID.DataB(2) = 0
USBStackGUID.DataB(3) = &HA0
USBStackGUID.DataB(4) = &HC9
USBStackGUID.DataB(5) = &H6
USBStackGUID.DataB(6) = &HBE
USBStackGUID.DataB(7) = &HD8
End Function
Private Function CreateListOfUSBPrinters(Optional lngErrorResult As Long = 0) As String()
Dim HDEVINFO_USBPrinterClassDevs As Long
Dim HDEVINFO_USBClassDevs As Long
Dim PSP_DEVINFO_DATA_NULL As Long
Dim PDEVINST_ParentDevInst As Long
Dim typChildDeviceInterfaceData As SP_DEVICE_INTERFACE_DATA
Dim typChildDeviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA
Dim typChildDevInfoData As SP_DEVINFO_DATA
Dim strChildPath As String
Dim typParentDeviceInterfaceData As SP_DEVICE_INTERFACE_DATA
'Dim typParentDeviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA
Dim typParentDevInfoData As SP_DEVINFO_DATA
Dim bytParentDeviceInstanceID() As Byte
'Dim strParentPath As String
Dim strHoldingArray() As String
Dim lngResultingSize As Long
Dim DP_SIZE As Long
If Globals.InIde Then On Error GoTo 0 Else On Error GoTo LocalErrorHandler
DP_SIZE = UBound(typChildDeviceInterfaceDetailData.DataPath)
ReDim bytParentDeviceInstanceID(DP_SIZE) As Byte
Const MIN_SIZE = 5
Const CR_SUCCESS = 0
Const ERROR_NO_MORE_ITEMS = 259
Dim lngDeviceInterfaceIndex As Long
Dim lngDeviceInterfaceCount As Long
' Fill in initial sizes for structures
typChildDeviceInterfaceData.cbSize = Len(typChildDeviceInterfaceData)
typChildDevInfoData.cbSize = Len(typChildDevInfoData)
' Fill in initial sizes for structures
typParentDeviceInterfaceData.cbSize = Len(typParentDeviceInterfaceData)
typParentDevInfoData.cbSize = Len(typParentDevInfoData)
lngErrorResult = 0
Do
' Create initial device information lists - one for USB Printers, one for generic USB devices
HDEVINFO_USBPrinterClassDevs = SetupDiGetClassDevs(PrinterGUID, 0, 0, DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)
HDEVINFO_USBClassDevs = SetupDiGetClassDevs(USBStackGUID, 0, 0, DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)
' If either is invalid, give up
If HDEVINFO_USBPrinterClassDevs = INVALID_HANDLE_VALUE Then Exit Do
If HDEVINFO_USBClassDevs = INVALID_HANDLE_VALUE Then Exit Do
' Count how many USB printer devices we have
Do While SetupDiEnumDeviceInterfaces(HDEVINFO_USBPrinterClassDevs, PSP_DEVINFO_DATA_NULL, PrinterGUID, lngDeviceInterfaceCount, typChildDeviceInterfaceData)
lngDeviceInterfaceCount = lngDeviceInterfaceCount + 1
Loop
' If we have zero, or we had an error, give up
If Err.LastDllError <> ERROR_NO_MORE_ITEMS Or lngDeviceInterfaceCount = 0 Then Exit Do
ReDim strHoldingArray(0 To lngDeviceInterfaceCount - 1, 0 To 1) As String
' Interate over each device
Do While SetupDiEnumDeviceInterfaces(HDEVINFO_USBPrinterClassDevs, PSP_DEVINFO_DATA_NULL, PrinterGUID, lngDeviceInterfaceIndex, typChildDeviceInterfaceData)
' You must setup this structure to the fixed size, not the total size. The fixed size is 5 bytes.
typChildDeviceInterfaceDetailData.cbSize = MIN_SIZE
ZeroMemory typChildDeviceInterfaceDetailData.DataPath(0), DP_SIZE
' Get the child's interface detail (path)
If False = SetupDiGetDeviceInterfaceDetail(HDEVINFO_USBPrinterClassDevs, typChildDeviceInterfaceData, typChildDeviceInterfaceDetailData, DP_SIZE, lngResultingSize, typChildDevInfoData) Then
Exit Do
End If
' Generate the Child's path
strChildPath = StrConv(typChildDeviceInterfaceDetailData.DataPath, vbUnicode)
strChildPath = Left(strChildPath, lngResultingSize - MIN_SIZE)
Dim varResult As Variant
Dim typSP As typSP_DEVINFO_DATA
CopyMemory typSP.lSize, typChildDevInfoData.cbSize, typChildDevInfoData.cbSize
If SetupDiGetDeviceRegistryProperty(HDEVINFO_USBPrinterClassDevs, typSP, SPDRP_DRIVER, varResult) = False Then
Exit Do
End If
' Get the parent's Device Instance Handle
If CR_SUCCESS <> CM_Get_Parent(PDEVINST_ParentDevInst, typChildDevInfoData.hDevInst, 0) Then
Exit Do
End If
' Clear the Parent's device ID
'ZeroMemory bytParentDeviceInstanceID(0), DP_SIZE
'lngResultingSize = 0
' Get the Parentn's Device Instance ID string
'If CR_SUCCESS <> CM_Get_Device_ID(PDEVINST_ParentDevInst, bytParentDeviceInstanceID(0), DP_SIZE, 0) Then
' Exit Do
'End If
' From that string get a Device Info Data structure, and add it to the USB Class device information list
' This will also generate us our parent's device information data
'If False = SetupDiOpenDeviceInfo(HDEVINFO_USBClassDevs, bytParentDeviceInstanceID(0), 0, 0, typParentDevInfoData) Then
' Exit Do
'End If
' Enumerate the device we just added to get the parent's device interface data
'If False = SetupDiEnumDeviceInterfacesWithInfoPtr(HDEVINFO_USBClassDevs, typParentDevInfoData, USBStackGUID, 0, typParentDeviceInterfaceData) Then
' Exit Do
'End If
' Clear the structure
'typParentDeviceInterfaceDetailData.cbSize = MIN_SIZE
'ZeroMemory typParentDeviceInterfaceDetailData.DataPath(0), DP_SIZE
' Get the parent's interface detail (path)
'If False = SetupDiGetDeviceInterfaceDetail(HDEVINFO_USBClassDevs, typParentDeviceInterfaceData, typParentDeviceInterfaceDetailData, DP_SIZE, lngResultingSize, typParentDevInfoData) Then
' Exit Do
'End If
' Generate the Parent's path
'strParentPath = StrConv(typParentDeviceInterfaceDetailData.DataPath, vbUnicode)
'strParentPath = Left(strParentPath, lngResultingSize - MIN_SIZE)
strHoldingArray(lngDeviceInterfaceIndex, 0) = strChildPath
strHoldingArray(lngDeviceInterfaceIndex, 1) = varResult
lngDeviceInterfaceIndex = lngDeviceInterfaceIndex + 1
Loop
Exit Do
Loop
lngErrorResult = Err.LastDllError
SetupDiDestroyDeviceInfoList HDEVINFO_USBClassDevs
SetupDiDestroyDeviceInfoList HDEVINFO_USBPrinterClassDevs
CreateListOfUSBPrinters = strHoldingArray
Exit Function
LocalErrorHandler:
If GlobalError("clsCommuncation:CreateListOfUSBPrinters", Err.Number, Err.Source, Err.Description) = 1 Then Resume Next
End Function