回 帖 发 新 帖 刷新版面

主题:GetKeyboardState之参数问题,APIer请进

首先说说GetKeyboardState的正确用法,和问题来源吧。。。

GetKeyboardState声明:
Public Declare Function GetKeyboardState Lib "user32" Alias "GetKeyboardState" (pbKeyState As Byte) As Long

我的代码:
Private Sub Timer1_Timer()
    dim kbState(255) as byte,ret as Long
    ret=GetKeyboardState(kbState(0))
    if ret<>0 then
        if (kbState(13) And &H80)=&H80 Then
            msgbox "你按了回车键"
        end if
    end if
end sub

好,以上可以实现实时监测键值。

那么,问题出来了,发现这个函数能由于kbState(0)的地址传入而改变一整个数组,想到传说中的API与C/C++有关,所有这个能参数需要的肯定是一个指针值(地址值)。于是乎我大胆地把函数声明更改如下:

Public Declare Function GetKeyboardState Lib "user32" Alias "GetKeyboardState" (pbKeyState As Long) As Long
但到了这一步,发现声明这里就报错了。[color=FF0000]这是问题之一,参数需要一个地址值,声明成Long居然是错的?Why?[/color]

于是再来,我又把声明改成了:

Public Declare Function GetKeyboardState Lib "user32" Alias "GetKeyboardState" (pbKeyState As Any) As Long
调用改成ret=GetKeyboardState(varptr(kbState(0))
这时不用说,程序撑不到半秒钟崩溃了。[color=FF0000]这是问题之二:为什么同样传入个地址值(varptr取kbState(0)的地址),这样子就不行?[/color]


这个问题有点莫名其妙,但是还是请知道的人说说看吧!

回复列表 (共5个回复)

沙发

好吧,问题结束,是我调试的问题和疏忽
改成这样所有的问题就都解决了:
Public Declare Function GetKeyboardState Lib "user32" (pbKeyState As Long) As Long
调用:ret = GetKeyboardState(ByVal VarPtr(kb(0)))

因为要传入一个地址址,用varptr取得后应用byval传进去,kb(0)的地址值,否则变成地址的地址。

那至于声明成long昨晚为什么不行?可能是太累了,哪里出问题了。。。

板凳

监控特定键可以用GetKeyState,这个更简单

这个函数好像只能监控自身线程的?我运行后,在记事本上狂按回车键也没反应。

如果我写会这样写,可读性好点:
[code=c]
Option Explicit

Private Declare Function GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long

Private Sub Timer1_Timer()
    Dim kbState(255) As Byte, ret As Long
    ret = GetKeyboardState(kbState(0))
    If ret Then
        If kbState(vbKeyReturn) And &H80 Then    '类似的,可以kbState(vbKeyL)等等
            Debug.Print "回车键被按下"
        End If
    End If
End Sub
[/code]

---------------------------
pbKeyState As Byte
这样声明实际上隐含了ByRef,也就是按引用(按址)传递参数,传入的是变量的地址,此时传入的是kbState(0)的地址

pbKeyState As Long
这样声明是要求传入一个Long的地址,理论上是错误的。但你可以用ByVal VarPtr(kb(0))来按值传入一个Long,也就纠正了声明的错误,所以与“pbKeyState As Byte”是等效的
既然这样,你还不如声明“ByVal pbKeyState As Long”,调用时用VarPtr(kbState(0))
由于调用了VarPtr(),效率是不如“pbKeyState As Byte”的,但现在的硬件条件下可以忽略。当然,能高效点为什么不呢?

pbKeyState As Any
实际上,这样声明也是可以的,调用时用kbState(0)或者ByVal VarPtr(kbState(0))都可以。

[quote]那至于声明成long昨晚为什么不行?可能是太累了,哪里出问题了。。。[/quote]
应该是调用时少了ByVal了吧。

一般API里面的默认声明都没什么错误的,没必要修改(极少部分有错误,需要修改)

3 楼

[quote]由于调用了VarPtr(),效率是不如“pbKeyState As Byte”的,但现在的硬件条件下可以忽略。当然,能高效点为什么不呢?
[/quote]

当然,我很同意你的观点。
之所以做一调试更改,是因为以前对API的参数传递与VB里面的参数传递觉得模糊不清。经过时间的磨合和对这个例子的调试,现在突然觉得清晰了。

4 楼

[quote][quote]由于调用了VarPtr(),效率是不如“pbKeyState As Byte”的,但现在的硬件条件下可以忽略。当然,能高效点为什么不呢?
[/quote]

当然,我很同意你的观点。
之所以做一调试更改,是因为以前对API的参数传递与VB里面的参数传递觉得模糊不清。经过时间的磨合和对这个例子的调试,现在突然觉得清晰了。[/quote]

你总算是弄清了大部分API函数就是C标准Dll这个概念。呵呵[em12]

5 楼

我晕,点第一次提示发送错误,再点一次成功,没想到发送错误那次也发送成功了。

我来回复

您尚未登录,请登录后再回复。点此登录或注册