As I’m not much of a VB.NET person, I went based on what I figured may be helpful.
C# would be a bit different however the key differences being that ' --- CONSOLIDATED RESOURCE ENUMERATION ---
section.
Hope this helps, or at least sparks something for you.
Added thread safety stuff cause UI may lock up when pulling resources.
So this way you can use threads efficiently
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Windows.Forms
Imports Microsoft.Win32
Imports System.Threading
Public NotInheritable Class Device
Private ReadOnly _deviceInfoset As IntPtr
Private ReadOnly _deviceInfoData As DeviceInstanceInfo
Private ReadOnly _properties As New Dictionary(Of DevicePropertyID, Object)()
Private ReadOnly _propertiesLock As New ReaderWriterLockSlim()
Private ReadOnly _drivers As Lazy(Of IList(Of DeviceDriver))
' Unified resource cache
Private ReadOnly _resourceCache As New Dictionary(Of Type, Object)()
Private ReadOnly _resourceCacheLock As New ReaderWriterLockSlim()
Private _flags As DeviceInstanceFlags
Private _problem As DeviceInstanceProblem
Public Sub New(hInfoSet As IntPtr, instanceInfo As DeviceInstanceInfo)
_deviceInfoset = hInfoSet
_deviceInfoData = instanceInfo
_drivers = New Lazy(Of IList(Of DeviceDriver))(AddressOf EnumerateDrivers)
InitializeDeviceStatus()
End Sub
Private Sub InitializeDeviceStatus()
Try
If NativeMethods.CM_Get_DevNode_Status(_flags, _problem, _deviceInfoData.InstanceID, 0) = ConfigResult.Success Then
If (_flags And DeviceInstanceFlags.HAS_PROBLEM) = 0 Then
_problem = DeviceInstanceProblem.NO_PROBLEM
End If
Else
_flags = DeviceInstanceFlags.HAS_PROBLEM
_problem = DeviceInstanceProblem.NO_PROBLEM
End If
Catch ex As Exception
Throw New InvalidOperationException("Failed to initialize device status.", ex)
End Try
End Sub
' --- CONSOLIDATED RESOURCE ENUMERATION ---
Private Function GetResourceList(Of T)() As IList(Of T)
_resourceCacheLock.EnterUpgradeableReadLock()
Try
If _resourceCache.ContainsKey(GetType(T)) Then
Return DirectCast(_resourceCache(GetType(T)), IList(Of T))
End If
_resourceCacheLock.EnterWriteLock()
Try
Dim list As IList(Of T)
If GetType(T) Is GetType(DeviceMemoryRange) Then
list = DirectCast(EnumerateMemoryResources(), IList(Of T))
ElseIf GetType(T) Is GetType(DevicePortRange) Then
list = DirectCast(EnumeratePortResources(), IList(Of T))
ElseIf GetType(T) Is GetType(DeviceDMARange) Then
list = DirectCast(EnumerateDMAResources(), IList(Of T))
ElseIf GetType(T) Is GetType(DeviceIRQRange) Then
list = DirectCast(EnumerateIRQResources(), IList(Of T))
Else
Throw New NotSupportedException($"Resource type {GetType(T).Name} not supported.")
End If
_resourceCache(GetType(T)) = list
Return list
Finally
_resourceCacheLock.ExitWriteLock()
End Try
Finally
_resourceCacheLock.ExitUpgradeableReadLock()
End Try
End Function
' --- RESOURCE ENUMERATION METHODS ---
Private Function EnumerateMemoryResources() As IList(Of DeviceMemoryRange)
Dim resources As New List(Of DeviceMemoryRange)()
' Example: Call your NativeMethods here
' NativeMethods.EnumerateMemoryResources(_deviceInfoset, _deviceInfoData, resources)
Return resources
End Function
Private Function EnumeratePortResources() As IList(Of DevicePortRange)
Dim resources As New List(Of DevicePortRange)()
' Example: Call your NativeMethods here
' NativeMethods.EnumeratePortResources(_deviceInfoset, _deviceInfoData, resources)
Return resources
End Function
Private Function EnumerateDMAResources() As IList(Of DeviceDMARange)
Dim resources As New List(Of DeviceDMARange)()
' Example: Call your NativeMethods here
' NativeMethods.EnumerateDMAResources(_deviceInfoset, _deviceInfoData, resources)
Return resources
End Function
Private Function EnumerateIRQResources() As IList(Of DeviceIRQRange)
Dim resources As New List(Of DeviceIRQRange)()
' Example: Call your NativeMethods here
' NativeMethods.EnumerateIRQResources(_deviceInfoset, _deviceInfoData, resources)
Return resources
End Function
Private Function EnumerateDrivers() As IList(Of DeviceDriver)
Dim drivers As New List(Of DeviceDriver)()
' Example: Call your NativeMethods here
' NativeMethods.EnumerateDrivers(_deviceInfoset, _deviceInfoData, drivers)
Return drivers
End Function
' --- TYPE-SAFE, THREAD-SAFE REGISTRY PROPERTY ACCESS WITH ERROR HANDLING ---
Private Function GetRegistryProperty(Of T)(propertyID As DevicePropertyID, expectedType As RegistryValueKind) As T
_propertiesLock.EnterUpgradeableReadLock()
Try
If _properties.ContainsKey(propertyID) Then
Return DirectCast(_properties(propertyID), T)
End If
_propertiesLock.EnterWriteLock()
Try
Dim buffer(1023) As Byte
Dim requiredSize As Integer
Dim dataType As RegistryValueKind
If Not NativeMethods.SetupDiGetDeviceRegistryPropertyW(_deviceInfoset, _deviceInfoData, propertyID, dataType, buffer, buffer.Length, requiredSize) Then
Throw New InvalidOperationException($"Failed to retrieve property {propertyID}.")
End If
If dataType <> expectedType Then
Throw New InvalidCastException($"Unexpected registry value type: {dataType} (expected {expectedType})")
End If
Dim value As Object = ConvertRegistryBuffer(buffer, requiredSize, dataType, GetType(T))
_properties(propertyID) = value
Return DirectCast(value, T)
Finally
_propertiesLock.ExitWriteLock()
End Try
Finally
_propertiesLock.ExitUpgradeableReadLock()
End Try
End Function
' --- TYPE CONVERSION LOGIC ---
Private Shared Function ConvertRegistryBuffer(buffer As Byte(), size As Integer, kind As RegistryValueKind, targetType As Type) As Object
Select Case kind
Case RegistryValueKind.String, RegistryValueKind.ExpandString
Dim str As String = Encoding.Unicode.GetString(buffer, 0, size)
Dim nullIdx = str.IndexOf(ChrW(0))
If nullIdx >= 0 Then str = str.Substring(0, nullIdx)
Return str
Case RegistryValueKind.DWord
If size < 4 Then Throw New InvalidCastException("DWORD registry value too small.")
Dim val As Integer = BitConverter.ToInt32(buffer, 0)
If targetType Is GetType(Boolean) Then
Return val <> 0
End If
Return Convert.ChangeType(val, targetType)
Case RegistryValueKind.QWord
If size < 8 Then Throw New InvalidCastException("QWORD registry value too small.")
Dim val As Long = BitConverter.ToInt64(buffer, 0)
Return Convert.ChangeType(val, targetType)
Case RegistryValueKind.MultiString
Dim str As String = Encoding.Unicode.GetString(buffer, 0, size)
Return str.Split(New Char() {ChrW(0)}, StringSplitOptions.RemoveEmptyEntries)
Case RegistryValueKind.Binary
Dim result(size - 1) As Byte
Array.Copy(buffer, result, size)
Return result
Case Else
Throw New NotSupportedException($"Registry value kind {kind} is not supported.")
End Select
End Function
' --- PROPERTIES USING CONSOLIDATED LOGIC ---
Public ReadOnly Property FriendlyName() As String
Get
Try
Return GetRegistryProperty(Of String)(DevicePropertyID.FriendlyName, RegistryValueKind.String)
Catch
Return GetRegistryProperty(Of String)(DevicePropertyID.Description, RegistryValueKind.String)
End Try
End Get
End Property
Public ReadOnly Property MemoryRanges As IList(Of DeviceMemoryRange)
Get
Return GetResourceList(Of DeviceMemoryRange)()
End Get
End Property
Public ReadOnly Property PortRanges As IList(Of DevicePortRange)
Get
Return GetResourceList(Of DevicePortRange)()
End Get
End Property
Public ReadOnly Property DMARanges As IList(Of DeviceDMARange)
Get
Return GetResourceList(Of DeviceDMARange)()
End Get
End Property
Public ReadOnly Property IRQRanges As IList(Of DeviceIRQRange)
Get
Return GetResourceList(Of DeviceIRQRange)()
End Get
End Property
Public ReadOnly Property Drivers As IList(Of DeviceDriver)
Get
Return _drivers.Value
End Get
End Property
Protected Overrides Sub Finalize()
Try
NativeMethods.SetupDiDestroyDeviceInfoList(_deviceInfoset)
Finally
MyBase.Finalize()
End Try
End Sub
End Class