Announcement

Collapse
No announcement yet.

GetKeyState vs GetAsyncKeyState

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • GetKeyState vs GetAsyncKeyState

    I wanted to use Ctrl+WheelMouse to change font size in an app.

    The test code below shows that GetAsyncKeyState is True for one additional scroll after releasing the Ctrl key, whereas GetKeyState is False immediately. (Since I use Time$, Scroll more than a second apart to see results)

    Code:
    'Compilable Example:
    #Compile Exe
    #Dim All
    %Unicode = 1
    #Include "Win32API.inc"
    Global hDlg As Dword
    Function PBMain() As Long
       Dialog Default Font "Tahoma", 36,1
       Dialog New Pixels, 0, "PowerBASIC",,,300,50, %WS_OverlappedWindow To hDlg
       Dialog Show Modal hDlg Call DlgProc
    End Function
    
    CallBack Function DlgProc() As Long
       Select Case Cb.Msg
          Case %WM_MouseWheel
    '         If GetAsyncKeyState(%VK_Control) Then       'after key release, 1st time is True but False after that
             If GetKeyState(%VK_Control) Then
                Select Case Hi(Integer,Cb.WParam)
                   Case < 0 : Dialog Set Text hDlg, Time$
                   Case > 0 : Dialog Set Text hdlg, Time$
                End Select
             End If
       End Select
    End Function
    So for setting font size in my larger app, GetKeyState would seem to be the right coding choice.

    But, in the larger app, both GetKeyState and GetAsyncKeyState are True for one additional scroll after releasing the Ctrl key, so there's something more to this that my simple example does not clarify.

  • #2
    Hi gary,

    Toggleling the CTRL key, GetKeyState() will set the low-order bit to 1.
    When the key is down, the high-order word bit will be 1.

    To get the down state, use...
    IF (GetKeyState(%VK_Control) AND &H8000) THEN ...

    Comment


    • #3
      Hey Pierre!

      Thank you for the comment but I'm not sure that code works as I understand your comments to suggest.

      Here's another version of using your suggestion, where I want the mouse wheel to run ZoomIn or ZoomOut only if the Ctrl key is down. From what you wrote, I'd expect this code to work that way but it does not. Once I release the Ctrl key, ZoomOut/ZoomIn are called the next time I use the mousewheel.

      Code:
            Case %WM_MouseWheel
               If (GetAsyncKeyState(%VK_Control) And &H8000) Then      '0 if not pressed - status at time of event
                  Select Case Hi(Integer,Cb.WParam)    'note the use of Integer
                     Case < 0 : ZoomOut
                     Case > 0 : ZoomIn
                  End Select
               End
      Have I misunderstood how your suggestion works?

      Comment


      • #4
        What if you try this...

        Code:
        #COMPILE EXE '#Win#
        #DIM ALL
        #INCLUDE "Win32api.inc"
        
        GLOBAL hDlg AS DWORD
        '_____________________________________________________________________________
        
        CALLBACK FUNCTION DlgProc() AS LONG
         LOCAL KeyState AS WORD
        
         SELECT CASE CB.MSG
           CASE %WM_MOUSEWHEEL
             KeyState = (GetAsyncKeyState(%VK_CONTROL) AND &H8000) 'GetAsyncKeyState test
             'KeyState = (GetKeyState(%VK_CONTROL) AND &H8000)     'GetKeyState test
             IF KeyState THEN
               SELECT CASE HI(INTEGER,CBWPARAM)
                 CASE < 0 : DIALOG SET TEXT hDlg, "CTRL-Down-ZoomOut " & HEX$(KeyState, 8)
                 CASE > 0 : DIALOG SET TEXT hdlg, "CTRL-Down-ZoomIn  " & HEX$(KeyState, 8)
               END SELECT
             ELSE
               DIALOG SET TEXT hdlg, "CTRL key is not down " & HEX$(KeyState, 8)
             END IF
         END SELECT
        
        END FUNCTION
        '_____________________________________________________________________________
        
        FUNCTION PBMAIN() AS LONG
         LOCAL hIcon AS DWORD
        
         DIALOG FONT "Segoe UI", 9
         DIALOG NEW %HWND_DESKTOP, "Use mouse wheel and CTRL key", , , 200, 50, _
         %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_SIZEBOX OR %WS_SYSMENU, %WS_EX_LEFT TO hDlg
        
         hIcon = ExtractIcon(GetModuleHandle(""), "C:\Windows\system32\DDORes.dll", 27)
         SetClassLong(hDlg, %GCL_HICON, hIcon)
        
         DIALOG SHOW MODAL hDlg CALL DlgProc
         DestroyIcon(hIcon)
        
        END FUNCTION
        '_____________________________________________________________________________
        '

        Comment


        • #5
          Howdy, Pierre!

          Is what you posted not the same as I put in #3, except you first assign the results to a variable? How does that change the results?

          ... added ... and the answer is because you're ANDing with a WORD?

          Comment


          • #6
            Hey Gary,
            Yes, both are the same and same result should be expected.
            Variable assignment was made for clarity.
            KeyState is defined as a WORD because GetAsyncKeyState() and GetKeyState() return a 2 bytes value.

            So, you still have unexpected result I guess.


            Comment


            • #7
              Also, note that GetAsyncKeyState() should be used over GetKeyState().
              I used both for demo purpose.

              Bill say: The key status returned from GetKeyState() changes as a thread reads key messages from its message queue. The status does not reflect the interrupt-level state associated with the hardware. Use the GetAsyncKeyState() to determines whether a key is up or down at the time the function is called.

              Comment


              • #8
                Hey,
                I know you use Windows 10, you know I use Windows 7.
                When I boot under 10, I know that it's easy for me to overload the OS by fast scrolling with my Free-Wheel mouse.
                Window will then produce many BEEPs to protest. This never happened to me in 7.
                You may explore this avenue to explain the difficulties you got...

                (Note: I won't boot in 10 because of a new Ten update will corrupt the allocation table of 7/10 shared partitions. I'll wait for a new update... Again...)

                Comment


                • #9
                  Gary,
                  So... To be clear, I can reproduce the problem you got using your first post code with
                  GetKeyState() or GetAsyncKeyState() and I can get rid of it by ANDing &h8000, so I'll wait for your next try...

                  Pierre

                  Code:
                  'Compilable Example:
                  #Compile Exe
                  #Dim All
                  %Unicode = 1
                  #Include "Win32API.inc"
                  Global hDlg As Dword
                  Function PBMain() As Long
                     Dialog Default Font "Tahoma", 36,1
                     Dialog New Pixels, 0, "PowerBASIC",,,300,50, %WS_OverlappedWindow To hDlg
                     Dialog Show Modal hDlg Call DlgProc
                  End Function
                  
                  CallBack Function DlgProc() As Long
                     Select Case Cb.Msg
                        Case %WM_MouseWheel
                  '         If GetAsyncKeyState(%VK_Control) Then       'after key release, 1st time is True but False after that
                  '         If GetKeyState(%VK_Control) Then 'Better to use GetAsyncKeyState() instead...
                           If (GetAsyncKeyState(%VK_Control) AND &h8000) Then
                              Select Case Hi(Integer,Cb.WParam)
                                 Case < 0 : Dialog Set Text hDlg, Time$
                                 Case > 0 : Dialog Set Text hdlg, Time$
                              End Select
                           End If
                     End Select
                  End Function

                  Comment


                  • #10
                    From the above it seems pretty clear that the Low Order Bit returned by GetAsyncKeyState() was messing up Gary's code.

                    Just BTW, is there any particular reason for going this route rather than using the fwKeys value passed with the WM_MouseWheel message to detect the state of the Ctrl Key?
                    Code:
                       CASE %WM_MouseWheel
                         IF LO(WORD, CBWPARAM) = %MK_CONTROL THEN
                           SELECT CASE HI(INTEGER, CBWPARAM)
                             CASE < 0 : DIALOG SET TEXT hDlg, "CTRL-Down-ZoomOut " & Time$
                             CASE > 0 : DIALOG SET TEXT hdlg, "CTRL-Down-ZoomIn  " & Time$
                           END SELECT
                         ELSE
                           DIALOG SET TEXT hdlg, "CTRL key is not down " & HEX$(KeyState, 4) & " " & Time$
                         END IF
                    Rgds, Dave

                    Comment


                    • #11
                      Yes, the least significant bit was also my first deduction as shown in my first post.
                      And then Gary's answer lead me to think that it was not working for him, so I hope he will tell us more...

                      About WM_MouseWheel message key state, I also think this is the most effective way, but since the focus
                      was on GetAsyncKeyState() and GetKeyState() it was hard to resolve Gary's problem without those.
                      Note that GetAsyncKeyState() still may be usefull if you want to get ALT key because WM_MouseWheel message
                      won't reveal it.

                      Here is a view of WM_MouseWheel messages.

                      Code:
                      #COMPILE EXE '#Win#
                      #DIM ALL
                      #INCLUDE "Win32api.inc"
                      
                      %LabelInfo = 101
                      
                      GLOBAL hDlg AS DWORD
                      '_____________________________________________________________________________
                      
                      CALLBACK FUNCTION DlgProc() AS LONG
                       LOCAL  sInfo      AS STRING
                       LOCAL  KeyDown    AS WORD
                       LOCAL  WheelDelta AS INTEGER
                       LOCAL  WheelRoll  AS INTEGER
                       STATIC WheelMeter AS LONG
                      
                       SELECT CASE CB.MSG
                      
                         CASE %WM_MOUSEWHEEL
                           WheelDelta  = GET_WHEEL_DELTA_WPARAM(CBWPARAM) '%WHEEL_DELTA = 120
                           WheelRoll   = WheelDelta \ %WHEEL_DELTA
                           WheelMeter += WheelRoll
                           sInfo = "WM_MOUSEWHEEL info..."                         & $CRLF & $CRLF & _
                                   "wParam:       " & $TAB & HEX$(CBWPARAM, 8)     & $CRLF & _
                                   "lParam:       " & $TAB & HEX$(CBLPARAM, 8)     & $CRLF & $CRLF & _
                                   "WHEEL_DELTA:  " & $TAB & FORMAT$(WheelDelta)   & $CRLF & _
                                   "WheelRoll:    " & $TAB & FORMAT$(WheelRoll)    & $CRLF & _
                                   "Meter:        " & $TAB & FORMAT$(WheelMeter)   & $CRLF & _
                                   "Going " & IIF$(WheelDelta > 0, "Up.", "Down.") & $CRLF
                           KeyDown = GET_KEYSTATE_WPARAM(CBWPARAM) 'LO(WORD, CBWPARAM)
                           IF (KeyDown AND %MK_CONTROL)                THEN sInfo &= "Control, "
                           IF (KeyDown AND %MK_LBUTTON)                THEN sInfo &= "Left button, "
                           IF (KeyDown AND %MK_MBUTTON)                THEN sInfo &= "Middle button, "
                           IF (KeyDown AND %MK_RBUTTON)                THEN sInfo &= "Right button, "
                           IF (KeyDown AND %MK_SHIFT)                  THEN sInfo &= "Shift, "
                           IF (KeyDown AND %MK_XBUTTON1)               THEN sInfo &= "xButton 1, "
                           IF (KeyDown AND %MK_XBUTTON2)               THEN sInfo &= "xButton 2, "
                           IF (GetAsyncKeyState(%VK_LMENU) AND &H8000) THEN sInfo &= "Left Alt, "
                           IF (GetAsyncKeyState(%VK_RMENU) AND &H8000) THEN sInfo &= "Right Alt, "
                           IF ASC(sInfo, -1) = 32 THEN
                             sInfo = LEFT$(sInfo, -2) & "."
                           ELSE
                             sInfo &= "No Ctrl, Shift, or Alt key pressed, nor L-M-R-X1-X2 mouse button."
                           END IF
                           sInfo &= $CRLF & "Now at position " & FORMAT$(GET_X_LPARAM(CBLPARAM)) & " x " & FORMAT$(GET_Y_LPARAM(CBLPARAM)) 'LO(INTEGER, CBLPARAM) & HI(INTEGER, CBLPARAM)
                           CONTROL SET TEXT hDlg, %LabelInfo, sInfo
                      
                       END SELECT
                      
                      END FUNCTION
                      '_____________________________________________________________________________
                      
                      FUNCTION PBMAIN() AS LONG
                       LOCAL hIcon AS DWORD
                      
                       DIALOG FONT "Segoe UI", 9
                       DIALOG NEW %HWND_DESKTOP, "Mouse wheel info", , , 230, 100, _
                       %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_SIZEBOX OR %WS_SYSMENU, %WS_EX_LEFT TO hDlg
                      
                       hIcon = ExtractIcon(GetModuleHandle(""), "C:\Windows\system32\DDORes.dll", 27)
                       SetClassLong(hDlg, %GCL_HICON, hIcon)
                      
                       CONTROL ADD LABEL, hDlg, %LabelInfo, "WM_MOUSEWHEEL info...", 5, 5, 220, 90
                      
                       DIALOG SHOW MODAL hDlg CALL DlgProc
                       DestroyIcon(hIcon)
                      
                      END FUNCTION
                      '_____________________________________________________________________________
                      '
                      Last edited by Pierre Bellisle; Yesterday, 09:39 AM.

                      Comment


                      • #12
                        Sorry guys ... I got pulled away, in a "squirrel" kind of way.

                        Pierre,
                        Once I switched over to your suggestion of using a variable the code performed as intended - no errant responses to the mousewheel after releasing the control button. I did not have to use the AND construct, as this example shows. In my test, the a LONG or WORD variable worked fine.

                        Code:
                        'Compilable Example:
                        #Compile Exe
                        #Dim All
                        %Unicode = 1
                        #Include "Win32API.inc"
                        Global hDlg As Dword
                        Function PBMain() As Long
                           Dialog Default Font "Tahoma", 36,1
                           Dialog New Pixels, 0, "PowerBASIC",,,300,50, %WS_OverlappedWindow To hDlg
                           Dialog Show Modal hDlg Call DlgProc
                        End Function
                        
                        CallBack Function DlgProc() As Long
                           Select Case Cb.Msg
                              Case %WM_MouseWheel
                                 Local Result As Word
                                 Result = GetKeyState(%VK_Control)
                                 If Result Then
                                    Select Case Hi(Integer,Cb.WParam)
                                       Case < 0 : Dialog Set Text hDlg, Time$
                                       Case > 0 : Dialog Set Text hdlg, Time$
                                    End Select
                                 End If
                           End Select
                        End Function
                        However, the use of AND, as in this example, fails - after releasing the control button, the mousewheel gives a response.

                        Code:
                        'Compilable Example:
                        #Compile Exe
                        #Dim All
                        %Unicode = 1
                        #Include "Win32API.inc"
                        Global hDlg As Dword
                        Function PBMain() As Long
                           Dialog Default Font "Tahoma", 36,1
                           Dialog New Pixels, 0, "PowerBASIC",,,300,50, %WS_OverlappedWindow To hDlg
                           Dialog Show Modal hDlg Call DlgProc
                        End Function
                        
                        CallBack Function DlgProc() As Long
                           Select Case Cb.Msg
                              Case %WM_MouseWheel
                                 If GetKeyState(%VK_Control) And &H8000 Then
                                    Select Case Hi(Integer,Cb.WParam)
                                       Case < 0 : Dialog Set Text hDlg, Time$
                                       Case > 0 : Dialog Set Text hdlg, Time$
                                    End Select
                                 End If
                           End Select
                        End Function

                        Comment


                        • #13
                          Dave!

                          So far, there are 2 smart people in this thread, and then there is me!

                          Just BTW, is there any particular reason for going this route rather than using the fwKeys value passed with the WM_MouseWheel message to detect the state of the Ctrl Key?
                          Umm .... that would be because I did not know Cb.Wparam could return the state of the control key.

                          Comment


                          • #14
                            https://docs.microsoft.com/en-us/win.../wm-mousewheel

                            Just control, shift and mouse buttons. (or shift and one or more mouse buttons, etc, etc)

                            Cheers,
                            Dale

                            Comment


                            • #15
                              Hi Dale!

                              I have a bad habit of using a function with a %WM_XX section of code, rather than using LO/HI to capture info that the message provides. That bad habit bit me this time.

                              I especially find myself using GetCursorPos when the information is available from the parent %WM_XX message parameters.

                              Comment


                              • #16
                                For lurkers, in WM_MOUSEWHEEL the position is in the LPARAM as two words.
                                Dale

                                Comment


                                • #17
                                  Gary,
                                  If GetKeyState(%VK_Control) And &H8000 Then..

                                  is not the same as:

                                  If ( GetKeyState(%VK_Control) And &H8000 ) Then
                                  Rgds, Dave

                                  Comment


                                  • #18
                                    Howdy, Dave!

                                    Explain please.

                                    ... added ... so, with (), the AND is treated as a bitwise operator. without (), it's a Boolean operation ... something like that?

                                    Comment


                                    • #19
                                      Dale,
                                      Not really two words.
                                      The position is in the LPARAM as two 16 bits signed integer.
                                      So INTEGER% in PowerBASIC speaking.

                                      One may also use those MS macros to get the x, y values.

                                      MACRO GET_X_LPARAM(lp)=LO(INTEGER, lp)
                                      MACRO GET_Y_LPARAM(lp)=HI(INTEGER, lp)

                                      Bill say: Do not use the LOWORD or HIWORD macros to extract the x- and y- coordinates of the cursor position because these macros return incorrect results on systems with multiple monitors. Systems with multiple monitors can have negative x- and y- coordinates, and LOWORD and HIWORD treat the coordinates as unsigned quantities.

                                      Comment


                                      • #20
                                        Originally posted by Gary Beene View Post
                                        Howdy, Dave!

                                        Explain please.

                                        ... added ... so, with (), the AND is treated as a bitwise operator. without (), it's a Boolean operation ... something like that?

                                        See "Short Circuit Evaluation" under IF in Help

                                        To give short-circuit optimization an extra boost, AND and OR are treated as a Boolean operator rather than a bitwise operator, and this can sometimes produce unexpected results. For example, consider the following expression:

                                        a& = 4

                                        b& = 2

                                        IF a& AND b& THEN CALL ShowText("TRUE") ELSE CALL ShowText("FALSE")

                                        Applying the traditional BASIC bitwise evaluation, you would expect to see FALSE displayed because (4 AND 2) = 0. Due to the short-circuit optimization though, each value is treated as a Boolean, just as if you had written:

                                        IF ISTRUE a& AND ISTRUE b& THEN ...

                                        If you believe this may be a problem for your particular code, you can disable the short-circuit evaluation by surrounding the entire conditional expression in parentheses:

                                        IF (a& AND b&) THEN CALL ShowText("TRUE") ELSE CALL ShowText("FALSE")

                                        The parentheses force the entire expression to be evaluated, so AND reverts to being a bitwise operator.

                                        Comment

                                        Working...
                                        X