You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET USB插拔检测代码触发System.NullReferenceException异常求解

问题成因
  • 代码未对WM_DEVICECHANGE消息做类型过滤:Windows发送该消息时涵盖所有类型设备的变更事件(包括USB键鼠、集线器、打印机等非存储设备),这类事件的LParam要么是空指针,要么指向非卷类型的设备广播结构体,直接按DEV_BROADCAST_VOLUME类型解析必然触发空引用或内存读取错误。
  • 自定义的DEV_BROADCAST_VOLUME结构体未声明内存布局规则,和Windows API实际使用的结构体内存对齐规则不匹配,Marshal解析时会读取错误的内存位置。
  • 代码中存在无任何处理逻辑的空Catch块,所有异常被直接吞掉,导致调试时无法快速定位错误触发的具体上下文。
  • 盘符解析函数逻辑存在缺陷:通过直接等于2^i判断盘符掩码的写法,无法适配多分区设备多位置位的场景,返回空字符后调用ToString()也存在触发空引用的风险。
可行修复方案

1. 修正结构体定义与常量声明

给结构体添加正确的内存布局特性,补充缺失的设备类型判定常量:

' 卷设备类型标识常量
Private Const DBT_DEVTYP_VOLUME As Integer = &H2

<Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Sequential, CharSet:=Runtime.InteropServices.CharSet.Auto)>
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

2. 重写WndProc消息处理逻辑

增加多层校验,仅处理存储卷的接入、拔出事件,确认参数合法后再解析结构体:

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If m.Msg = WM_DEVICECHANGE Then
        Dim eventType As Integer = m.WParam.ToInt32()
        ' 仅过滤需要处理的设备接入、移除完成事件
        If eventType = WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL OrElse 
           eventType = WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE Then
            ' 先校验LParam非空指针
            If m.LParam <> IntPtr.Zero Then
                ' 先读取设备类型字段(结构体偏移4字节位置),确认是卷设备再做完整解析
                Dim devType As Integer = Runtime.InteropServices.Marshal.ReadInt32(m.LParam, 4)
                If devType = DBT_DEVTYP_VOLUME Then
                    Dim volumeInfo As DEV_BROADCAST_VOLUME = CType(
                        Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), 
                        DEV_BROADCAST_VOLUME
                    )
                    Dim driveLetter As Char = GetDriveLetterFromMask(volumeInfo.dbcv_unitmask)
                    If driveLetter <> ChrW(0) Then
                        Dim driveRootPath As String = driveLetter & ":\"
                        Select Case eventType
                            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL
                                LblUSB.Text = driveRootPath
                                GetFiles()
                                MsgBox("USB已连接")
                            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE
                                LblUSB.Text = String.Empty
                                CheckedListBox1.Items.Clear()
                                MsgBox("USB已拔出")
                        End Select
                    End If
                End If
            End If
        End If
    End If
    MyBase.WndProc(m)
End Sub

3. 修正盘符解析函数逻辑

改用位与运算判断盘符掩码位,适配多分区设备场景,避免返回无效空值:

Private Function GetDriveLetterFromMask(ByVal unitMask As Int32) As Char
    For i As Integer = 0 To 25
        ' 位与运算判断对应盘符位是否置位
        If (unitMask And (1 << i)) <> 0 Then
            Return ChrW(AscW("A"c) + i)
        End If
    Next
    ' 无效掩码返回空字符标识
    Return ChrW(0)
End Function

额外注意事项

  • 删除代码中所有无处理逻辑的空Catch块,至少添加异常日志输出逻辑,禁止直接吞掉所有异常,否则后续出现其他问题时完全无法定位。
  • 修复后的逻辑只会响应挂载为逻辑盘符的USB存储设备,不会响应非存储类USB设备的变更事件,从根源上避免无效解析导致的空引用异常。

内容的提问来源于stack exchange,提问作者Giovanni De Miccoli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:36:34