USB Device Identification & Comport Mapping
-
Sunday, May 06, 2007 10:07 PM
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
All Replies
-
Friday, May 11, 2007 7:11 AM
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
-
Saturday, May 12, 2007 10:11 PM
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
-
Monday, August 13, 2007 8:24 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?
-
Wednesday, August 15, 2007 12:05 AM
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:06 AM
Code SnippetOption 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 SnippetOption 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:10 AM
To use it...
My Vendor is 0A12 and productID is 0001:
Code SnippetDim 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 -
Tuesday, June 10, 2008 3:00 PMThanks 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
-
Wednesday, April 21, 2010 3:17 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. -
Friday, December 03, 2010 8:20 PMDim 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 IfjoOls,
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
-
Saturday, January 29, 2011 12:21 AM
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. -
Wednesday, August 01, 2012 10:32 PMvery good thread
nattelip

