locked
USB Device Identification & Comport Mapping RRS feed

  • Question

  • I have a USB gadget that appears to Windows as a USB serial port, there is no special driver.

     

    I know the USB Vendor ID, and the USB Product ID, I need 1 or 2 functions:

     

    First:    IsThisUsbDevicePresent( Vendor_ID, Product_ID )

     

    Second: Some means of transforming the above into "COM1" or "COM9" or what ever it is so I can call it.

     

    Any suggestions?

     

    I'm pretty sure, it's just a matter of knowing where to look in the WIndows registry...

     

    And that- should be doable from VB...

     

    -Duane

     

     

    Sunday, May 6, 2007 10:07 PM

Answers

  • unix2vb,

     

    According to your question, I would like to provide the answer from VB.NET support part:

     

    1. Programming Serial Ports Using Visual Basic 2005

    http://www.devx.com/dotnet/Article/31001?type=kbArticle&trk=MSCP

     

    While serial port programming was absent in .NET version 1.1, Visual Basic developers who grew accustomed to the MSCOMM control in VB6 will be glad to know that this functionality is supported again in .NET 2.0. Learn to use the SerialPort class to make two computers talk to one another or even to manipulate a mobile device from your computer using Bluetooth. 

    2. Identifiers for USB Devices

     

    http://msdn2.microsoft.com/en-us/library/ms791079.aspx

     

    For all USB devices, the USB bus driver generates a standard set of identifiers composed of values retrieved from the USB device and interface descriptors. Standard USB Identifiers are discussed in the first of the two sections indicated above. In addition to the standard USB identifiers, native Windows drivers for mass storage and printer devices generate a separate set of USB identifiers composed of information of special relevance to printers and storage devices. These special USB identifiers are discussed in the second section.

     

    3. IStaticPortMapping: The IStaticPortMapping interface provides methods to retrieve and change the information for a particular port mapping.

     

    http://msdn2.microsoft.com/en-us/library/aa366145.aspx

     

     

    Friday, May 11, 2007 7:11 AM
  • Code Snippet
    Option Strict On
    Option
    Explicit On

    Imports
    System.Runtime.InteropServices
    Imports System.ComponentModel
    Imports System.Text

    Friend Class NativeMethods

    #
    Region " Constants "
        Public Shared ReadOnly GUID_DEVINTERFACE_COMPORT As New Guid("86e0d1e0808911d09ce408003e301f73")
        
    Public Const CM_REGISTRY_HARDWARE As Integer = 0
        
    Public Const ERROR_INSUFFICIENT_BUFFER As Integer = 122
        
    Public Const ERROR_INVALID_DATA As Integer = 13
        
    Public Const ERROR_NO_MORE_ITEMS As Integer = 259
        
    Public Const KEY_QUERY_VALUE As Integer = 1
        
    Public Const RegDisposition_OpenExisting As Integer = 1

    #
    End Region

    #Region " Enums "
        Public Enum CRErrorCodes
            CR_SUCCESS = 0
            CR_DEFAULT
            CR_OUT_OF_MEMORY
            CR_INVALID_POINTER
            CR_INVALID_FLAG
            CR_INVALID_DEVNODE
            CR_INVALID_RES_DES
            CR_INVALID_LOG_CONF
            CR_INVALID_ARBITRATOR
            CR_INVALID_NODELIST
            CR_DEVNODE_HAS_REQS
            CR_INVALID_RESOURCEID
            CR_DLVXD_NOT_FOUND            
    ' WIN 95 ONLY
            CR_NO_SUCH_DEVNODE
            CR_NO_MORE_LOG_CONF
            CR_NO_MORE_RES_DES
            CR_ALREADY_SUCH_DEVNODE
            CR_INVALID_RANGE_LIST
            CR_INVALID_RANGE
            CR_FAILURE
            CR_NO_SUCH_LOGICAL_DEV
            CR_CREATE_BLOCKED
            CR_NOT_SYSTEM_VM            
    ' WIN 95 ONLY
            CR_REMOVE_VETOED
            CR_APM_VETOED
            CR_INVALID_LOAD_TYPE
            CR_BUFFER_SMALL
            CR_NO_ARBITRATOR
            CR_NO_REGISTRY_HANDLE
            CR_REGISTRY_ERROR
            CR_INVALID_DEVICE_ID
            CR_INVALID_DATA
            CR_INVALID_API
            CR_DEVLOADER_NOT_READY
            CR_NEED_RESTART
            CR_NO_MORE_HW_PROFILES
            CR_DEVICE_NOT_THERE
            CR_NO_SUCH_VALUE
            CR_WRONG_TYPE
            CR_INVALID_PRIORITY
            CR_NOT_DISABLEABLE
            CR_FREE_RESOURCES
            CR_QUERY_VETOED
            CR_CANT_SHARE_IRQ
            CR_NO_DEPENDENT
            CR_SAME_RESOURCES
            CR_NO_SUCH_REGISTRY_KEY
            CR_INVALID_MACHINENAME      
    ' NT ONLY
            CR_REMOTE_COMM_FAILURE      ' NT ONLY
            CR_MACHINE_UNAVAILABLE      ' NT ONLY
            CR_NO_CM_SERVICES           ' NT ONLY
            CR_ACCESS_DENIED            ' NT ONLY
            CR_CALL_NOT_IMPLEMENTED
            CR_INVALID_PROPERTY
            CR_DEVICE_INTERFACE_ACTIVE
            CR_NO_SUCH_DEVICE_INTERFACE
            CR_INVALID_REFERENCE_STRING
            CR_INVALID_CONFLICT_LIST
            CR_INVALID_INDEX
            CR_INVALID_STRUCTURE_SIZE
            NUM_CR_RESULTS
        
    End Enum

        Public Enum RegPropertyTypes
            REG_BINARY = 3
            REG_DWORD = 4
            REG_DWORD_BIG_ENDIAN = 5
            REG_DWORD_LITTLE_ENDIAN = 4
            REG_EXPAND_SZ = 2
            REG_MULTI_SZ = 7
            REG_SZ = 1
        
    End Enum

        <Flags()> _
        
    Public Enum DeviceFlags As Integer
            DigCFDefault = 1
            DigCFPresent = 2            
    ' return only devices that are currently present
            DigCFAllClasses = 4         ' gets all classes, ignores the guid...
            DigCFProfile = 8            ' gets only classes that are part of the current hardware profile
            DigCDDeviceInterface = 16   ' Return devices that expose interfaces of the interface class that are specified by ClassGuid.
        End Enum

    #End Region

    #Region " Structures "
        ' SP_DEVICE_INTERFACE_DATA
        <StructLayout(LayoutKind.Sequential, pack:=1)> _
        
    Public Structure DeviceInterfaceData
            
    Public MySize As Integer
            Public InterfaceClassGuid As Guid
            
    Public Flags As Integer
            Public Reserved As IntPtr
            
    Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DeviceInterfaceData))
            
    End Sub
        End Structure

        ' SP_DEVINFO_DATA
        <StructLayout(LayoutKind.Sequential, pack:=1)> _
        
    Public Structure DevinfoData
            
    Public MySize As Integer
            Public ClassGuid As Guid
            
    Public DevInst As IntPtr ' DWORD? x64?
            Public Reserved As Integer
            Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DevinfoData))
            
    End Sub
        End Structure

        ' SP_DEVICE_INTERFACE_DETAIL_DATA
        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        
    Public Structure DeviceInterfaceDetailData
            
    Public MySize As Integer
            Public DevicePath As Short
            Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DeviceInterfaceDetailData))
            
    End Sub
        End Structure
    #End Region

    #Region " P/Invoke Signatures "

        <DllImport("setupapi.dll")> _
        
    Public Shared Function CM_Get_Device_ID( _
        
    ByVal hDeviceInstance As IntPtr, _
        
    ByVal buffer As System.Text.StringBuilder, _
        
    ByVal bufferLength As Integer, _
        
    ByVal mustBeZero As Integer) As CRErrorCodes
        
    End Function

        <DllImport("cfgmgr32", _
        SetLastError:=
    True)> _
        
    Public Shared Function CM_Open_DevNode_Key( _
        
    ByVal devNode As IntPtr, _
        
    ByVal samDesired As Integer, _
        
    ByVal hardwareProfile As Integer, _
        
    ByVal disposition As Integer, _
        
    ByRef hKey As IntPtr, _
        
    ByVal flags As Integer) As CRErrorCodes
        
    End Function

        <DllImport("setupapi.dll")> _
        
    Public Shared Function CM_Get_Parent( _
        
    ByRef hDeviceInstanceParent As IntPtr, _
        
    ByVal hDeviceInstance As IntPtr, _
        
    ByVal mustBeZero As Integer) As CRErrorCodes
        
    End Function

        <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _
        
    Public Shared Function RegCloseKey(ByVal hkey As IntPtr) As Integer
        End Function

        <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _
        
    Public Shared Function RegQueryValueEx( _
        
    ByVal hKey As IntPtr, _
        
    ByVal valueName As String, _
        
    ByVal reserved As Integer, _
        
    ByRef type As RegPropertyTypes, _
        
    ByVal data As System.Text.StringBuilder, _
        
    ByRef dataSize As Integer) As Integer
        End Function

        <DllImport("setupapi", SetLastError:=True)> _
        
    Public Shared Function SetupDiDestroyDeviceInfoList( _
        
    ByVal hDeviceInfoSet As IntPtr) As Boolean
        End Function

        <DllImport("setupapi.dll", SetLastError:=True)> _
        
    Public Shared Function SetupDiEnumDeviceInterfaces( _
        
    ByVal deviceInfoSet As IntPtr, _
        
    ByVal deviceInfoData As IntPtr, _
        <MarshalAs(UnmanagedType.LPStruct)> _
        
    ByVal interfaceClassGuid As Guid, _
        
    ByVal memberIndex As Integer, _
        
    ByRef deviceInterfaceData As DeviceInterfaceData) As Boolean
        End Function

        <DllImport("setupapi", SetLastError:=True)> _
        
    Public Shared Function SetupDiGetClassDevs( _
        <MarshalAs(UnmanagedType.LPStruct)> _
        
    ByVal classGuid As System.Guid, _
        
    ByVal enumerator As String, _
        
    ByVal hwndParent As IntPtr, _
        
    ByVal flags As DeviceFlags) As IntPtr
        
    End Function

        <DllImport("setupapi.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
        
    Public Shared Function SetupDiGetDeviceInterfaceDetail( _
        
    ByVal deviceInfoSet As IntPtr, _
        
    ByRef deviceInterfaceData As DeviceInterfaceData, _
        
    ByVal deviceInterfaceDetailData As IntPtr, _
        
    ByVal deviceInterfaceDetailDataSize As Integer, _
        
    ByRef requiredSize As Integer, _
        
    ByRef deviceInfoData As DevinfoData) As Boolean
        End Function

    #End Region

    End
    Class

     

     

    Wednesday, August 15, 2007 12:06 AM
  • Code Snippet
    Option Strict On
    Option
    Explicit On

    Imports
    System.Runtime.InteropServices
    Imports System.ComponentModel
    Imports System.Text
    Imports WindowsApplication25.NativeMethods ' Rename...

    Public Class USBPortEnumerator

        
    Public Shared Function GetCOMPortNumberForUSBDevice(ByVal vendorID As String, ByVal productID As String) As String
            ' Get a handle to a Device Information set for
            ' all Present Ports on the computer that support
            ' the Device Infomation Interface.
            Dim hDevInfoSet As IntPtr = SetupDiGetClassDevs( _
                GUID_DEVINTERFACE_COMPORT,
    Nothing, Nothing, _
                DeviceFlags.DigCDDeviceInterface
    Or DeviceFlags.DigCFPresent)
            
    If hDevInfoSet.ToInt64 = -1 Then
                Throw New Win32Exception
            
    End If

            ' We loop though all the Ports.
            Dim portIndex As Integer = 0
            
    Try
                Do
                    ' Get a Device Interface Data structure.
                    ' --------------------------------------
                    Dim interfaceData As New DeviceInterfaceData
                    interfaceData.Initialize()
                    
    Dim result As Boolean = SetupDiEnumDeviceInterfaces( _
                        hDevInfoSet,
    Nothing, GUID_DEVINTERFACE_COMPORT, _
                        portIndex, interfaceData)
                    
    If result = False Then
                        If Marshal.GetLastWin32Error = ERROR_NO_MORE_ITEMS Then
                            ' We've done all the com ports.
                            Exit Do
                        Else
                            ' Unforseen problem...
                            Throw New Win32Exception
                        
    End If
                    End If

                    ' Get a DevInfoDetailData and DeviceInfoData
                    ' ------------------------------------------
                    Dim infoData As New DevinfoData
                    infoData.Initialize()
                    
    Dim requiredSize As Integer
                    ' First call to get the required size.
                    result = SetupDiGetDeviceInterfaceDetail( _
                        hDevInfoSet, interfaceData,
    Nothing, _
                        0, requiredSize, infoData)
                    
    ' We expect an insufficient buffer error.
                    If Marshal.GetLastWin32Error <> ERROR_INSUFFICIENT_BUFFER Then
                        Throw New Win32Exception
                    
    End If
                    ' Create the buffer.
                    Dim detailData As New DeviceInterfaceDetailData
                    detailData.Initialize()
                    
    Dim devDetailBuffer As IntPtr
                    
    Dim devicePath As String = Nothing
                    Try
                        devDetailBuffer = Marshal.AllocHGlobal(requiredSize)
                        Marshal.StructureToPtr(detailData, devDetailBuffer,
    True)

                        
    ' Call with the correct buffer
                        result = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, _
                            interfaceData, devDetailBuffer, requiredSize, _
                            requiredSize, infoData)

                        
    If result = False Then
                            Throw New Win32Exception
                        
    End If
                    Finally
                        Marshal.FreeHGlobal(devDetailBuffer)
                    
    End Try

                    ' Is this Port a USB Port? Ask the parent, then it's parent
                    ' etc if it is a USB device.
                    ' ---------------------------------------------------------------
                    Dim startingDevice As IntPtr = infoData.DevInst
                    
    Dim CRResult As CRErrorCodes
                    
    Do
                        Dim hParentDevice As IntPtr = IntPtr.Zero
                        CRResult = CM_Get_Parent(hParentDevice, startingDevice, 0)
                        
    If CRResult = CRErrorCodes.CR_NO_SUCH_DEVNODE Then
                            ' We hit the top of the pnp tree.
                            Exit Do
                        End If
                        If CRResult <> CRErrorCodes.CR_SUCCESS Then
                            Throw New Exception("Error calling CM_Get_Parent: " & CRResult.ToString)
                        
    End If
                        Dim sb As New System.Text.StringBuilder(1024)
                        CRResult = CM_Get_Device_ID(hParentDevice, sb, sb.Capacity, 0)
                        
    If CRResult <> CRErrorCodes.CR_SUCCESS Then
                            Throw New Exception("Error calling CM_Get_Device_ID: " & CRResult.ToString)
                        
    End If
                        ' We have the pnp string of the parent device.
                        Dim deviceID As String = sb.ToString
                        
    If deviceID.StartsWith("USB\") Then
                            ' check the version and product ids.
                            Dim vid As String = deviceID.Substring(deviceID.IndexOf("VID_") + 4, 4)
                            
    Dim pid As String = deviceID.Substring(deviceID.IndexOf("PID_") + 4, 4)
                            
    If vid.Equals(vendorID) AndAlso pid.Equals(productID) Then
                                Dim hkey As IntPtr = IntPtr.Zero
                                CRResult = CM_Open_DevNode_Key(infoData.DevInst, KEY_QUERY_VALUE, _
                                   0, RegDisposition_OpenExisting, hkey, CM_REGISTRY_HARDWARE)
                                
    If CRResult <> CRErrorCodes.CR_SUCCESS Then
                                    Throw New Exception("CM_Open_DevNode_Key error: " & CRResult.ToString)
                                
    End If
                                Dim comNumber As New StringBuilder(16)
                                
    Dim type As RegPropertyTypes
                                
    Dim size As Integer = comNumber.Capacity
                                
    Dim hresult As Integer = RegQueryValueEx(hkey, "PortName", 0, type, comNumber, size)
                                
    If hresult <> 0 Then
                                    Throw New Win32Exception
                                
    End If
                                hresult = RegCloseKey(hkey)
                                
    If hresult <> 0 Then
                                    Throw New Win32Exception
                                
    End If
                                ' After all that...
                                Return comNumber.ToString
                                
    ' (If you have several ports on one USB device, then
                                ' add this value to some collection and then Exit Do)
                            Else
                                ' it was another usb device.
                                ' no point continuing with this port.
                                Exit Do
                            End If
                        End If
                        ' Do the next parent.
                        startingDevice = hParentDevice
                    
    Loop
                    ' Do the next port
                    portIndex += 1
                
    Loop
            Finally
                SetupDiDestroyDeviceInfoList(hDevInfoSet)
            
    End Try
            ' We did all the ports and didn't find it.
            Return Nothing
        End Function

    End
    Class

     

     

    Wednesday, August 15, 2007 12:06 AM

All replies

  • unix2vb,

     

    According to your question, I would like to provide the answer from VB.NET support part:

     

    1. Programming Serial Ports Using Visual Basic 2005

    http://www.devx.com/dotnet/Article/31001?type=kbArticle&trk=MSCP

     

    While serial port programming was absent in .NET version 1.1, Visual Basic developers who grew accustomed to the MSCOMM control in VB6 will be glad to know that this functionality is supported again in .NET 2.0. Learn to use the SerialPort class to make two computers talk to one another or even to manipulate a mobile device from your computer using Bluetooth. 

    2. Identifiers for USB Devices

     

    http://msdn2.microsoft.com/en-us/library/ms791079.aspx

     

    For all USB devices, the USB bus driver generates a standard set of identifiers composed of values retrieved from the USB device and interface descriptors. Standard USB Identifiers are discussed in the first of the two sections indicated above. In addition to the standard USB identifiers, native Windows drivers for mass storage and printer devices generate a separate set of USB identifiers composed of information of special relevance to printers and storage devices. These special USB identifiers are discussed in the second section.

     

    3. IStaticPortMapping: The IStaticPortMapping interface provides methods to retrieve and change the information for a particular port mapping.

     

    http://msdn2.microsoft.com/en-us/library/aa366145.aspx

     

     

    Friday, May 11, 2007 7:11 AM
  • I am sorry Bruno, you did not understand my question.

     

    Let me ask my question a different way.

     

    I have a special USB device, it has USB Vendor ID number:   &H09B2, and Product id: &H0001

     

    Sometimes it is connected to my computer.

     

    Sometimes it is not connected to my computer.

     

    When it is connected to my computer, the USB device becomes a COM PORT.

     

    How do I do the following:

     

    (1) I need to know if the device is presently plugged into my computer yes or no.

     

    The function I need would be used like this:

     

        if   NOT IsUsbDevicePresent(   &H09B2, &H0001 ) then

               MsgBox( "Please Attach the special device to the computer");

        end if

     

    (2) The device, when plugged into my computer becomes a "COM<some number>"

     

    On my computer it becomes COM3, on somebody elses computer it becomes a different random COM<number>.

     

    That is a problem.   I need to determine the exact name of the COMPORT for my special USB device.

     

    The function I need would be used like this:

     

        dim  NAME as string

     

       name = GetComportNameForUsbDevice( &H09B2, &H0001 )

     

      if   NAME  = "NONE" then

            MsgBox ("The special device is not present" );

      else

            MsgBox ( "The special device is present as:  " & name )

     endif

     

     

     

      

     

    Saturday, May 12, 2007 10:11 PM
  • Hey unix2vb,

      I am new to VB and face the same problem as you. A USB device emulate as a serial port and I need to find the com port number from the device manager manually (higher than >100 now). Right now I just hard code the com port number. Did you have luck to find out how to get he com port number given the USB vendor and product ID?

     

     

     

    Monday, August 13, 2007 8:24 PM
  • I'm using a USB Bluetooth dongle, and it has a com port set up. In device manager (view by connection) I see:

     

    USB Root Hub

    - Generic Bluetooth Radio

    -- Bluetooth Device (RFCOMM Protocol TDI)

    ---Standard Serial Over Bluetooth Link (COM22)

     

    So here the parent of COM22 is a Bluetooth device, and it's parent is a generic bluetooth radio. If I look at the properties of the bluetooth radio, it's device ID is:

     

    USB\VID_0A12&PID_0001\7&1D7EB380&0&1

    This string identifies it as a USB device, and has the Vendor ID and the Product ID.

     

    So, here is the plan, we use a big bunch of API calls to:

     

    Enumerate all Ports.

    Walk up the tree looking at all the parent devices.

    If a parent device is USB and has the correct vendor ID and Product ID then we have found the correct device.

     

    Once we know we have the correct device, we can use an api call to read the com port name from the registry.

     

    My dongle actaully has about 10 Com ports on it. Why, I don't know, I never use them anyway. Maybe the driver is rubbish and creates a new one every time it gets plugged in. This function stops at the first port that has a USB parent with a matching vendor and product id, so it only gets one COM port.

     

    (This is bait for someone to come up with a way to do it in 3 lines.)

     

    (argh. too big. Will split it into two classes...)

    Wednesday, August 15, 2007 12:05 AM
  • Code Snippet
    Option Strict On
    Option
    Explicit On

    Imports
    System.Runtime.InteropServices
    Imports System.ComponentModel
    Imports System.Text

    Friend Class NativeMethods

    #
    Region " Constants "
        Public Shared ReadOnly GUID_DEVINTERFACE_COMPORT As New Guid("86e0d1e0808911d09ce408003e301f73")
        
    Public Const CM_REGISTRY_HARDWARE As Integer = 0
        
    Public Const ERROR_INSUFFICIENT_BUFFER As Integer = 122
        
    Public Const ERROR_INVALID_DATA As Integer = 13
        
    Public Const ERROR_NO_MORE_ITEMS As Integer = 259
        
    Public Const KEY_QUERY_VALUE As Integer = 1
        
    Public Const RegDisposition_OpenExisting As Integer = 1

    #
    End Region

    #Region " Enums "
        Public Enum CRErrorCodes
            CR_SUCCESS = 0
            CR_DEFAULT
            CR_OUT_OF_MEMORY
            CR_INVALID_POINTER
            CR_INVALID_FLAG
            CR_INVALID_DEVNODE
            CR_INVALID_RES_DES
            CR_INVALID_LOG_CONF
            CR_INVALID_ARBITRATOR
            CR_INVALID_NODELIST
            CR_DEVNODE_HAS_REQS
            CR_INVALID_RESOURCEID
            CR_DLVXD_NOT_FOUND            
    ' WIN 95 ONLY
            CR_NO_SUCH_DEVNODE
            CR_NO_MORE_LOG_CONF
            CR_NO_MORE_RES_DES
            CR_ALREADY_SUCH_DEVNODE
            CR_INVALID_RANGE_LIST
            CR_INVALID_RANGE
            CR_FAILURE
            CR_NO_SUCH_LOGICAL_DEV
            CR_CREATE_BLOCKED
            CR_NOT_SYSTEM_VM            
    ' WIN 95 ONLY
            CR_REMOVE_VETOED
            CR_APM_VETOED
            CR_INVALID_LOAD_TYPE
            CR_BUFFER_SMALL
            CR_NO_ARBITRATOR
            CR_NO_REGISTRY_HANDLE
            CR_REGISTRY_ERROR
            CR_INVALID_DEVICE_ID
            CR_INVALID_DATA
            CR_INVALID_API
            CR_DEVLOADER_NOT_READY
            CR_NEED_RESTART
            CR_NO_MORE_HW_PROFILES
            CR_DEVICE_NOT_THERE
            CR_NO_SUCH_VALUE
            CR_WRONG_TYPE
            CR_INVALID_PRIORITY
            CR_NOT_DISABLEABLE
            CR_FREE_RESOURCES
            CR_QUERY_VETOED
            CR_CANT_SHARE_IRQ
            CR_NO_DEPENDENT
            CR_SAME_RESOURCES
            CR_NO_SUCH_REGISTRY_KEY
            CR_INVALID_MACHINENAME      
    ' NT ONLY
            CR_REMOTE_COMM_FAILURE      ' NT ONLY
            CR_MACHINE_UNAVAILABLE      ' NT ONLY
            CR_NO_CM_SERVICES           ' NT ONLY
            CR_ACCESS_DENIED            ' NT ONLY
            CR_CALL_NOT_IMPLEMENTED
            CR_INVALID_PROPERTY
            CR_DEVICE_INTERFACE_ACTIVE
            CR_NO_SUCH_DEVICE_INTERFACE
            CR_INVALID_REFERENCE_STRING
            CR_INVALID_CONFLICT_LIST
            CR_INVALID_INDEX
            CR_INVALID_STRUCTURE_SIZE
            NUM_CR_RESULTS
        
    End Enum

        Public Enum RegPropertyTypes
            REG_BINARY = 3
            REG_DWORD = 4
            REG_DWORD_BIG_ENDIAN = 5
            REG_DWORD_LITTLE_ENDIAN = 4
            REG_EXPAND_SZ = 2
            REG_MULTI_SZ = 7
            REG_SZ = 1
        
    End Enum

        <Flags()> _
        
    Public Enum DeviceFlags As Integer
            DigCFDefault = 1
            DigCFPresent = 2            
    ' return only devices that are currently present
            DigCFAllClasses = 4         ' gets all classes, ignores the guid...
            DigCFProfile = 8            ' gets only classes that are part of the current hardware profile
            DigCDDeviceInterface = 16   ' Return devices that expose interfaces of the interface class that are specified by ClassGuid.
        End Enum

    #End Region

    #Region " Structures "
        ' SP_DEVICE_INTERFACE_DATA
        <StructLayout(LayoutKind.Sequential, pack:=1)> _
        
    Public Structure DeviceInterfaceData
            
    Public MySize As Integer
            Public InterfaceClassGuid As Guid
            
    Public Flags As Integer
            Public Reserved As IntPtr
            
    Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DeviceInterfaceData))
            
    End Sub
        End Structure

        ' SP_DEVINFO_DATA
        <StructLayout(LayoutKind.Sequential, pack:=1)> _
        
    Public Structure DevinfoData
            
    Public MySize As Integer
            Public ClassGuid As Guid
            
    Public DevInst As IntPtr ' DWORD? x64?
            Public Reserved As Integer
            Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DevinfoData))
            
    End Sub
        End Structure

        ' SP_DEVICE_INTERFACE_DETAIL_DATA
        <StructLayout(LayoutKind.Sequential, Pack:=1)> _
        
    Public Structure DeviceInterfaceDetailData
            
    Public MySize As Integer
            Public DevicePath As Short
            Public Sub Initialize()
                
    Me.MySize = Marshal.SizeOf(GetType(DeviceInterfaceDetailData))
            
    End Sub
        End Structure
    #End Region

    #Region " P/Invoke Signatures "

        <DllImport("setupapi.dll")> _
        
    Public Shared Function CM_Get_Device_ID( _
        
    ByVal hDeviceInstance As IntPtr, _
        
    ByVal buffer As System.Text.StringBuilder, _
        
    ByVal bufferLength As Integer, _
        
    ByVal mustBeZero As Integer) As CRErrorCodes
        
    End Function

        <DllImport("cfgmgr32", _
        SetLastError:=
    True)> _
        
    Public Shared Function CM_Open_DevNode_Key( _
        
    ByVal devNode As IntPtr, _
        
    ByVal samDesired As Integer, _
        
    ByVal hardwareProfile As Integer, _
        
    ByVal disposition As Integer, _
        
    ByRef hKey As IntPtr, _
        
    ByVal flags As Integer) As CRErrorCodes
        
    End Function

        <DllImport("setupapi.dll")> _
        
    Public Shared Function CM_Get_Parent( _
        
    ByRef hDeviceInstanceParent As IntPtr, _
        
    ByVal hDeviceInstance As IntPtr, _
        
    ByVal mustBeZero As Integer) As CRErrorCodes
        
    End Function

        <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _
        
    Public Shared Function RegCloseKey(ByVal hkey As IntPtr) As Integer
        End Function

        <System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError:=True)> _
        
    Public Shared Function RegQueryValueEx( _
        
    ByVal hKey As IntPtr, _
        
    ByVal valueName As String, _
        
    ByVal reserved As Integer, _
        
    ByRef type As RegPropertyTypes, _
        
    ByVal data As System.Text.StringBuilder, _
        
    ByRef dataSize As Integer) As Integer
        End Function

        <DllImport("setupapi", SetLastError:=True)> _
        
    Public Shared Function SetupDiDestroyDeviceInfoList( _
        
    ByVal hDeviceInfoSet As IntPtr) As Boolean
        End Function

        <DllImport("setupapi.dll", SetLastError:=True)> _
        
    Public Shared Function SetupDiEnumDeviceInterfaces( _
        
    ByVal deviceInfoSet As IntPtr, _
        
    ByVal deviceInfoData As IntPtr, _
        <MarshalAs(UnmanagedType.LPStruct)> _
        
    ByVal interfaceClassGuid As Guid, _
        
    ByVal memberIndex As Integer, _
        
    ByRef deviceInterfaceData As DeviceInterfaceData) As Boolean
        End Function

        <DllImport("setupapi", SetLastError:=True)> _
        
    Public Shared Function SetupDiGetClassDevs( _
        <MarshalAs(UnmanagedType.LPStruct)> _
        
    ByVal classGuid As System.Guid, _
        
    ByVal enumerator As String, _
        
    ByVal hwndParent As IntPtr, _
        
    ByVal flags As DeviceFlags) As IntPtr
        
    End Function

        <DllImport("setupapi.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
        
    Public Shared Function SetupDiGetDeviceInterfaceDetail( _
        
    ByVal deviceInfoSet As IntPtr, _
        
    ByRef deviceInterfaceData As DeviceInterfaceData, _
        
    ByVal deviceInterfaceDetailData As IntPtr, _
        
    ByVal deviceInterfaceDetailDataSize As Integer, _
        
    ByRef requiredSize As Integer, _
        
    ByRef deviceInfoData As DevinfoData) As Boolean
        End Function

    #End Region

    End
    Class

     

     

    Wednesday, August 15, 2007 12:06 AM
  • Code Snippet
    Option Strict On
    Option
    Explicit On

    Imports
    System.Runtime.InteropServices
    Imports System.ComponentModel
    Imports System.Text
    Imports WindowsApplication25.NativeMethods ' Rename...

    Public Class USBPortEnumerator

        
    Public Shared Function GetCOMPortNumberForUSBDevice(ByVal vendorID As String, ByVal productID As String) As String
            ' Get a handle to a Device Information set for
            ' all Present Ports on the computer that support
            ' the Device Infomation Interface.
            Dim hDevInfoSet As IntPtr = SetupDiGetClassDevs( _
                GUID_DEVINTERFACE_COMPORT,
    Nothing, Nothing, _
                DeviceFlags.DigCDDeviceInterface
    Or DeviceFlags.DigCFPresent)
            
    If hDevInfoSet.ToInt64 = -1 Then
                Throw New Win32Exception
            
    End If

            ' We loop though all the Ports.
            Dim portIndex As Integer = 0
            
    Try
                Do
                    ' Get a Device Interface Data structure.
                    ' --------------------------------------
                    Dim interfaceData As New DeviceInterfaceData
                    interfaceData.Initialize()
                    
    Dim result As Boolean = SetupDiEnumDeviceInterfaces( _
                        hDevInfoSet,
    Nothing, GUID_DEVINTERFACE_COMPORT, _
                        portIndex, interfaceData)
                    
    If result = False Then
                        If Marshal.GetLastWin32Error = ERROR_NO_MORE_ITEMS Then
                            ' We've done all the com ports.
                            Exit Do
                        Else
                            ' Unforseen problem...
                            Throw New Win32Exception
                        
    End If
                    End If

                    ' Get a DevInfoDetailData and DeviceInfoData
                    ' ------------------------------------------
                    Dim infoData As New DevinfoData
                    infoData.Initialize()
                    
    Dim requiredSize As Integer
                    ' First call to get the required size.
                    result = SetupDiGetDeviceInterfaceDetail( _
                        hDevInfoSet, interfaceData,
    Nothing, _
                        0, requiredSize, infoData)
                    
    ' We expect an insufficient buffer error.
                    If Marshal.GetLastWin32Error <> ERROR_INSUFFICIENT_BUFFER Then
                        Throw New Win32Exception
                    
    End If
                    ' Create the buffer.
                    Dim detailData As New DeviceInterfaceDetailData
                    detailData.Initialize()
                    
    Dim devDetailBuffer As IntPtr
                    
    Dim devicePath As String = Nothing
                    Try
                        devDetailBuffer = Marshal.AllocHGlobal(requiredSize)
                        Marshal.StructureToPtr(detailData, devDetailBuffer,
    True)

                        
    ' Call with the correct buffer
                        result = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, _
                            interfaceData, devDetailBuffer, requiredSize, _
                            requiredSize, infoData)

                        
    If result = False Then
                            Throw New Win32Exception
                        
    End If
                    Finally
                        Marshal.FreeHGlobal(devDetailBuffer)
                    
    End Try

                    ' Is this Port a USB Port? Ask the parent, then it's parent
                    ' etc if it is a USB device.
                    ' ---------------------------------------------------------------
                    Dim startingDevice As IntPtr = infoData.DevInst
                    
    Dim CRResult As CRErrorCodes
                    
    Do
                        Dim hParentDevice As IntPtr = IntPtr.Zero
                        CRResult = CM_Get_Parent(hParentDevice, startingDevice, 0)
                        
    If CRResult = CRErrorCodes.CR_NO_SUCH_DEVNODE Then
                            ' We hit the top of the pnp tree.
                            Exit Do
                        End If
                        If CRResult <> CRErrorCodes.CR_SUCCESS Then
                            Throw New Exception("Error calling CM_Get_Parent: " & CRResult.ToString)
                        
    End If
                        Dim sb As New System.Text.StringBuilder(1024)
                        CRResult = CM_Get_Device_ID(hParentDevice, sb, sb.Capacity, 0)
                        
    If CRResult <> CRErrorCodes.CR_SUCCESS Then
                            Throw New Exception("Error calling CM_Get_Device_ID: " & CRResult.ToString)
                        
    End If
                        ' We have the pnp string of the parent device.
                        Dim deviceID As String = sb.ToString
                        
    If deviceID.StartsWith("USB\") Then
                            ' check the version and product ids.
                            Dim vid As String = deviceID.Substring(deviceID.IndexOf("VID_") + 4, 4)
                            
    Dim pid As String = deviceID.Substring(deviceID.IndexOf("PID_") + 4, 4)
                            
    If vid.Equals(vendorID) AndAlso pid.Equals(productID) Then
                                Dim hkey As IntPtr = IntPtr.Zero
                                CRResult = CM_Open_DevNode_Key(infoData.DevInst, KEY_QUERY_VALUE, _
                                   0, RegDisposition_OpenExisting, hkey, CM_REGISTRY_HARDWARE)
                                
    If CRResult <> CRErrorCodes.CR_SUCCESS Then
                                    Throw New Exception("CM_Open_DevNode_Key error: " & CRResult.ToString)
                                
    End If
                                Dim comNumber As New StringBuilder(16)
                                
    Dim type As RegPropertyTypes
                                
    Dim size As Integer = comNumber.Capacity
                                
    Dim hresult As Integer = RegQueryValueEx(hkey, "PortName", 0, type, comNumber, size)
                                
    If hresult <> 0 Then
                                    Throw New Win32Exception
                                
    End If
                                hresult = RegCloseKey(hkey)
                                
    If hresult <> 0 Then
                                    Throw New Win32Exception
                                
    End If
                                ' After all that...
                                Return comNumber.ToString
                                
    ' (If you have several ports on one USB device, then
                                ' add this value to some collection and then Exit Do)
                            Else
                                ' it was another usb device.
                                ' no point continuing with this port.
                                Exit Do
                            End If
                        End If
                        ' Do the next parent.
                        startingDevice = hParentDevice
                    
    Loop
                    ' Do the next port
                    portIndex += 1
                
    Loop
            Finally
                SetupDiDestroyDeviceInfoList(hDevInfoSet)
            
    End Try
            ' We did all the ports and didn't find it.
            Return Nothing
        End Function

    End
    Class

     

     

    Wednesday, August 15, 2007 12:06 AM
  • To use it...

     

    My Vendor is 0A12 and productID is 0001:

     

    Code Snippet

     

    Dim comPort As String
    Try
        comPort = USBPortEnumerator.GetCOMPortNumberForUSBDevice("0A12", "0001")
    Catch ex As Exception
        
    Throw ex '  it might well throw some exceptions, this isn't well tested!
    End Try
    If comPort Is Nothing Then
        MessageBox.Show("did not find the device")
    Else
        MessageBox.Show(comPort)
    End If

     

     

    Wednesday, August 15, 2007 12:10 AM
  • Thanks jo0ls for the code.
    I tried using it in my project but it doesn't seem to find the device when I enter my vendor and product ID. Does it matter if the ID is entered in capitals?

    Please reply ASAP. Thanks
    Tuesday, June 10, 2008 3:00 PM
  • Thanks jo0ls for the code.
    I tried using it in my project but it doesn't seem to find the device when I enter my vendor and product ID. Does it matter if the ID is entered in capitals?

    Please reply ASAP. Thanks


    Hi, I'm wondering the code worked for you? I'm using it now but seems not working for me. Thanks for your help.

     

    Wednesday, April 21, 2010 3:17 PM
  • Dim comPort As String
    Try
        comPort = USBPortEnumerator.GetCOMPortNumberForUSBDevice("0A12", "0001")
    Catch ex As Exception
        
    Throw ex '  it might well throw some exceptions, this isn't well tested!
    End Try
    If comPort Is Nothing Then
        MessageBox.Show("did not find the device")
    Else
        MessageBox.Show(comPort)
    End If

     

    joOls,

    I realize that this is an ancient thread, but I was looking for a quick way to return the port number of an attached device and found these routines.  I've incorporated them into my .NET project and everything compiles nicely (yes, I did change the VID & PID to the device I was looking for).  However, when I run this, the program exits before finding any USB device.  In fact, as I step through it, I only see a few ACPI devices and the PCI bus come up, but nothig else.  Any idea why it isn't combing through all the system devices?

    Thanks,

    Fran

    Friday, December 3, 2010 8:20 PM
  • Hi,

    Bit late, someone just linked back to this old thread.

    I'm not sure why it's not finding com port devices for some people. I'll see if I can reproduce the problem. I probably misunderstood how to use the api and how these information sets are laid out.

    SetupDiGetClassDevs is the starting point, and it is filtering...

    Dim hDevInfoSet As IntPtr = SetupDiGetClassDevs(GUID_DEVINTERFACE_COMPORT, Nothing, Nothing, DeviceFlags.DigCFDeviceInterface Or DeviceFlags.DigCFPresent)
    

    This determines which bits of the PnP tree it's going to look at. The DigCFDeviceInterface in combination with the first parameter GUID_DEVINTERFACE_COMPORT maens it will only look for devices that expose the COMPORT interface. DigCFPresent means that the device has to be present on the system right now.

    And alternative would be to search for all devices with some interface (so it will get a lot more things!)...

    Dim hDevInfoSet As IntPtr = SetupDiGetClassDevs(Nothing, Nothing, Nothing, DeviceFlags.DigCFAllClasses Or DeviceFlags.DigCFDeviceInterface)
    

    I guess case sensitivity could come into it, so just trim the strings and do a case insensitive check:

    Dim vid As String = deviceID.Substring(deviceID.IndexOf("VID_") + 4, 4).Trim
    Dim pid As String = deviceID.Substring(deviceID.IndexOf("PID_") + 4, 4).Trim
    If vid.Equals(vendorID.Trim, StringComparison.InvariantCultureIgnoreCase) AndAlso pid.Equals(productID.Trim, StringComparison.InvariantCultureIgnoreCase) Then
    
    

     and 

    If deviceID.StartsWith("USB\", StringComparison.InvariantCultureIgnoreCase) Then
    

    maybe this

    ' (If you have several ports on one USB device, then
    ' add this value to some collection and then Exit Do)
    Else
    ' it was another usb device.
    ' no point continuing with this port.
    Exit Do
    
    End If
    
    


    was a bad idea, take out the exit do and let it go up until it hits the top.

    Saturday, January 29, 2011 12:21 AM
  • very good thread

    nattelip

    Wednesday, August 1, 2012 10:32 PM