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

VB.NET显微镜登录系统LowLevelKeyboardProc委托被回收报错求助

解决方案:低级键盘钩子委托被GC回收问题

这个错误的核心原因是你的LowLevelKeyboardProc委托被.NET垃圾回收器(GC)回收了——哪怕你声明了变量,要么是变量作用域不对,要么是注销时没正确卸载钩子,导致系统钩子仍在引用已被回收的委托。闲置10分钟触发是因为GC会在后台周期性回收无引用对象,刚好在这段时间回收了该委托。

以下是具体修复步骤:

1. 将委托声明为类级共享变量

确保委托变量的生命周期和应用程序/窗口周期一致,避免局部变量被GC回收:

' 在主窗体/核心类中声明类级共享委托变量
Private Shared _keyboardHookProc As LowLevelKeyboardProc
Private Shared _hookHandle As IntPtr = IntPtr.Zero

' 定义钩子回调委托
Private Delegate Function LowLevelKeyboardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr

2. 注销流程中主动卸载钩子

在F2触发注销时,必须先卸载已注册的低级键盘钩子,再切换回登录界面:

Private Sub Logout()
    ' 先卸载钩子
    If _hookHandle <> IntPtr.Zero Then
        UnhookWindowsHookEx(_hookHandle)
        _hookHandle = IntPtr.Zero
    End If
    
    ' 切换回登录界面
    LoginForm.Show()
    Me.Hide()
End Sub

' 导入卸载钩子的API函数
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function UnhookWindowsHookEx(ByVal hhk As IntPtr) As Boolean
End Function

3. 登录成功后重新注册钩子

确保钩子的生命周期和登录状态严格绑定:每次登录后注册钩子,注销时卸载:

Private Sub RegisterKeyboardHook()
    If _hookHandle = IntPtr.Zero Then
        _keyboardHookProc = AddressOf LowLevelKeyboardHookCallback
        _hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardHookProc, GetModuleHandle(Nothing), 0)
        
        ' 可选:强制GC暂时不回收委托(辅助手段,核心还是靠作用域和卸载)
        GC.KeepAlive(_keyboardHookProc)
    End If
End Sub

' 登录成功时调用
Private Sub LoginSuccess()
    RegisterKeyboardHook()
    MainForm.Show()
    LoginForm.Hide()
End Sub

4. 钩子回调函数的正确实现

确保回调逻辑规范,避免不必要的引用泄漏:

Private Function LowLevelKeyboardHookCallback(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
    If nCode >= 0 Then
        ' 处理Win+ESC、Win+D、Alt+Tab等组合键屏蔽逻辑
        Dim keyData = CType(Marshal.ReadInt32(lParam), Keys)
        If (keyData = Keys.Escape AndAlso (Control.ModifierKeys And Keys.LWin) = Keys.LWin) OrElse _
           (keyData = Keys.D AndAlso (Control.ModifierKeys And Keys.LWin) = Keys.LWin) OrElse _
           (Control.ModifierKeys And Keys.Alt) = Keys.Alt AndAlso (keyData = Keys.Tab) Then
            ' 返回1表示拦截该按键消息
            Return New IntPtr(1)
        End If
    End If
    
    ' 传递消息给下一个钩子
    Return CallNextHookEx(_hookHandle, nCode, wParam, lParam)
End Function

关键注意点:

  • 绝对不能让钩子在注销后仍处于激活状态,否则即便委托被回收,系统仍会尝试调用它触发错误。
  • 类级共享变量能确保委托在类的生命周期内不会被GC回收,直到你主动卸载钩子并置空引用。

内容的提问来源于stack exchange,提问作者xiao000139

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:33:36