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.
- Use the GetWindowThreadProcessId function with your control handle to achieve the relevant native thread id.
- Pass that thread id to GetKeyboardLayout to get the current keyboard layout.
- Call GetKeyboardState to get the current keyboard state. This helps the next method to decide which character to generate according to modifiers state.
- 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
Why Switch for Enum Accepts Implicit Conversion to 0 But No for Any Other Integer
Calling Function from Generated Button in Blazor
How to Retrieve Data Annotations from Code? (Programmatically)
How to Call a Vbscript File in a C# Application
How to Get Command Line Arguments of Other Processes from .Net/C#
Why Does Calling a Method in My Derived Class Call the Base Class Method
How to Fix "Referenced Assembly Does Not Have a Strong Name" Error
ASP.NET MVC Ambiguous Action Methods
C# - Approach for Saving User Settings in a Wpf Application
List<T> VS Bindinglist<T> Advantages/Disadvantages
Web API Put Request Generates an Http 405 Method Not Allowed Error
Task Parallel Library Replacement for Backgroundworker
Implementing Zero or One to Zero or One Relationship in Ef Code First by Fluent API
How to Get the Last Day of a Month
Get Return Value from Stored Procedure
Is There a Good Way to Convert Between Bitmapsource and Bitmap