【全局键盘钩子】的事件处理问题,各位老鸟给个方案

清晨曦月 元老
博客专家认证
2008-05-28 11:12:24
情况如下:
用.NET 2008写的全局键盘钩子,分别处理Key_Down和Key_Up——分别引发Key_Down和Key_Up事件,但当吃掉按键消息时,遇到了如下问题:
在Key_Down的代码中,吃掉了某个按键,但由于该代码运行时间较长,没有运行完呢,Key_Up事件就被激活了,结果导致只吃掉了KeyDown而KeyUp被投递到了其他窗口,这是非常令人头疼的,希望用钩子的老鸟给出一个解决方案,但不要告诉我Key_Down事件未完成时让Key_Up永远等待,这种方式会导致效率问题,而且可能导致下一个按键无法被正确处理。
回调部分代码如下:
Private Function KeyBoardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
If _KyesCallNext Then
Dim newPtr As New KeyboardHookStruct
newPtr = CType(Marshal.PtrToStructure(lParam, newPtr.GetType), KeyboardHookStruct)
If nCode = HC_ACTION Then
Dim KeyData As Keys = newPtr.vkCode
Dim e As New System.Windows.Forms.KeyEventArgs(KeyData)
If wParam.ToInt32 = WM_KEYDOWN Or wParam.ToInt32 = WM_SYSKEYDOWN Then
If Not KeyCancel.Contains(e.KeyCode.ToString.ToLower) Then
RaiseEvent KeyDown(e,DownCancel) '111111111111111111
End If
if DownCancel then return 1
ElseIf wParam.ToInt32 = WM_KeyUP Or wParam.ToInt32 = WM_SYSKEYUP Then
RaiseEvent KeyUp(e,UpCancel) '222222222222222222
if UpCancel then return 1
End If
End If
End If
Return CallNextHookEx(m_iKeyHandle, nCode, wParam, lParam)
End Function
以上1处被处理时,2处代码会被激活。
分析时请注意:
1、问题仅限这一处,代码的其他功能绝对没有任何问题
2、不要认为用户会严格的每次只按下一个键,抬起,然后再去按其他键,再抬起:按键可能是组合的,含有ctrl,Shift,Alt,Win等等的,也可能是同时按下多个键铷ABC等,当然这种同时不是绝对的,只是我们写代码要考虑到按键交叉的情况。

要求代码效率一定要高,原因如上所述。

郁闷,在.NET区只能开100的帖子。视情况再加分吧
...全文
356 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
清晨曦月 元老 2008-07-12
  • 打赏
  • 举报
回复
。。。。。。。。。。。。。。最近还是没大有时间,估计没有朋友着急升级吧,这个帖子是要结的,现在有两种解决方案,朋友在帮忙搞,其中一个已经实现了,是利用系统现成的处理方式,没有什么问题,多线程+链表这个方式还在讨论具体的解决方案,估计快了。
mzhao 2008-07-08
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 gyc 的回复:]
引用 17 楼 zcsor 的回复:


你太有才了,就算增加了鼠标控制的全部内容,我也无法用它们来实现我要的键盘功能,你成?

小裤衩子就这么想当然了?


我的意思是,如果.NET 已经实现了, 没有必要在自己弄一个
弄那么多API, 也会让程序变得不稳定的
[/Quote]
但很不幸,.Net没有实现。
如果要收全局钩子,并且(很重要)要防止其他程序得到该键盘消息,只能用API


另外,大家,按照原贴的问题,我觉得多线程是最好的解决方案,不管是将事件记录下来之后再处理,还是每个事件打开一个线程,使用一个非窗体线程处理是最好的方法。

同时,楼主,我当初也写过这样的程序,我的处理程序不复杂,一般不会出问题,但是程序运行很长时间之后会出错,而且用Try...Catch 或On error resume next 或application.onerror等方式都不管用,总会叫出JIT。估计是.Net与HookAPI兼容不好,用C++写一个程序再用.Net调用就没问题。
gyc 2008-07-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 zcsor 的回复:]


你太有才了,就算增加了鼠标控制的全部内容,我也无法用它们来实现我要的键盘功能,你成?

小裤衩子就这么想当然了?
[/Quote]

我的意思是,如果.NET 已经实现了, 没有必要在自己弄一个
弄那么多API, 也会让程序变得不稳定的
tjficcbw 2008-07-02
  • 打赏
  • 举报
回复
我觉得这个问题是不是能这样做,
定义一个全局变量upid 在Key_Down事件的开头设置upid=0计算完了则upid=1
而在Key_Up事件中加一句
if upid=0 then exit sub

这只是实现的思想,要是你的Key_Up必须执行,则可在Key_Down事件的最后加一个执行Key_Up事件的语句
清晨曦月 元老 2008-07-02
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 luxu365 的回复:]
zcsor我才疏学浅,不过我觉得这个问题其实11楼的建议我感觉是最好的,就是吃掉所有的Key_Down和Key_UP事件,同时建一个表将他们存进来,按顺序判断某个事件是否放行,如果放行则发送消息再次模拟这个操作,同时通知拦截部分程序对这个模拟操作予以放行(就像CPU中断一样工作,本来是按顺序执行的,突然插进来一个事情,就请求中断然后记录下中断后的指令等待中断请求的指令处理完再继续往下执行指令。)。为了加快处理速度,你还…
[/Quote]

来写点BLOG顺便看看,思路不错!最近时间确实很紧张,工作上的事情很多,打算过一段时间再深入研究一下这个问题的解决方法。。

感谢这么多人的热心帮助。。



一个关于WORD中组合后的对象设置为嵌入式的帖子一直挂着,大家有空的时候帮忙看一下


http://topic.csdn.net/u/20080612/01/8b6bdba5-45f7-45da-8845-a9734481b678.html
luxu365 2008-07-01
  • 打赏
  • 举报
回复
刚才又仔细想了一下,其实很多地方VB可以参考一些JAVA的变成理念,就是把不同的功能完整的打包到一个功能模块里(不知道我是不是又菜了),单独的功能模款可以完成一个完整的任务,然后留下足够的接口(如参数和返回值)以便和其他功能模块间交流。配合刚才的思路就是,你先建立一个功能模块,专门用于挂钩键盘事件,它有至少两个参数一个是判断任务池是否唯空的返回值和当前是否正在进行处理事件的判定变量值的与结果(当两个值都为F时可进入快速事件处理模块)。另一个参数是事件处理模块给出的允许放行标识变量的值,用来判定当前事件是否是本程序的模拟操作。如果发现第一个参数的值是可以进行快速处理,那就直接在这个模块中就消化掉。如果不行,就把它发给中断处理模块进行排队处理,当次操作实际按照吃掉来做。中断处理模块收到第一个模块发来的处理请求后先将该任务排入列队,然后判断当前排队处理模块是否空闲(第三个模块了),如果是空闲责马上调用它对列队里的事件进行处理。排队处理模块则从列队中的第一个事件开始处理事件,处理完成后检查任务池是否唯空,不为空则循环调用自己直道任务池空为止。处理结果分为两种,一种是不认许放行呢就什么都不做了(因为在第一个模块中就被吃掉了)。如果允许放行则先改变第一个模块的第二个参数要检查的传入值,使其放行下一个相同事件(这里不用考虑是否会碰巧有另一个相同的来自真正键盘的事件,因为他们本来就是一样的,你放行哪个拦截哪个都是一样的)。然后调用键盘模拟模块向计算机模拟一次该事件。至此所用功能完成,你可使他们之间是单线程同步调用。也可以让他们分别在不同的线程里异步工作。

仅个人拙见,仅供参考,些许连参考也谈不上。

也许是因为VB真的是太Basic了所以有些看上去很简单但环境本身未给出直接解决方案的东西我们还是麻烦点写个复杂的东西出来专门应对它,以后若要使用也方便很多。^^
luxu365 2008-07-01
  • 打赏
  • 举报
回复
zcsor我才疏学浅,不过我觉得这个问题其实11楼的建议我感觉是最好的,就是吃掉所有的Key_Down和Key_UP事件,同时建一个表将他们存进来,按顺序判断某个事件是否放行,如果放行则发送消息再次模拟这个操作,同时通知拦截部分程序对这个模拟操作予以放行(就像CPU中断一样工作,本来是按顺序执行的,突然插进来一个事情,就请求中断然后记录下中断后的指令等待中断请求的指令处理完再继续往下执行指令。)。为了加快处理速度,你还可以加一个判断,当事件池是空的时候,且当前没有未处理完的事件可以不进入事件池直接进行正常步骤的处理,这样可以加快一些程序的运行效率。我的思路来源于某些软件对自身退出进程时进行合法退出判断的思路一样,就是变相的应用在这里了。不知道我说的是不是合理,如果显得太幼稚了你还多包涵。
清晨曦月 元老 2008-06-27
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 gyc 的回复:]
2.0 以后,增加了不少 对 鼠标控制的内容。 有必要在使用系统的API吗?
[/Quote]
你太有才了,就算增加了鼠标控制的全部内容,我也无法用它们来实现我要的键盘功能,你成?

小裤衩子就这么想当然了?
gyc 2008-06-26
  • 打赏
  • 举报
回复
2.0 以后,增加了不少 对 鼠标控制的内容。 有必要在使用系统的API吗?
清晨曦月 元老 2008-06-25
  • 打赏
  • 举报
回复
====这个帖子一定是要结的,可最近在学另外一个东东,感觉智商有点……………………正在努力中,12楼的方法还没有试验








先放放
yanlongwuhui 2008-06-23
  • 打赏
  • 举报
回复
还没有人给解决呀,都关注了N久了
mzhao 2008-06-23
  • 打赏
  • 举报
回复
对不起,写错了,是queueuserworkitem
mzhao 2008-06-22
  • 打赏
  • 举报
回复
Private Sub Run(o as object)
Select Case o
... '分析输入键等,包括执行,但注意要访问窗体或其控件时一定要注意线程安全,否则会报错。(上网查,我这里不具体介绍了)
End Select
End Sub
Private Function KeyBoardProc(ByVal nCode As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
If _KyesCallNext Then
Dim newPtr As New KeyboardHookStruct
newPtr = CType(Marshal.PtrToStructure(lParam, newPtr.GetType), KeyboardHookStruct)
If nCode = HC_ACTION Then
Dim KeyData As Keys = newPtr.vkCode
Dim e As New System.Windows.Forms.KeyEventArgs(KeyData)
System.Threading.ThreadPool.QueueForUserItem(addressof run,e.ToInt32) '创建线程,注意因我有一段时间不碰了,可能记得不对,如有错误请自己看参数提示。
End If
End If
Return CallNextHookEx(m_iKeyHandle, nCode, wParam, lParam)
End Function
allanli 2008-06-22
  • 打赏
  • 举报
回复
既然你的Key_Down时间处理时间过长,而Key_Up又必须等Key_Down事件处理完才知道怎么做,
那么必须在Key_Up中等待Key_Down处理完的,这个不是效率不效率的问题,而是必须这么做,别无他法

至于怎么等待那是你的设计能力了,好的算法就不存在效率问题了
个人觉得可以考虑把键盘按键事件缓存起来,然后再处理,直到把所有事件处理完毕
可以用多线程,一个线程负责记录键盘事件,一个负责处理事件.
清晨曦月 元老 2008-06-22
  • 打赏
  • 举报
回复
高人是有的,例如我。。。。。。。。。。。
清晨曦月 元老 2008-06-19
  • 打赏
  • 举报
回复
通过其他方式解决了问题,钩子部分的代码基本没变,不过我还是很想知道这个问题应该是如何解决的
神之泪花 2008-06-19
  • 打赏
  • 举报
回复
关注中...

期待高人出现.
清晨曦月 元老 2008-06-03
  • 打赏
  • 举报
回复
喔耶,又挂了一个,没人愿意碰我的帖子吗,怎么挂了这么多
清晨曦月 元老 2008-05-29
  • 打赏
  • 举报
回复
把处理代码放在一个新线程里面处理,让处理过程得以返回,这个方法倒是想过,可是如果按下多个按键……要用线程池???还得处理临界。。。吗?似乎很不值得这样做。

异步调用?嘛用法
a523194491 2008-05-29
  • 打赏
  • 举报
回复
或者用线程出来
加载更多回复(4)

16,554

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧