GetAsyncKeyState not working with Right Click
|
|
Thread rating:  |
MP - 28 Jun 2007 22:22 GMT I thought this used to work Now it doesn't seem to What am I forgetting?
'(in AutoCad vba) but I should think vb would be similar except for the KeyCodeConstants.vbKeyRButton reference ( = 2)
Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer Private Const VK_RMC = KeyCodeConstants.vbKeyRButton
Public Function UserRightClicked() As Boolean If (0 > GetAsyncKeyState(VK_RMC)) Then UserRightClicked = True End Function
by doesn't seem to work, I mean when used in an error trap it says I didn't hit right click when I did! eg
On Error GoTo ErrCtl ....ask for selection from user oDoc.Utility.GetEntity oEnt, vpt, "Pick text or block "
... if user hits right click selection fails with error sending us to ErrCtl ... I should get msgBox "right" but I don't....
ErrCtl: If UserRightClicked Then MsgBox"Right" Else MsgBox"Nope" End if
MP - 28 Jun 2007 23:05 GMT >I thought this used to work > Now it doesn't seem to > What am I forgetting? > > '(in AutoCad vba) but I should think vb would be similar except for the > KeyCodeConstants.vbKeyRButton reference ( = 2) 'ok this seems to work in limited testing 'in the calling sub I need to hit GetAsyncKeyState to clear the value before trying the line that could throw the error I'm trying to catch 'but that means I need to define GetAsyncKeyState in every scope I want to use the test, or make it public in the .bas mod??? 'is there a right way to do this?
:-) '------------ Sub Main() 'initialize GetAsyncKeyState Call GetAsyncKeyState(anyInteger)
On Error Resume Next' just for this example oDoc.Utility.GetEntity oEnt, vpt, "Pick text or block" If Err Then If UserRightClicked Then MsgBox "Right" etc '--------------
'in .Bas mod Private Const VK_RBUTTON As Long = &H2& Public Function UserRightClicked() As Boolean If GetAsyncKeyState(VK_RBUTTON) = 1 Then UserRightClicked = True End Function
Steve Gerrard - 29 Jun 2007 02:44 GMT > 'ok this seems to work in limited testing > 'in the calling sub I need to hit GetAsyncKeyState to clear the value before [quoted text clipped - 3 lines] > 'is there a right way to do this? > :-) You could just make a Public sub in the .bas module to get entities, and put the calls in there. With appropriate types for Doc, Entity, and Vpt, something like:
Public Sub GetDocEntity(Doc As X, Entity As Y, Vpt As Z, Msg As String) 'initialize GetAsyncKeyState Call GetAsyncKeyState(anyInteger) On Error Resume Next' just for this example Doc.Utility.GetEntity Entity, Vpt , Msg If Err.Number <> 0 Then If GetAsyncKeyState(VK_RBUTTON) = 1 Then MsgBox "Right" End If End If End Sub
' ....
Sub Main() GetDocEntity oDoc, oEnt, vpt, "Pick text or block" End Sub
Ken Halter - 28 Jun 2007 23:06 GMT >I thought this used to work > Now it doesn't seem to [quoted text clipped - 10 lines] > If (0 > GetAsyncKeyState(VK_RMC)) Then UserRightClicked = True > End Function This works in VB6 - need a form with a timer '============== Option Explicit
Private Declare Function GetAsyncKeyState Lib "user32.dll" (ByVal vKey As Long) As Integer Private Const VK_RBUTTON As Long = &H2
Private Sub Form_Load() With Timer1 .Enabled = False .Interval = 100 .Enabled = True End With End Sub
Private Sub Timer1_Timer() If GetAsyncKeyState(VK_RBUTTON) < 0 Then Debug.Print "RButton Down" Else Debug.Print "RButton Up" End If End Sub
 Signature Ken Halter - MS-MVP-VB - Please keep all discussions in the groups.. In Loving Memory - http://www.vbsight.com/Remembrance.htm
MP - 28 Jun 2007 23:33 GMT >>I thought this used to work >> Now it doesn't seem to [quoted text clipped - 34 lines] > End If > End Sub Thanks for the response Ken, What I'm trying to do is use it in error traps to detect which key caused the error In my case space, esc, enter, or right click can be meaningful in response to an autocad command. There seems to be something about having to initialize the call before checking the value again to see if it was pressed "since the previous call to GetAsnyc" Due to the Hi bit Lo bit values (which I don't know how to get at) The hi bit being if the key is currently pressed and the lo bit if it was pressed since last time the function was called (or is that visa versa ? <g>) Any way thanks for looking at it. The workaround I posted just before your response seems to work on limited testing but doesn't seem to be an elegant solution, having to call it directly, before letting the error check call the test function "UserRightClicked" etc.
Mark
Mike Williams - 29 Jun 2007 12:59 GMT > The workaround I posted seems to work on limited testing but doesn't seem > to be an elegant solution . . . I'm not sure how your mention of AutoCad VBA fits into this but I assume you are trying to detect the current state of the right mouse button system wide from your VB or VBA program? In that case a low level hook might be the best solution. When hooking, unless you jump through lots of hoops to achieve "safe in the IDE" hooking, you obviously need to be careful when working in the VB IDE that you close your app properly using the normal Close button (and not use the VB Run / End menu while the hook is still on), but in a compiled exe there are no such problems. Try the following code. Paste the first block into a VB Form and the second block into a standarde code module. At runtime your app can check the current state of the right mouse button at any time merely by checking the value of the Publicly declared variable RightButton, which will return True or False:
' *** START OF FORM CODE *** Option Explicit Private Sub Form_Load() SetMouseHook End Sub
Private Sub Form_Unload(Cancel As Integer) ReleaseMouseHook End Sub ' *** END OF FORM CODE *** ' ' *** START OF MODULE CODE *** Option Explicit Private Declare Function SetWindowsHookEx _ Lib "user32" Alias "SetWindowsHookExA" _ (ByVal idHook As Long, ByVal lpfn As Long, _ ByVal hmod As Long, ByVal dwThreadId As Long) As Long Private Declare Function UnhookWindowsHookEx _ Lib "user32" (ByVal hHook As Long) As Long Private Const WH_MOUSE_LL As Long = 14 Private Declare Function CallNextHookEx _ Lib "user32" (ByVal hHook As Long, _ ByVal nCode As Long, ByVal wParam As Long, _ lParam As Any) As Long Private Const HC_ACTION = 0 Private Const WM_RBUTTONDOWN As Long = &H204 Private Const WM_RBUTTONUP As Long = &H205 Private Hook As Long Public RightButton As Boolean
Public Sub SetMouseHook() Hook = SetWindowsHookEx(WH_MOUSE_LL, _ AddressOf MouseProc, App.hInstance, 0) End Sub
Public Sub ReleaseMouseHook() UnhookWindowsHookEx Hook End Sub
Private Function MouseProc(ByVal nCode As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long If (nCode = HC_ACTION) Then Select Case wParam Case WM_RBUTTONDOWN: RightButton = True Case WM_RBUTTONUP: RightButton = False End Select End If ' Done our job so allow the system to carry on as normal MouseProc = CallNextHookEx _ (0, nCode, wParam, ByVal lParam) End Function ' *** START OF MODULE CODE ***
DanS - 30 Jun 2007 02:57 GMT >> The workaround I posted seems to work on limited testing but doesn't >> seem to be an elegant solution . . . [quoted text clipped - 65 lines] > End Function > ' *** START OF MODULE CODE *** I was going to offer a mouse hook also, but since this was VBA under AutoCAD, I wasn't sure of the implications since it's not just plain VB that I'm used to using.
Anyway, the question is, in the above code, wouldn't it be better to not use the LL hook, with a global scope, but instead just use a regular mouse hook on the app threadID ?
Or is there some other reason maybe tied to VBA that you would use the LL hook here ?
DanS
Mike Williams - 30 Jun 2007 06:09 GMT > I was going to offer a mouse hook also, but since this was > VBA under AutoCAD, I wasn't sure of the implications > since it's not just plain VB that I'm used to using. Anyway, > the question is, in the above code, wouldn't it be better to > not use the LL hook, with a global scope, but instead just > use a regular mouse hook on the app threadID ? You're probably right. It's just that I wasn't quite sure exactly what the OP was doing and I've never actually used VBA so I don't really know what is involved there. I just thought I'd throw the LL Hook into the pot just in case it was any use to him.
Mike
Ken Halter - 29 Jun 2007 15:40 GMT GetKeyboardState may be a better option (if you don't go with the hook). GetAsyncKeyState also tells you if something's been pressed since last call (see MSDN). May be confusing for your purposes. '=============== Option Explicit
Private Declare Function _ GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long
Private Function MouseIsDown() As Boolean Const VK_LBUTTON = &H1 Dim b(255) As Byte Call GetKeyboardState(b(0)) If b(VK_LBUTTON) > 127 Then 'Mouse is down MouseIsDown = True End If End Function
Private Function RMouseIsDown() As Boolean Const VK_RBUTTON = &H2 Dim b(255) As Byte Call GetKeyboardState(b(0)) If b(VK_RBUTTON) > 127 Then 'RMouse is down RMouseIsDown = True End If End Function
Private Sub Form_Load() With Timer1 .Interval = 100 .Enabled = True End With End Sub
Private Sub Timer1_Timer() Me.Caption = "L-" & MouseIsDown & " R-" & RMouseIsDown End Sub '===============
This VB6 code needs a new project with an empty form. It'll build the form and show the states of all keys and mouse buttons. It also shows that all keys and buttons are "toggle" buttons, like CapsLock, etc. '=================== Option Explicit
Private Declare Function _ GetKeyboardState Lib "user32" (pbKeyState As Byte) As Long Private WithEvents Timer1 As Timer Private mobjLabels(255) As Label
Private Sub Form_Load() Dim i As Integer Dim j As Integer Dim iIndex As Integer Const STARTXY = 8
Me.ScaleMode = vbPixels
For i = 0 To 15 For j = 0 To 15 iIndex = i * 16 + j Set mobjLabels(iIndex) _ = Controls.Add("VB.Label", "lbl" & iIndex)
With mobjLabels(iIndex) .BorderStyle = 1 .Move 30 * j + STARTXY, 21 * i + STARTXY, 26, 17 .Caption = "00" .BackColor = vbButtonFace .Visible = True .ZOrder End With Next Next
Set Timer1 = Controls.Add("VB.Timer", "WhoCares") With Timer1 .Interval = 80 .Enabled = True End With
Me.Height = 400 * Screen.TwipsPerPixelY Me.Width = 500 * Screen.TwipsPerPixelX
End Sub
Private Sub Timer1_Timer() Dim btArray(255) As Byte Dim sList As String Dim i As Integer
Call GetKeyboardState(btArray(0))
For i = 0 To 255 With mobjLabels(i) .Caption = Right$("0" & Hex$(btArray(i)), 2) Select Case btArray(i) Case 0 .BackColor = vbButtonFace Case Is < 127 .BackColor = vbGreen Case Else .BackColor = vbRed sList = sList & GetChar(i) & " " End Select End With Next
Me.Caption = "Keys pressed now: " & sList
End Sub
Private Function GetChar(ByVal ByteIn As Byte) As String
GetChar = Right$("0" & Hex$(ByteIn), 2)
If ByteIn < 32 Or ByteIn > 127 Then ByteIn = 63 '="?" End If
GetChar = GetChar & "(" & Chr$(ByteIn) & ")"
End Function '===================
 Signature Ken Halter - MS-MVP-VB - Please keep all discussions in the groups.. In Loving Memory - http://www.vbsight.com/Remembrance.htm
|
|
|