User5152
User5152

Reputation: 131

How do I convert ascii integer to corresponding keypress correctly?

When I try converting ASCII integers to keys that have more than one character I always get an incorrect result. A -> z / 0 -> 9 works correctly, but any key press that has more than one character always gives an incorrect result, e.g F1 -> F12 / Numlock 0 -> Numlock 9 / home key etc.

example, ASCII code for F3 is 114 but when I convert 114 to character it always outputs R instead of F3

This is something similar to what I have tried:

(It seems useless in this example, but this isn't exactly how I will be using it)

Dim bind = "114" '(F3) depending on user input
Dim c As Char = bind 'Convert the ASCII integer(bind) to character(c)
TextBox1.text = c 'which in this case outputs as "r"

How do I fix this so that 114 / any other key that has more than one character will output as F3 / their correct value?

Edit, Updated example:

This code is for user defined shortcuts which needs to work outside the application, so I was using GetAsyncKeyState to determine which ASCII integer was pressed, then using that ASCII integer for the bind, but I need to also then convert that integer into a character (F3 in this case) so the user knows which keybind is currently active.

    Do
        ExitLoop = False

        For i = 0 To 255
            result = 0
            result = GetAsyncKeyState(i)
            If result = -32767 Then

                Dim c As Char = Chr(i)      ' Convert ASCII integer to char.
                Bind1.Text = c
                Shortcut1 = i
                ExitLoop = True

            End If
        Next i

    Loop While ExitLoop = False

Upvotes: 1

Views: 1206

Answers (1)

djv
djv

Reputation: 15774

The title is a bit misleading, as it's not the right way to solve your problem. You have GetAsyncKeyState which tells you about the state of the key passed by index according to its Virtual-Key Code. The value returned tells us if the key is currently pressed down. From the MSDN page,

If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState.

Since the return value is a UInt16, and we want to see if the most significant bit is 1, we will And the result with 0xFFFF (in VB, &HFFFF).

The program below will report all keys which are currently pressed, and put them together in a message, and put that message in the title bar of the form. Make a new WinForms project, and paste this code. You may need to delete Dispose from the code behind the designer. The function GetVKeyPressed has a limited number of supported keys, but you can make the required cases according to the Virtual-Key Code page.

Public Class Form1
    Implements IDisposable

    Declare Function GetAsyncKeyState Lib "User32" (ByVal vKey As Integer) As UInt16

    Private timer As New System.Threading.Timer(AddressOf timerCallback, Nothing, -1, -1)

    Private Function GetVKeyPressed(vKey As Integer) As String
        ' see https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
        Select Case vKey
            Case &H30 To &H39 ' 0 to 9
                Return Chr(vKey).ToString()
            Case &H41 To &H5A ' A to Z
                Return Chr(vKey).ToString()
            Case &H70 To &H87
                Return "F" & (vKey - &H70 + 1)
            Case &H1
                Return "LMB"
            Case &H2
                Return "RMB"
            Case Else
                Return "Key not supported yet!"
        End Select
    End Function

    Private Sub timerCallback(state As Object)
        Dim results As New Dictionary(Of Integer, UInt16)()
        For i = 0 To 255
            results.Add(i, GetAsyncKeyState(i))
        Next i
        Dim pressedKeys = String.Join(", ", results.
            Where(Function(kvp) kvp.Value And &HFFFF).
            Select(Function(kvp) GetVKeyPressed(kvp.Key)))
        Try
            Me.Invoke(Sub() Me.Text = pressedKeys)
        Catch ex As ObjectDisposedException
        End Try
        timer.Change(100, -1)
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        timer.Change(100, -1)
    End Sub

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        timer.Change(-1, -1)
    End Sub

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
                timer.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

End Class

Upvotes: 1

Related Questions