Visual Studio Vb.net |USB检测代码部分工作

Visual studio Vb.net | Usb detection code partially working

提问人:Giovanni De Miccoli 提问时间:7/9/2022 更新时间:7/12/2022 访问量:157

问:

我不得不重写帖子,因为 stackoverflow 检测到该帖子已经在另一个线程中解决,我不得不删除它。但是引用论坛的线程是针对 C# 的,无论如何,即使阅读它,我也无法找到解决我的问题的方法。

所以,大家好,:D

我确实在网上的某个地方找到了一个代码,可以让我检测 USB 设备和相关文件, 它有效但不是很好,因为每次我插入或拔出 USB 设备时,我都会得到:

“System.NullReferenceException”,我不会写其余的错误以避免stackoverflow关闭线程,无论如何你应该已经理解我的问题。

代码如下:

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

我得到的System.NullReferenceException是在这一点上:

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

我确实读到过,当值为 null 并且应该有一个关联的值时,会发生此错误, 我尝试了不同的方法,但没有一个奏效,我希望你们中的某个人能给我一个解决方案和解释,以更好地理解这一点。

非常感谢你们的帮助!

vb.net visual-studio nullreferenceexception

评论

0赞 Giovanni De Miccoli 7/9/2022
我确实通过使用 CType 而不是使用 DirectCast 解决了这个问题。即使我解决了我的问题,我也希望听到你们中的一些人更好地理解它。谢谢你们
0赞 Hans Passant 7/10/2022
该代码假定每条WM_DEVICECHANGE消息都适用于卷。这显然是错误的,并导致了异常。您至少会收到两条消息,一条是设备本身 (DBT_DEVTYP_DEVICEINTERFACE) 的消息,另一条是音量 (DBT_DEVTYP_VOLUME) 的消息。只有后者才能让您获得卷信。

答:

0赞 Tu deschizi eu inchid 7/10/2022 #1

当在 VS 2019 中运行代码时,在它下面指出:System.Runtime.InteropServices.Marshal.PtrToStructure(...) 返回 Nothing ,因为 是 .通过将以下代码放在 DirectCast 语句之前,可以观察到这一点:.System.NullReferenceExceptionm.LParam0Debug.WriteLine($"m.LParam: '{m.LParam.ToString()}'")

在检测到已插入 USB 设备之前,无需尝试获取驱动器号。如果重构代码,如下所示,则不会发生此问题:

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

旁注:建议为您的项目启用 Option Strict


更新

启用 Option Strict 后,由于以下语句,会收到以下编译器错误:in function: 。要解决此问题,请尝试以下操作之一:BC30512: Option Strict On disallows implicit conversions from 'String' to 'Char'GetDriveLetterFromMaskReturn ""

选项 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

选项 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

资源

评论

0赞 Giovanni De Miccoli 7/11/2022
正确的解决方案!非常感谢user9938!多亏了你的评论,我理解了这个问题。旁注:它在没有的情况下效果很好,但是当我启用 Option Strict 时,我在“返回”“的代码中出现错误: 私有函数 GetDriveLetterFromMask(ByRef Unit As Int32) As Char For i As Integer = 0 到 25 如果单位 = (2 ^ i) 则返回 Chr(Asc(”A“) + i) End 如果下一个返回 ”“ 结束函数