Golang中如何修改底层鼠标事件
Golang中如何修改底层鼠标事件 我正在尝试编写一个程序来修复鼠标滚轮漂移问题。该程序的目标是在无效的滚轮事件发送到系统之前,修改它们并用有效的事件进行纠正(例如,如果你正在向下滚动,然后出现了一个向上事件,应将其修改为向下事件)。因此,我编写了一个钩子来监听鼠标事件,并在事件类型正确时进行修改。然而,每当我发送修改后的事件时,系统就开始卡顿,并且实际上什么也没发生。我尝试了几种方法,结果都一样。为什么会这样?
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"unsafe"
"github.com/gonutz/w32"
)
const CUSTOM_DATA = 0x1234567
// Define the MSLLHOOKSTRUCT structure
type MSLLHOOKSTRUCT struct {
Pt w32.POINT
MouseData uint32
Flags uint32
Time uint32
DwExtraInfo uintptr
}
var hook w32.HHOOK
// MouseProc is the hook procedure for mouse events
func MouseProc(code int, wparam w32.WPARAM, lparam w32.LPARAM) w32.LRESULT {
if code >= 0 {
msllhookstruct := (*MSLLHOOKSTRUCT)(unsafe.Pointer(lparam))
if wparam == w32.WM_MOUSEWHEEL && msllhookstruct.DwExtraInfo != CUSTOM_DATA {
// Modify the scroll delta here, for example, halving it to reduce scroll speed
scrollAmount := int32(msllhookstruct.MouseData >> 16)
scrollAmount /= 2 // Adjust this factor as necessary
msllhookstruct.MouseData = uint32(scrollAmount << 16)
msllhookstruct.DwExtraInfo = CUSTOM_DATA
fmt.Println("Scroll event modified")
}
// Ensure the modified event is passed to the next hook
return w32.CallNextHookEx(hook, code, wparam, lparam)
}
return w32.CallNextHookEx(hook, code, wparam, lparam)
}
func main() {
hook = w32.SetWindowsHookEx(w32.WH_MOUSE_LL, MouseProc, 0, 0)
if hook == 0 {
fmt.Println("Failed to set hook")
return
}
defer w32.UnhookWindowsHookEx(hook)
// Keep the program running to listen to mouse events
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
}
更多关于Golang中如何修改底层鼠标事件的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang中如何修改底层鼠标事件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
问题在于你直接修改了钩子回调函数中的原始数据结构,但没有正确重新发送修改后的事件。在低级鼠标钩子中,CallNextHookEx只是将事件传递给下一个钩子,但系统最终处理的是原始事件。要修改系统接收的事件,你需要拦截并阻止原始事件,然后重新发送修改后的事件。
以下是修复后的代码示例:
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
"unsafe"
"github.com/gonutz/w32"
)
const CUSTOM_DATA = 0x1234567
type MSLLHOOKSTRUCT struct {
Pt w32.POINT
MouseData uint32
Flags uint32
Time uint32
DwExtraInfo uintptr
}
var (
hook w32.HHOOK
lastScrollDir int32
lastScrollTime time.Time
)
func MouseProc(code int, wparam w32.WPARAM, lparam w32.LPARAM) w32.LRESULT {
if code >= 0 {
msllhookstruct := (*MSLLHOOKSTRUCT)(unsafe.Pointer(lparam))
if wparam == w32.WM_MOUSEWHEEL && msllhookstruct.DwExtraInfo != CUSTOM_DATA {
scrollDelta := int32(msllhookstruct.MouseData >> 16)
// 检测滚轮漂移的逻辑
now := time.Now()
timeSinceLastScroll := now.Sub(lastScrollTime)
if timeSinceLastScroll < 50*time.Millisecond {
if lastScrollDir > 0 && scrollDelta < 0 {
// 检测到漂移:上次向下滚动,这次收到向上事件
fmt.Printf("Detected drift: last=%d, current=%d\n", lastScrollDir, scrollDelta)
// 阻止原始事件
return 1
} else if lastScrollDir < 0 && scrollDelta > 0 {
fmt.Printf("Detected drift: last=%d, current=%d\n", lastScrollDir, scrollDelta)
return 1
}
}
// 更新最后滚动方向和事件
lastScrollDir = scrollDelta
lastScrollTime = now
// 如果需要修改滚动量,重新发送事件
if scrollDelta != 0 {
// 阻止原始事件
return 1
}
}
}
return w32.CallNextHookEx(hook, code, wparam, lparam)
}
func sendModifiedWheelEvent(x, y int32, delta int32) {
// 准备输入结构
var inputs []w32.MOUSE_INPUT
// 设置鼠标位置
inputs = append(inputs, w32.MOUSE_INPUT{
Type: w32.INPUT_MOUSE,
Mi: w32.MOUSEINPUT{
Dx: x,
Dy: y,
MouseData: uint32(delta << 16),
DwFlags: w32.MOUSEEVENTF_WHEEL,
Time: 0,
DwExtraInfo: CUSTOM_DATA,
},
})
// 发送输入
w32.SendInput(inputs)
}
func main() {
// 安装钩子
hook = w32.SetWindowsHookEx(w32.WH_MOUSE_LL,
syscall.NewCallback(MouseProc),
0,
0)
if hook == 0 {
fmt.Println("Failed to set hook")
return
}
defer w32.UnhookWindowsHookEx(hook)
// 消息循环
var msg w32.MSG
for w32.GetMessage(&msg, 0, 0, 0) > 0 {
w32.TranslateMessage(&msg)
w32.DispatchMessage(&msg)
}
}
对于更完整的实现,你还需要一个单独的goroutine来处理事件重发:
func eventProcessor() {
for {
select {
case event := <-eventQueue:
// 添加延迟以确保原始事件被阻止
time.Sleep(10 * time.Millisecond)
// 发送修改后的事件
w32.SendInput([]w32.MOUSE_INPUT{
{
Type: w32.INPUT_MOUSE,
Mi: w32.MOUSEINPUT{
Dx: event.X,
Dy: event.Y,
MouseData: uint32(event.Delta << 16),
DwFlags: w32.MOUSEEVENTF_WHEEL,
DwExtraInfo: CUSTOM_DATA,
},
},
})
}
}
}
关键点:
- 通过返回非零值来阻止原始事件
- 使用
SendInput重新发送修改后的事件 - 添加适当的延迟以避免竞争条件
- 使用单独的goroutine处理事件重发以避免阻塞钩子回调

