If I know that a certain key has been pressed (eg Key.D3), and that the Shift key is also down (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)), how can I find out what character that refers to (eg, # on US keyboard, UK pound sign on UK keyboard, etc)?
Put another way, how can I find out, programatically, that Shift + 3 produces # (it wouldn't on a non-US keyboard).
If you want to determine what character you will get from a given key with given modifiers, you should use the user32 ToAscii function. Or ToAsciiEx if you want to use a keyboard layout other then the current one.
using System.Runtime.InteropServices;
public static class User32Interop
{
public static char ToAscii(Keys key, Keys modifiers)
{
var outputBuilder = new StringBuilder(2);
int result = ToAscii((uint)key, 0, GetKeyState(modifiers),
outputBuilder, 0);
if (result == 1)
return outputBuilder[0];
else
throw new Exception("Invalid key");
}
private const byte HighBit = 0x80;
private static byte[] GetKeyState(Keys modifiers)
{
var keyState = new byte[256];
foreach (Keys key in Enum.GetValues(typeof(Keys)))
{
if ((modifiers & key) == key)
{
keyState[(int)key] = HighBit;
}
}
return keyState;
}
[DllImport("user32.dll")]
private static extern int ToAscii(uint uVirtKey, uint uScanCode,
byte[] lpKeyState,
[Out] StringBuilder lpChar,
uint uFlags);
}
You can now use it like this:
char c = User32Interop.ToAscii(Keys.D3, Keys.ShiftKey); // = '#'
If you need more than one modifier, just or them. Keys.ShiftKey | Keys.AltKey
Related
I want a TextBox to only accept some specific characters by using the KeyDown event. I've already got it working, except for one character, the single quote. To get the character that will be written I use (char)e.KeyValue which works for all the characters except the quote (it gives Û). I know I could just use e.KeyCode but it's value is Keys.Oem4, which AFAIK might be different across systems.
Is there any way of consistently detecting a single quote key press?
Code snippet:
char c = (char)e.KeyValue;
char[] moves = { 'r', 'u', ..., '\'' };
if (!(moves.Contains(c) || e.KeyCode == Keys.Back || e.KeyCode == Keys.Space))
{
e.SuppressKeyPress = true;
}
I have been using this for a long time. It handles single quotes just fine. e.KeyChar == 39 '\'' and e.Handled = true behaves exactly as you would expect. I tested it with the KeyPress event and works there too.
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (e.KeyChar == (char)8) // backspace
return;
if (e.KeyChar == (char)3) // ctrl + c
return;
if (e.KeyChar == (char)22) // ctrl + v
return;
typedkey = true;
if (_allowedCharacters.Count > 0) // if the string of allowed characters is not empty, skip test if empty
{
if (!_allowedCharacters.Contains(e.KeyChar)) // if the new character is not in allowed set,
{
e.Handled = true; // ignoring it
return;
}
}
if (_disallowedCharacters.Count > 0) // if the string of allowed characters is not empty, skip test if empty
{
if (_disallowedCharacters.Contains(e.KeyChar)) // if the new character is in disallowed set,
{
e.Handled = true; // ignoring it
return;
}
}
}
As #EdPlunkett suggested, this answer works for me:
[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
public static string KeyCodeToUnicode(System.Windows.Forms.Keys key)
{
byte[] keyboardState = new byte[255];
bool keyboardStateStatus = GetKeyboardState(keyboardState);
if (!keyboardStateStatus)
{
return "";
}
uint virtualKeyCode = (uint)key;
uint scanCode = MapVirtualKey(virtualKeyCode, 0);
IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);
StringBuilder result = new StringBuilder();
ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);
return result.ToString();
}
I'm trying to get text which user type from keyboard. Everything seems working fine when I have English Keyboard Layout, bus when I change it into russian it does not work. I have used
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if ((Keys)vkCode == Keys.Enter || (Keys)vkCode == Keys.Tab || (Keys)vkCode == Keys.LButton || (Keys)vkCode == Keys.RButton)
WriteNewLine(_text);
else
if (ShiftKey)
_text += GetCharsFromKeys((Keys)vkCode, true, false);
else
_text += GetCharsFromKeys((Keys)vkCode, false, false);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
This function gets key code. As you can see I call next function
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;
}
ToUnicode((uint)keys, 0, keyboardState, buf, 256, 0);
return buf.ToString();
}
It returns actual key what user type. But it work only for ENG keyboard layout.
If you know how to add International keyboard layout handling, pls let me know.
P.S. When I enter :
Руский
I get :
Hecrbq
You need to use InstalledInputLanguages and CurrentInputLanguage. There is an example here that you can follow. I will copy the code here in case the link dies. This code is looking to recognize # symbol entry in the European keyboard so you would need to tweak it to meet your needs.
DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
public static void GetKeyboardShortcutForChar(char c, InputLanguage lang, out Keys key, out bool shift)
{
var keyCode = VkKeyScanEx(c, lang.Handle);
key = (Keys) (keyCode & 0xFF);
shift = (keyCode & 0x100) == 0x100;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int ToUnicodeEx(int wVirtKey, uint wScanCode, byte[] lpKeyState, StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
public static char? FromKeys(int keys, bool shift, bool capsLock)
{
var keyStates = new byte[256];
if (shift)
keyStates[16] = 0x80;
if (capsLock)
keyStates[20] = 0x80;
var sb = new StringBuilder(10);
int ret = User32.ToUnicodeEx(keys, 0, keyStates, sb, sb.Capacity, 0, InputLanguage.CurrentInputLanguage.Handle);
return ret == 1 ? (char?)sb[0] : null;
}
I'm working on wpf c# application and I need to detect when user press "/" but I'm having trouble with finding " / " e.Key, I saw there is Key.OemBackslash and stuffs like that, but I can't find right event for " / " (forward slash) ...
Thanks guys,
Cheers
It should be Key.OemQuestion on a US keyboard. But on a Swedish keyboard it is D7 so it depends. The keys on the keyboard doesn't always produce the same character.
Depending on what you are trying to do you may be better off handling the PreviewTextInput event:
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
if (e.Text == "/")
{
Debug.WriteLine("...");
}
}
You can the following methods (see this site) to get the character from the key.
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
bool toUnicodeIsTrue=false;
char t = GetCharFromKey(e.Key, ref toUnicodeIsTrue);
if ( t == '/')
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
public static char GetCharFromKey(System.Windows.Input.Key key, ref bool toUnicodeIsTrue)
{
toUnicodeIsTrue = true;
char ch = ' ';
// First, you need to get the VirtualKey code. Thankfully, there’s a simple class
// called KeyInterop, which exposes a static method VirtualKeyFromKey
// that gets us this information
int virtualKey = System.Windows.Input.KeyInterop.VirtualKeyFromKey(key);
//Then, we need to get the character. This is much trickier.
//First we have to get the keyboard state and then we have to map that VirtualKey
//we got in the first step to a ScanCode, and finally, convert all of that to Unicode,
//because .Net doesn’t really speak ASCII
byte[] keyboardState = new byte[256];
GetKeyboardState(keyboardState);
uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MAPVK_VK_TO_VSC);
StringBuilder stringBuilder = new StringBuilder(2);
int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
switch (result)
{
case -1:
toUnicodeIsTrue = false;
break;
case 0:
toUnicodeIsTrue = false;
break;
case 1:
{
ch = stringBuilder[0];
break;
}
default:
{
ch = stringBuilder[0];
break;
}
}
return ch;
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte[] lpKeyState);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern uint MapVirtualKey(uint uCode, MapType uMapType);
public enum MapType : uint
{
MAPVK_VK_TO_VSC = 0x0,
MAPVK_VSC_TO_VK = 0x1,
MAPVK_VK_TO_CHAR = 0x2,
MAPVK_VSC_TO_VK_EX = 0x3,
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int ToUnicode(
uint wVirtKey,
uint wScanCode,
byte[] lpKeyState,
[System.Runtime.InteropServices.Out, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr, SizeParamIndex = 4)]
StringBuilder pwszBuff,
int cchBuff,
uint wFlags);
}
My old Answer:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{ //***
if (e.Key == Key.Oem2)
{
// do stuff
}
base.OnPreviewKeyDown(e);
}
Note that the name starts with "oem" (Original Equipment Manufacturer), which means the keyboard manufacturer is responsible for its functionality and it varies in local keyboards. So, you can set a break point in
{//***
line of my code and check e.Key property.
I'm trying to convert System.Windows.Forms.Keys to string/char using :
KeysConverter converter = new KeysConverter();
string text = converter.ConvertToString(keyCode);
Console.WriteLine(text);
But it returned "OemPeriod" for "." and "Oemcomma" for ",". Is there any way to get the exact character?
What you are trying to achieve is no trivial task by any means. There is the keyboard mapping (keyboard layout) that windows (for example) uses to translate keyboard keys to actual characters. Here is how I was able to achieve this:
public string KeyCodeToUnicode(Keys key)
{
byte[] keyboardState = new byte[255];
bool keyboardStateStatus = GetKeyboardState(keyboardState);
if (!keyboardStateStatus)
{
return "";
}
uint virtualKeyCode = (uint)key;
uint scanCode = MapVirtualKey(virtualKeyCode, 0);
IntPtr inputLocaleIdentifier = GetKeyboardLayout(0);
StringBuilder result = new StringBuilder();
ToUnicodeEx(virtualKeyCode, scanCode, keyboardState, result, (int)5, (uint)0, inputLocaleIdentifier);
return result.ToString();
}
[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
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();
}
since there is "console-application" in your question tags, try this
ConsoleKeyInfo input = Console.ReadKey(true);
StringBuilder output = new StringBuilder(
String.Format("You pressed {0}", input.KeyChar));
Console.WriteLine(output.ToString());
Those are the correct and expected names for the comma and period keys on your keyboard. You can see that clearly from the documentation. The KeysConverter class is behaving as expected and as designed.
If you want to come up with a different name for those keys you can detect them and substitute the name that you desire to use. For instance:
string name;
switch (e.KeyCode)
{
case Keys.Oemcomma:
name = "Comma";
break;
case Keys.OemPeriod:
name = "Period";
break;
default:
name = (new KeysConverter()).ConvertToString(e.KeyCode);
break;
}
Im currently using the InputSimulator v0.1.0.0 to simulate keypresses and/or mouse events over Remote Desktop. Basic keypresses (for example pressing 'a') works, but special characters, like 'tab', 'enter' dont.
I simulate entering texts with:
InputSimulator.SimulateTextEntry("blabla");
but the following only works locally:
InputSimulator.SimulateKeyPress(VirtualKeyCode.TAB);
or
InputSimulator.SimulateKeyPress(VirtualKeyCode.RETURN);
I searched over the net for working examples but i havent found anything useful. Anyone has idea how to make it work?
Thanks in advance!
-------------------------OWN ANSWER----------------------------------
After googling some more, i have found this article:
http://www.pinvoke.net/default.aspx/user32.keybd_event
in which there is a good code, that does not solve the InputSimulator problem, but does exactly that i need. Here is the code, and how i used that:
[StructLayout(LayoutKind.Sequential)]
public struct KEYBOARD_INPUT
{
public const uint Type = 1;
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Explicit)]
struct KEYBDINPUT
{
[FieldOffset(0)]
public ushort wVk;
[FieldOffset(2)]
public ushort wScan;
[FieldOffset(4)]
public uint dwFlags;
[FieldOffset(8)]
public uint time;
[FieldOffset(12)]
public IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
};
[StructLayout(LayoutKind.Explicit)]
struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)]
public MOUSEINPUT mi;
[FieldOffset(4)]
public KEYBDINPUT ki;
[FieldOffset(4)]
public HARDWAREINPUT hi;
};
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, IntPtr pInput, int cbSize);
And this is how i called the 'press TAB' event:
keybd_event(0x09, 0x0f, 0, 0); // Tab Press
keybd_event(0x09, 0x0f, 0x0002, 0);
As suggested i copy my solution as an answer. I hope these will help for anyone working on similar problems. :) The solution is a bit long, but the problem was not only 'how-to-press a button programatically', but also 'how-to-make it work via remote desktop' and 'how-to-make a general solution for different keyboards'. Well, im not 100% sure that the last problem is completely solved, but the solution below may be used for further developing. I also know that the code is not optimal and sometimes ugly, but im still testing and developing it! :)
//m_text is the whole text i want to write. It may contain special characters,
//like 'enter', 'tab', lower/upper-case chars, and chars with shit/alt is
//pressed, like ';'.
//Test with this string, its difficult-enough. :)
string m_text = "123qweQWE;{tab}!{eNTer}*|";
IntPtr keyboardLayout = GetKeyboardLayout(0);
while (!string.IsNullOrWhiteSpace(m_text))
{
int m_Index = 0;
//Enter, tab and similar keys are in {} brackets
//(for example {tab}). We get the 'tab' from the
//string and pass this to our method. Key combinations
//are separated by a '+' like {alt+tab+tab}, from this
//we will get 'press the alt, then press the tab, then
//press the tab again'.
if (m_text[m_Index] == '{')
{
#region [ Special chars ]
string m_SubString = m_text.Substring(
m_Index + 1, m_text.IndexOf("}") - 1);
string[] m_Splitted = m_SubString.Split(new char[] { '+' });
for (int i = 0; i < m_Splitted.Length; i++)
{
//If the string is longer than 1 char it means we are processing a tab-like key.
if (m_Splitted[i].Length > 1)
PressSpecial(m_Splitted[i]);
else
{
//If the char is 1-char-long, it means we previously pressed a tab-like key,
//and now we press a simple key, like in the case of {altgr+w}.
//Get the virtual key of the char.
short vKey = VkKeyScanEx(
char.Parse(m_Splitted[i]), keyboardLayout);
//Get the low byte from the virtual key.
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
//Get the scan code of the key.
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Press the key.
//Key down event, as indicated by the 3rd parameter that is 0.
keybd_event(m_LOWBYTE, sScan, 0, 0);
}
}
Application.DoEvents();
//We have pressed all the keys we wanted, now release them in backward-order
//when pressing alt+tab we beed to release them in tab-alt order! The logic is the same.
for (int i = m_Splitted.Length - 1; i > -1; i--)
{
if (m_Splitted[i].Length > 1)
ReleaseSpecial(m_Splitted[i]);
else
{
short vKey = VkKeyScanEx(
char.Parse(m_Splitted[i]), keyboardLayout);
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Key up event, as indicated by the 3rd parameter that is 0x0002.
keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
}
}
Application.DoEvents();
#endregion
//We do not use the '{' and '}' brackets, thats why the '+2'. :)
m_Index = m_SubString.Length + 2;
}
else
{
#region [ One char ]
short vKey = VkKeyScanEx(m_text[m_Index], keyboardLayout);
//Hi-byte indicates if we need to press shift, alt or other similar keys.
byte m_HIBYTE = (Byte)(vKey >> 8);
byte m_LOWBYTE = (Byte)(vKey & 0xFF);
byte sScan = (byte)MapVirtualKey(m_LOWBYTE, 0);
//Press the special key if needed.
if ((m_HIBYTE == 1))
PressShift();
else if ((m_HIBYTE == 2))
PressControl();
else if ((m_HIBYTE == 4))
PressAlt();
else if ((m_HIBYTE == 6))
PressAltGr();
//Press, then release the key.
keybd_event(m_LOWBYTE, sScan, 0, 0); //Key down
keybd_event(m_LOWBYTE, sScan, 0x0002, 0); //Key up
//Release the special key if needed.
if ((m_HIBYTE == 1))
ReleaseShift();
else if ((m_HIBYTE == 2))
ReleaseControl();
else if ((m_HIBYTE == 4))
ReleaseAlt();
else if ((m_HIBYTE == 6))
ReleaseAltGr();
#endregion
//Get the next char from the string.
m_Index++;
}
//Remove the already processed chars from the string.
if (m_Index < m_text.Length)
m_text = m_text.Substring(m_Index);
else
m_text = string.Empty;
}
So, this was the logic that processes a string. Lets see the helper methods that will handle the events:
Press and release special keys are the same, only the first two parameters are different.
Check msdn to get the virtual and scan codes for enter, tab, alt, altgr, etc...
#region [ Press shift ]
private void PressShift()
{
//0xA0 is the virtual key of 'shift'.
//0x2A is the scan code of 'shift'.
keybd_event(0xA0, 0x2A, 0, 0);
}
#endregion
#region [ Release shift ]
private void ReleaseShift()
{
keybd_event(0xA0, 0x2A, 0x0002, 0);
}
#endregion
PressSpecial is similar to the code above, so it can be used for shift as well. I separated some of them into different methods as for me its easier to see what i use in the code (its easier for me to use 'PressShift();' instead of 'PressSpecial("shift")';). :)
private void PressSpecial(string p_Special)
{
switch (p_Special.ToLower()) //<-- use lower version!
{
case "home":
keybd_event(0x24, 0x47, 0, 0);
break;
case "end":
keybd_event(0x23, 0x4F, 0, 0);
break;
//Do the same for any key you need (enter, tab, page up, etc...).
//Remember to get the proper virtual- and scan codes for each keys!
}
}
ReleaseSpecial is the same as PressSpecial, but the 3rd parameter is 0x0002.
private void ReleaseSpecial(string p_Special)
{
switch (p_Special.ToLower())
{
case "home":
keybd_event(0x24, 0x47, 0x0002, 0);
break;
case "end":
keybd_event(0x23, 0x4F, 0x0002, 0);
break;
}
}
And finally, here are the dll import methods. You can put them into a static class, if you wish:
[DllImport("user32.dll", EntryPoint = "keybd_event", CharSet = CharSet.Auto,
ExactSpelling = true)]
public static extern void keybd_event(byte vk, byte scan, int flags, int extrainfo);
[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MapVirtualKey(int uCode, int uMapType);