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

Go协程调用BlockInput后无法恢复输入的原因及解决方案

Go协程调用BlockInput无法恢复输入的原因及解决方案

原因

Windows API BlockInput 有一个关键限制:必须由调用 BlockInput(true) 的同一个系统线程来调用 BlockInput(false),才能解除输入屏蔽。

Go的协程采用M:N调度模型,协程会被Go runtime动态调度到不同的系统线程上执行。当你在一个协程里调用 BlockInput(true) 后,后续用于恢复的 BlockInput(false) 如果在另一个协程(主协程或其他协程)执行,大概率会被调度到另一个系统线程,导致恢复操作无效。而直接在主协程连续调用时,两次调用都在同一个系统线程,因此可以正常恢复。

解决方案

方案1:绑定协程到固定系统线程

使用 runtime.LockOSThread() 将执行屏蔽/恢复逻辑的协程绑定到一个固定的系统线程,确保两次 BlockInput 调用在同一线程执行:

package main

import (
    "runtime"
    "syscall"
    "time"
)

var blockInput = syscall.MustLoadDLL("user32.dll").MustFindProc("BlockInput")

func main() {
    done := make(chan struct{})
    go func() {
        // 锁定协程到当前系统线程,后续该协程的所有操作都在这个线程执行
        runtime.LockOSThread()
        defer runtime.UnlockOSThread()

        // 执行输入屏蔽
        if ret, _, err := blockInput.Call(true); ret == 0 {
            panic(err)
        }

        // 等待恢复信号
        <-done

        // 执行输入恢复
        if ret, _, err := blockInput.Call(false); ret == 0 {
            panic(err)
        }
    }()

    // 模拟业务逻辑执行时间
    time.Sleep(3 * time.Second)
    // 发送恢复信号
    close(done)
    time.Sleep(5 * time.Second)
}

方案2:在同一线程(主协程)执行完整逻辑

如果不需要异步执行屏蔽逻辑,直接在主协程中连续调用 BlockInput(true) 和 BlockInput(false),确保两次调用在同一系统线程:

package main

import (
    "syscall"
    "time"
)

var blockInput = syscall.MustLoadDLL("user32.dll").MustFindProc("BlockInput")

func main() {
    // 屏蔽输入
    blockInput.Call(true)
    // 模拟业务逻辑
    time.Sleep(3 * time.Second)
    // 恢复输入
    blockInput.Call(false)
    time.Sleep(5 * time.Second)
}

注意事项

  • BlockInput 仅在调用它的线程存活期间有效,如果调用 BlockInput(true) 的线程意外终止,系统会自动恢复输入。
  • 在Windows Vista及以上系统中,程序需要具备UAC权限才能成功调用 BlockInput,否则会调用失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 14:07:05