简体   繁体   中英

Visual studio Vb.net | Usb detection code partially working

I had to re-write the post because stackoverflow detects the post as already solved in another thread and i had to delete it. But the thread that referenced the forum was for C# and in anycase even reading it I can't find out a solution for my problem.

So, hello again guys :D

I did find somewhere online a code that allows me to detect usb devices and relative files, it works but not very well because each time i plug or unplug the usb device i get:

"System.NullReferenceException", i will not write the rest of the error to avoid that stackoverflow closes the thread, anyway you should have understood my issue.

Here the code:

Imports System.IO
Public Class Form1
#Region "USB EVENT"

    Private WM_DEVICECHANGE As Integer = &H219

    Public Enum WM_DEVICECHANGE_WPPARAMS As Integer
        DBT_CONFIGCHANGECANCELED = &H19
        DBT_CONFIGCHANGED = &H18
        DBT_CUSTOMEVENT = &H8006
        DBT_DEVICEARRIVAL = &H8000
        DBT_DEVICEQUERYREMOVE = &H8001
        DBT_DEVICEQUERYREMOVEFAILED = &H8002
        DBT_DEVICEREMOVECOMPLETE = &H8004
        DBT_DEVICEREMOVEPENDING = &H8003
        DBT_DEVICETYPESPECIFIC = &H8005
        DBT_DEVNODES_CHANGED = &H7
        DBT_QUERYCHANGECONFIG = &H17
        DBT_USERDEFINED = &HFFFF
    End Enum

    Private Structure DEV_BROADCAST_VOLUME
        Public dbcv_size As Int32
        Public dbcv_devicetype As Int32
        Public dbcv_reserved As Int32
        Public dbcv_unitmask As Int32
        Public dbcv_flags As Int16
    End Structure

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        Try
            If m.Msg = WM_DEVICECHANGE Then

                Dim Volume As DEV_BROADCAST_VOLUME

                Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)

                If Not GetDriveLetterFromMask(Volume.dbcv_unitmask).ToString.Trim = String.Empty Then

                    Dim DriveLetter As String = (GetDriveLetterFromMask(Volume.dbcv_unitmask) & ":\")

                    Select Case m.WParam

                        Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL

                            LblUSB.Text = DriveLetter

                            GetFiles()
                            MsgBox("Usb connected")
                            '   Code When add USB

                        Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE

                            LblUSB.Text = ""

                            CheckedListBox1.Items.Clear()
                            MsgBox("Usb disconnected")
                            '   Code When Remove USB

                    End Select

                End If

            End If

        Catch ex As Exception

        End Try

        MyBase.WndProc(m)

    End Sub

    Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As Char
        For i As Integer = 0 To 25
            If Unit = (2 ^ i) Then
                Return Chr(Asc("A") + i)
            End If
        Next
        Return ""
    End Function

#End Region

    Sub GetFiles()

        Try

            Dim Path = LblUSB.Text

            Dim LstFiles = My.Computer.FileSystem.GetFiles(Path, FileIO.SearchOption.SearchTopLevelOnly)

            For Each File In LstFiles
            Dim F As New IO.FileInfo(File)
            CheckedListBox1.Items.Add(F.Name)
        Next

        Catch ex As Exception

        End Try

    End Sub
End Class

The System.NullReferenceException i get is at this point:

 Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)

I did read that this error happens when the value is null and there should be a value associated, i tried different approaches but none did work and i hope that someone of you could give me a solution and an explaination to better understand that.

Thank you very much guys for the help!

When one runs the code in VS 2019, beneath the System.NullReferenceException it states: System.Runtime.InteropServices.Marshal.PtrToStructure(...) returned Nothing which occurs because m.LParam is 0 . This can be observed by placing the following code just before the DirectCast statement: Debug.WriteLine($"m.LParam: '{m.LParam.ToString()}'") .

There isn't a need to attempt to get a drive letter until it's been detected that the USB device has been inserted. If one refactors the code as shown below, the issue doesn't occur:

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

    If m.Msg = WM_DEVICECHANGE Then
        Debug.WriteLine($"m.LParam: '{m.LParam.ToString()}'")

        Select Case CInt(m.WParam)
            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL
                Debug.WriteLine($"    DBT_DEVICEARRIVAL")

                Dim Volume As DEV_BROADCAST_VOLUME

                Volume = DirectCast(Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)

                If Not GetDriveLetterFromMask(Volume.dbcv_unitmask).ToString.Trim = String.Empty Then

                    Dim DriveLetter As String = (GetDriveLetterFromMask(Volume.dbcv_unitmask) & ":\")

                    LblUSB.Text = DriveLetter

                    GetFiles()
                    MsgBox("Usb connected")
                    '   Code When add USB
                End If

            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE

                LblUSB.Text = ""

                CheckedListBox1.Items.Clear()
                MsgBox("Usb disconnected")
                '   Code When Remove USB

        End Select
    End If

    MyBase.WndProc(m)
End Sub

A side note: It's recommended to enable Option Strict for your project.


Update :

After enabling Option Strict , one receives the following compiler error: BC30512: Option Strict On disallows implicit conversions from 'String' to 'Char' in function GetDriveLetterFromMask because of the statement: Return "" . To fix the issue, try one of the following:

Option 1 :

Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As String
    For i As Integer = 0 To 25
        If Unit = (2 ^ i) Then
            Return Chr(Asc("A") + i)
        End If
    Next

    Return ""
End Function

Option 2 :

Private Function GetDriveLetterFromMask(ByRef Unit As Int32) As Char
    For i As Integer = 0 To 25
        If Unit = (2 ^ i) Then
            Return Chr(Asc("A") + i)
        End If
    Next

    Return Chr(0)
End Function

Resources :

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM