International keyboard layout handling

In the Outlook add-in project I'm working on there is a feature that if the users enters '@' character in the body of mail editor window a dropdown popup will show with some options.
Because this feature is on Outlook window we implemented the feature by capturing keyboard events1 and waiting for "shift + 2" keycode. We check for "shift + 2" because the captured windows keyboard message contains keycode and not the actual character.
In a beta phase an European client reported that this functionality doesn't work, a quick session with the client revealed he used European keyboard layout, something new to me as I used to US keyboard layout.
 

clip_image001.png
 

The bug

Comparing keyboard message keycode with "shift+2" to handle '@' character ignores the different keyboard layouts, handling only US keyboard layout, European and Asian keyboard layout will fail.
 

How to handle different keyboard layouts

.NET framework provide a very useful class: InputLanguage with static CurrentInputLanguage and InstalledInputLanguages properties allowing to get the current and all installed culture/keyboard layout pairs.
But by itself the InputLanguage class doesn't provide a way to convert character to keycode and vice versa. To accomplish this we need to resort to Win32 API in user32 dll, ToUnicodeEx and VkKeyScanEx are able to convert keycode to character and character to keycode respectively (sample code can be found below).
 

The solution

Using InstalledInputLanguages with GetKeyboardShortcutForChar method (see below) to get all the keycodes for all installed culture/keyboard layout pairs and then including the CurrentInputLanguage in comparing the keyboard message keycode to the collection of keycodes collected for specific character.
I have chosen this approach and not converting the keyboard message keycode into character because then I will have to call ToUnicodeEx for each keyboard event, an overhead my approach lacks.
 

Sample code

[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;      
}      

 


  1. Outlook API has limited support for keyboard handling, implementing process level hook for keyboard messages is the best option we found though is also can be dangerous
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s