无法正确检测CapsLock密钥状态

时间:2015-07-04 07:54:57

标签: .net vb.net winapi key raw-input

我的RawInput键盘记录实现无法检测 CapsLock 键是否被使用ToUnicodeEx函数来返回mayus或minus字符。

我将描述会发生什么,有些奇怪......

如果我在启用了 CapsLock 键的情况下运行应用程序,那么即使在程序运行后我在执行时禁用了该键,它也会被检测为已启用。

如果我在禁用 CapsLock 键的情况下运行应用程序,则在执行时,即使我在程序运行后启用该键,它也会被检测为已禁用。

因此,例如,如果我在启用 CapsLock 键的情况下运行应用程序,即使我在应用程序运行后禁用该键,该值也始终为True:

Dim isCapsLockPress As Boolean = 
    ((CUShort(InputDevice.NativeMethods.GetKeyState(Keys.Capital)) And &HFFFF) <> 0)

这是相关代码,如果我需要显示任何其他定义,请对其进行评论。

''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Processes WM_INPUT messages to retrieve information about any keyboard events that occur.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="message">
''' The WM_INPUT message to process.
''' </param>
''' ----------------------------------------------------------------------------------------------------
Private Sub ProcessInputCommand(ByVal message As Message)

    Dim dwSize As UInteger = 0

    ' First call to GetRawInputData sets the value of dwSize,
    ' which can then be used to allocate the appropriate amount of memory, storing the pointer in "buffer".
    InputDevice.NativeMethods.GetRawInputData(message.LParam,
                                              NativeMethods.GetRawInputDataCommand.Input,
                                              IntPtr.Zero,
                                              dwSize,
                                              CUInt(Marshal.SizeOf(GetType(InputDevice.NativeMethods.RawInputHeader))))

    Dim buffer As IntPtr = Marshal.AllocHGlobal(CInt(dwSize))

    Try
        ' Check that buffer points to something, 
        ' and if so, call GetRawInputData again to fill the allocated memory with information about the input.
        If (buffer <> IntPtr.Zero) AndAlso
           (InputDevice.NativeMethods.GetRawInputData(message.LParam,
                                                      NativeMethods.GetRawInputDataCommand.Input,
                                                      buffer,
                                                      dwSize,
                                                      CUInt(Marshal.SizeOf(GetType(InputDevice.NativeMethods.RawInputHeader)))) = dwSize) Then

            ' Store the message information in "raw", 
            ' then check that the input comes from a keyboard device before processing it to raise an appropriate KeyPressed event.
            Dim raw As InputDevice.NativeMethods.Rawinput =
                CType(Marshal.PtrToStructure(buffer, GetType(InputDevice.NativeMethods.Rawinput)), InputDevice.NativeMethods.Rawinput)

            If raw.Header.DwType = NativeMethods.DeviceType.Keyboard Then

                ' Filter for Key Down events and then retrieve informationabout the keystroke.
                If (raw.Keyboard.Message = InputDevice.NativeMethods.WindowsMessages.WM_KEYDOWN) OrElse
                   (raw.Keyboard.Message = InputDevice.NativeMethods.WindowsMessages.WM_SYSKEYDOWN) Then

                    Dim key As UShort = raw.Keyboard.VKey

                    ' On most keyboards, "extended" keys such as the arrow or page keys return two codes: 
                    ' 1: the key 's own code, and 2: a "extended key" flag, which translates to 255. 
                    ' The flag isn't useful to us, so it can be disregarded.
                    If key > VKLastKey Then
                        Return
                    End If

                    Dim isShiftPress As Boolean = (InputDevice.NativeMethods.GetAsyncKeyState(Keys.ShiftKey) <> 0)
                    Dim isCapsLockPress As Boolean = ((CUShort(InputDevice.NativeMethods.GetKeyState(Keys.Capital)) And &HFFFF) <> 0)
                    Dim isAltGrPress As Boolean = (InputDevice.NativeMethods.GetAsyncKeyState(Keys.Menu) <> 0)

                    ' Retrieve information about the device and the key that was pressed.
                    Dim dInfo As DeviceInfo = CType(deviceList(raw.Header.HDevice), DeviceInfo)
                    dInfo.KeyNum = key
                    dInfo.Key = CType([Enum].Parse(GetType(Keys), [Enum].GetName(GetType(Keys), key)), Keys)
                    dInfo.Character = Me.GetCharsFromKeys(dInfo.Key,
                                                          isShiftPress Or isCapsLockPress,
                                                          isAltGrPress)

                    ' If the key that was pressed is valid and there was no problem retrieving information on the device,
                    ' raise the KeyPressed event.
                    If (KeyPressedEvent IsNot Nothing) AndAlso (dInfo IsNot Nothing) Then
                        RaiseEvent KeyPressed(Me, New KeyPressedEventArgs(dInfo, Me.GetDevice(message.LParam.ToInt32())))

                    Else
                        Throw New ApplicationException(String.Format("Received Unknown Key: {0}. Possibly an unknown device.", key))

                    End If

                End If

            End If

        End If

    Finally
        Marshal.FreeHGlobal(buffer)

    End Try

End Sub

Private Function GetCharsFromKeys(ByVal key As Keys,
                                  ByVal shift As Boolean,
                                  ByVal altGr As Boolean) As String

    Dim buf As New StringBuilder(256)
    Dim keyboardState As Byte() = New Byte(255) {}

    If shift Then
        keyboardState(CInt(Keys.ShiftKey)) = &HFF
    End If

    If altGr Then
        keyboardState(CInt(Keys.ControlKey)) = &HFF
        keyboardState(CInt(Keys.Menu)) = &HFF
    End If

    Dim rc As Integer
    Do ' Remove dead characters. 
        rc = InputDevice.NativeMethods.ToUnicodeEx(CUInt(key), 0UI, keyboardState, buf, 256, 0UI,
                                                   Me.KeyboardLayoutHandle)
    Loop While rc < 0

    Return buf.ToString()

End Function
  

编辑:

在构建RawInputDevice结构时,我注意到对于dwFlags参数,如果我删除NoLegacy标志解决了问题,但是,我不确定这是否会导致另一个奇怪的行为,所以,这里只删除这个标志是对的吗?:

Public Sub New(ByVal hwnd As IntPtr)

    ' Create an array of all the raw input devices we want to listen to. 
    ' In this case, only keyboard devices.
    Dim rid As InputDevice.NativeMethods.RawInputDevice() =
        New InputDevice.NativeMethods.RawInputDevice(0) {}

    With rid(0)
        .UsUsagePage = 1US
        .UsUsage = 6US
        .HwndTarget = hwnd
        .DwFlags = InputDevice.NativeMethods.RawInputDeviceFlags.NoLegacy Or
                   InputDevice.NativeMethods.RawInputDeviceFlags.InputSink

    End With

    If Not InputDevice.NativeMethods.RegisterRawInputDevices(rid, CUInt(rid.Length), CUInt(Marshal.SizeOf(rid(0)))) Then
        Throw New ApplicationException("Failed to register raw input device(s).")
    End If

    Me.deviceCountB = EnumerateDevices()

End Sub

0 个答案:

没有答案