C# How to Translate Virtual Keycode to Char

Convert keycode to char/string

This is probably what you really want (bit late, but hope this will help someone else), converting the keycode directly to the character the key prints.

First add this directive into your class:

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int ToUnicode(
uint virtualKeyCode,
uint scanCode,
byte[] keyboardState,
StringBuilder receivingBuffer,
int bufferSize,
uint flags
);

Then use this if you just want to ignore the shift modifier

StringBuilder charPressed = new StringBuilder(256);
ToUnicode((uint)keyCode, 0, new byte[256], charPressed, charPressed.Capacity, 0);

Now just call charPressed.ToString() to get your key.

If you want the shift modifier, you can use something like this to make it easier

static string GetCharsFromKeys(Keys keys, bool shift)    
{
var buf = new StringBuilder(256);
var keyboardState = new byte[256];
if (shift)
{
keyboardState[(int)Keys.ShiftKey] = 0xff;
}
ToUnicode((uint)keys, 0, keyboardState, buf, 256, 0);
return buf.ToString();
}

How to convert a virtual-key code to a character according to the current keyboard layout?

The correct solution is the ToUnicode WinAPI function:

[DllImport("user32.dll")]
public static extern int ToUnicode(uint virtualKeyCode, uint scanCode,
byte[] keyboardState,
[Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
StringBuilder receivingBuffer,
int bufferSize, uint flags);

One way to wrap this into a sensible, convenient method would be:

static string GetCharsFromKeys(Keys keys, bool shift, bool altGr)
{
var buf = new StringBuilder(256);
var keyboardState = new byte[256];
if (shift)
keyboardState[(int) Keys.ShiftKey] = 0xff;
if (altGr)
{
keyboardState[(int) Keys.ControlKey] = 0xff;
keyboardState[(int) Keys.Menu] = 0xff;
}
WinAPI.ToUnicode((uint) keys, 0, keyboardState, buf, 256, 0);
return buf.ToString();
}

Now we can retrieve characters and actually get the expected results:

Console.WriteLine(GetCharsFromKeys(Keys.E, false, false));    // prints e
Console.WriteLine(GetCharsFromKeys(Keys.E, true, false)); // prints E

// Assuming British keyboard layout:
Console.WriteLine(GetCharsFromKeys(Keys.E, false, true)); // prints é
Console.WriteLine(GetCharsFromKeys(Keys.E, true, true)); // prints É

It is also possible to use ToUnicodeEx to retrieve the characters for a keyboard layout that is not the currently active one. The signature is the same except for one extra parameter, the input locale ID, which can be retrieved using the LoadKeyboardLayout function.

Convert character to the corresponding virtual-key code

You should be clearer in what your requirements are, more specifically in what you consider to be an appropriate key code. The VkKeyScan as specified in it's documentation returns the virtual-key code in the low-order byte and the shift state in the high-byte of the return value.

This is demonstrated in the code snippet below that uses the '(' character as input for the VkKeyScan method.

[DllImport("user32.dll")]static extern short VkKeyScan(char ch);

static void Main(string[] args)
{
var helper = new Helper { Value = VkKeyScan('(') };

byte virtualKeyCode = helper.Low;
byte shiftState = helper.High;

Console.WriteLine("{0}|{1}", virtualKeyCode, (Keys)virtualKeyCode);
Console.WriteLine("SHIFT pressed: {0}", (shiftState & 1) != 0);
Console.WriteLine("CTRL pressed: {0}", (shiftState & 2) != 0);
Console.WriteLine("ALT pressed: {0}", (shiftState & 4) != 0);
Console.WriteLine();

Keys key = (Keys)virtualKeyCode;

key |= (shiftState & 1) != 0 ? Keys.Shift : Keys.None;
key |= (shiftState & 2) != 0 ? Keys.Control : Keys.None;
key |= (shiftState & 4) != 0 ? Keys.Alt : Keys.None;

Console.WriteLine(key);
Console.WriteLine(new KeysConverter().ConvertToString(key));
}

[StructLayout(LayoutKind.Explicit)]
struct Helper
{
[FieldOffset(0)]public short Value;
[FieldOffset(0)]public byte Low;
[FieldOffset(1)]public byte High;
}

Running this snippet will result in the following output:

// 56|D8
// SHIFT pressed: True
// CTRL pressed: False
// ALT pressed: False
//
// D8, Shift
// Shift+8

convert a keycode to the relevant display character

The trick is to use a set of user32.dll functions: GetWindowThreadProcessId, GetKeyboardLayout, GetKeyboardState and ToUnicodeEx.

  1. Use the GetWindowThreadProcessId function with your control handle to achieve the relevant native thread id.
  2. Pass that thread id to GetKeyboardLayout to get the current keyboard layout.
  3. Call GetKeyboardState to get the current keyboard state. This helps the next method to decide which character to generate according to modifiers state.
  4. Finally, call the ToUnicodeEx function with the wanted virtual key code and scan code (those two can be the same), the current keyboard state, a string builder as a string holder (to hold the result), no flags (0), and the current keyboard layout pointer.

If the result is not zero, just return the first returned character.

  public class KeyboardHelper
{
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
private static extern int ToUnicodeEx(
uint wVirtKey,
uint wScanCode,
Keys[] lpKeyState,
StringBuilder pwszBuff,
int cchBuff,
uint wFlags,
IntPtr dwhkl);

[DllImport("user32.dll", ExactSpelling = true)]
internal static extern IntPtr GetKeyboardLayout(uint threadId);

[DllImport("user32.dll", ExactSpelling = true)]
internal static extern bool GetKeyboardState(Keys[] keyStates);

[DllImport("user32.dll", ExactSpelling = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hwindow, out uint processId);

public static string CodeToString(int scanCode)
{
uint procId;
uint thread = GetWindowThreadProcessId(Process.GetCurrentProcess().MainWindowHandle, out procId);
IntPtr hkl = GetKeyboardLayout(thread);

if (hkl == IntPtr.Zero)
{
Console.WriteLine("Sorry, that keyboard does not seem to be valid.");
return string.Empty;
}

Keys[] keyStates = new Keys[256];
if (!GetKeyboardState(keyStates))
return string.Empty;

StringBuilder sb = new StringBuilder(10);
int rc = ToUnicodeEx((uint)scanCode, (uint)scanCode, keyStates, sb, sb.Capacity, 0, hkl);
return sb.ToString();
}
}

Convert character to virtual key code

Sending WM_KEYDOWN/UP is troublesome. The application itself already translates the WM_KEYDOWN message into WM_CHAR, using the state of the modifier keys (Shift, Alt, Ctrl) and the keyboard layout. Neither of which you can control, you'll get the wrong character, randomly.

Just send WM_CHAR messages, set the wparam to the character code. No need to worry about lparam, few apps ever use it.



Related Topics



Leave a reply



Submit