Go/Fyne应用调用hook.End()时因快速连续点击按钮崩溃
问题分析与解决方案
根源
快速连续点击按钮会多次触发mouse函数,导致重复注册系统钩子、并发调用hook.End()。由于robotn/gohook底层依赖C代码实现系统钩子,重复启停或并发操作会引发内存访问冲突(SIGSEGV),直接导致程序崩溃。
解决步骤
1. 新增全局状态控制
通过互斥锁+运行状态标记,确保同一时间只有一个钩子会话在运行:
import "sync" var ( hookIsRunning bool hookMutex sync.Mutex )
2. 修改mouse函数,加入状态校验与资源清理
func mouse(data *[]string) { // 先校验是否已有钩子在运行,避免重复触发 hookMutex.Lock() if hookIsRunning { hookMutex.Unlock() return } hookIsRunning = true hookMutex.Unlock() // 清理残留的钩子与回调(防止之前的钩子未完全终止) hook.End() hook.Clear() // 注册鼠标移动回调(带退出通道防止残留回调触发) moveQuit := make(chan struct{}) defer close(moveQuit) hook.Register(hook.MouseMove, []string{}, func(e hook.Event) { select { case <-moveQuit: return default: // 你的鼠标移动逻辑 } }) // 注册鼠标点击回调 downQuit := make(chan struct{}) defer close(downQuit) hook.Register(hook.MouseDown, []string{}, func(e hook.Event) { select { case <-downQuit: return default: // 获取像素颜色 c := robotgo.GetPixelColor(int(e.X), int(e.Y)) *data = append(*data, c) // 更新运行状态 hookMutex.Lock() hookIsRunning = false hookMutex.Unlock() // 终止钩子并清理资源 hook.End() hook.Clear() } }) s := hook.Start() <-hook.Process(s) }
3. 优化按钮点击逻辑(可选)
在Fyne按钮的点击回调中,可临时禁用按钮直到钩子会话结束,进一步避免重复触发:
btn.OnTapped = func() { btn.Disable() go func() { mouse(&colorData) btn.Enable() }() }
关键说明
- 之前将
mouse放入单独Goroutine无效的原因:Goroutine不会阻止多次触发mouse函数,多个钩子实例仍会并发运行,底层C代码无法处理这种并发操作。 hook.Clear()必须调用:每次终止钩子后清理所有注册的回调,避免残留的回调在后续钩子运行时被错误触发,引发内存问题。
内容的提问来源于stack exchange,提问作者Dachi Natsvlishvili
相关产品推荐
相关产品推荐

