Hello there. I'm trying to detect which character is produced whenever the user presses a key. For this, I'm using the ToUnicodeEx function, which translates the specified virtual-key code and keyboard state to the corresponding Unicode character, inside a keyboard system wide hook. However, every time the user presses dead keys, such as the ones producing '¨', '~' or '´', strange things happen. Here is sample code of the first version:
Code:
private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
//indicates if any of underlaing events set e.Handled flag
bool handled = false;
if (nCode >= 0)
{
//read structure KeyboardHookStruct at lParam
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
// raise KeyPress
if (s_KeyPress != null && wParam == WM_KEYDOWN)
{
bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
System.Text.StringBuilder sbString = new System.Text.StringBuilder();
IntPtr HKL = GetKeyboardLayout(0);
switch (ToUnicodeEx((uint)MyKeyboardHookStruct.VirtualKeyCode,
(uint)MyKeyboardHookStruct.ScanCode,
keyState,
sbString,
5,
(uint)MyKeyboardHookStruct.Flags,
HKL))
{
case 1:
char key = sbString.ToString()[0];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
s_KeyPress.Invoke(null, e);
handled = handled || e.Handled;
break;
}
}
}
//if event handled in application do not handoff to other listeners
if (handled)
return -1;
//forward to other application
return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
}
With this code I faced the following problem: Whenever I tried to type "á" in notepad, which requires '´' to be pressed before 'a', the result would be "´´a". Although the captured character by ToUnicodeEx was 'á', I can't admit that my application prevents the user from using characters such as 'á'.
So, I worked around this problem by avoiding ToUnicodeEx to be called whenever a dead key is pressed. This way, I can avoid the ToUnicodeEx function from messing with the keyboard state. Here is the current version code:
Code:
private static int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
//indicates if any of underlaing events set e.Handled flag
bool handled = false;
if (nCode >= 0)
{
//read structure KeyboardHookStruct at lParam
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
// raise KeyPress
if (s_KeyPress != null && wParam == WM_KEYDOWN)
{
bool isDownShift = ((GetKeyState(VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (GetKeyState(VK_CAPITAL) != 0 ? true : false);
byte[] keyState = new byte[256];
GetKeyboardState(keyState);
System.Text.StringBuilder sbString = new System.Text.StringBuilder();
IntPtr HKL = GetKeyboardLayout(0);
if (!IsDeadKey((uint)MyKeyboardHookStruct.VirtualKeyCode))
{
switch (ToUnicodeEx((uint)MyKeyboardHookStruct.VirtualKeyCode,
(uint)MyKeyboardHookStruct.ScanCode,
keyState,
sbString,
5,
(uint)MyKeyboardHookStruct.Flags,
HKL))
{
case 1:
char key = sbString.ToString()[0];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
s_KeyPress.Invoke(null, e);
handled = handled || e.Handled;
break;
}
}
}
}
//if event handled in application do not handoff to other listeners
if (handled)
return -1;
//forward to other application
return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
}
static bool IsDeadKey(uint key)
{
if ((MapVirtualKey(key, 2) & 2147483648) == 2147483648)
{
return true;
}
else
{
return false;
}
}
I though this solution would solve my problem, but I was wrong. The result has improved, but it's not quite what I wanted. Now, typing 'á' in notepad results in 'a'. Notice that the accent '´' is missing.
Does anyone know how to work around this issue? My current method already captures the correct character, but prevents the user from producing characters with accents or diacritics. I think the KeyboardHookProc method just need a few changes, but I can't figure them out.
Thanks in advance.
PS: I tried the solution described in the following link: Keyboard hooks, ToUnicodeEx, MapVirtrualKeyEx. However, the result is quite the same as in my current version. That code only erases the keyboard buffer, which deletes the accents, therefore not allowing the user to use them.