Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Multi language on-screen keyboard using Windows keyboard DLLs

5.00/5 (14 votes)
18 Dec 2012Ms-PL5 min read 78.6K   9.3K  
Windows has built inn support for a lot of keyboard languages, this class makes it possible to extract that info in an easy manner and present it to screen.

Multi language - Hello World preview

Introduction

This multi-language onscreen keyboard is a result of "laziness" to not invent the wheel again. It reads all the keyboard layouts that are available in the Windows-directory and a layout is presented with an Apple / Android / Windows 8 look-a-like keyboard. Deadkeys are yellow and presented on a separate row, and all available keys are shown.

The keyboard has been tested on Windows XP / 2003 server / Vista / 7 / 2008 Server / 8, all operating systems seem to work as expected.

Background

My initial start was to create my own keyboard maps manually, going through each language, using the Microsoft On-screen Keyboard. After I started on my third keyboard map, I got fed up and started searching for solutions to read existing layouts.

In my quest I discovered a bug while loading keyboards on x86/x64 systems, the solution can be found here. The same technique is used in this application. The solution is two different kbd.h versions, one for x86 and one for x64. I wanted to know what caused this problem, so I contacted the Microsoft-keyboard-guru Michael Kaplan but his answer was negative, since this is "undocumented". His thumb down made me even more persistent to actually complete this project, and here we are Smile. Another smart guy "kwhat" made a solution that shows the x86/x64 bit-shifts more clearly, his code is also usable with GNU Compiler Collection (GCC).

(Note: Please share if you know any good APIs for getting the keyboard layout. I'm looking for approaches/API/functions that extract the scan code + character relationship; comment your solution below!)

Scan codes

Scan codes are the actually physical reference for a key on the keyboard, a hex-number. See the picture to get an overview, an English layout uses the 0x10 as "q"-key, the 0x11 is "w", the 0x1E is "a", and so on...
Image 3

The main work done by this class is to sort out all the characters and link them to the correct scan code. There are three different scan code registers as described in Wiki, Microsoft uses set 1; IBM PC XT.

Using the code

The code is split between the loading of the keyboard layout and the presentation of it.

Loading keyboard DLLs

There is one class that handles the loading of DLL layouts; CKLL (Keyboard Layout Loader). The function LoadDLL loads the keyboard layout, and afterwards the function GetCharFromSC is used to retrieve a VK_CHAR based on scan code and keyboard-state. The variables returned are:

C#
struct VK_CHAR
{
    WCHAR wChar;        //Actual char
    OSKState nState;    //Which state it are in
    BOOL bDead;         //Dead key
};

The keyboard states are the variation of common-modifiers:

C#
enum OSK_KEYBOARD_STATES
{
    Normal,         //000
    Shift,          //001
    Alt,            //010
    ShiftAlt,       //011
    Ctrl,           //100
    ShiftCtrl,      //101
    CtrlAlt,        //110
    ShiftCtrlAlt,   //111
};

Each keyboard layout DLL has a file description that makes a reasonable description of the layout. This info can be retrieved by calling GetKeyboardName() (only in MFC-version).

Presentation

The presentation is done by a CStatic derivate called CKeyboardStatic. It has a subclass called CKeyButton that contains each character with the scan code, character, and state. Which key goes where is linked to the array OSKScanCodes, in this version the standard qwerty is used. A change of this table will affect the presentation directly.

C#
const char OSKScanCodes[4][13] = {
    {0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D},
    {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x00},
    {0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x2B, 0x00},
    {0x56, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x00, 0x00}
};

The different presentations were made just for fun Smile | <img src=. The picture below shows the variations of presentations, the function of the keyboard is the same in all three:

Image 5

By using GDI+ it is possible to create good looking graphics, using AA and the ARGB-coloring, that is the technique used here. The Apple style requires a lot of painting to replicate, but it sure looks the best of all three (in my eyes...) CKeyboardStatic has these public functions, read comments behind for their descriptions.

C#
void Clear(); //Resets the static from all keys
BOOL SetKey(...); //Adds a character linked to a scan code + deadkey info
void SetKeyboardState(...); //Sets the state the keyboard are in
OSKState GetKeyboardState(...); //Returns the state the keyboard are in
void SetKeyboardPresentation(...);
//Sets the presentation of the static; Apple, Android or Windows 8

Using the CKLL class you can set the scan code + character, using the SetKey() to link to the "physical" key, based on the OSKScanCodes-array. The SetKeyboardPresentation() changes the characters, based on the OSK_KEYBOARD_STATES-array value. The SetKeyboardLayout() changes the presentation of keyboard.

MFC or not to MFC

The CKLL class comes in two flavors, one MFC version and one MFC-free. The MFC-free CKLL uses a vector based array to to keep track of scan codes and characters instead of CPtrArray. CKeyboardStatic is pure MFC and is not straightforward to convert to other types. Project "ConsoleKeyboard" is a pure Win32 console application and the string handling between CKLL has changed to wstring (for this mod). It was made to show off how little code is needed to present a keyboard. See picture below for results:

Image 6

You have to change the console font to a Unicode supported one, select Lucida Console or Consolas. Right click on header and select "Properties" to change it. The source for getting this presentation is simple (code has been stripped of error handling for easy reading):

C++
int _tmain(int argc, _TCHAR* argv[])
{
    //Set unicode mode
    _setmode(_fileno(stdout), _O_U16TEXT);
 
    //Load the keyboard-dll
    CKLL m_kll;
    m_kll.LoadDLL(argv[1]);
 
    //Set our state to "Normal"
    OSKState nState = OSK::Normal;
 
    wprintf(L"\n");
 
    //Loop through each row
    for(int nRow = 0; nRow < ( sizeof(OSK::OSKScanCodes) / 
              sizeof(OSK::OSKScanCodes[0]) ) ; nRow++)
    {
        wprintf(L"-----------------------------------------------------\n");
 
        //Loop through each scan code in that row
        for(int nScanCode = 0; nScanCode < (sizeof(OSK::OSKScanCodes[0]) / 
                    sizeof(OSK::OSKScanCodes[0][0])); nScanCode++)
        {
            //The non 0x00 are actually keys
            if(OSK::OSKScanCodes[nRow][nScanCode] != 0x00)
            {
                CKLL::VK_CHAR aChar;
                if(m_kll.GetCharFromSC(OSK::OSKScanCodes[nRow][nScanCode], nState, aChar))
                    wprintf(L"| %c ", aChar.wChar);
            }
        }
        wprintf(L"|\n");
    }
    wprintf(L"-----------------------------------------------------\n");
    return 0;
}

By using the CKLL class you could easily make a multi-language onscreen keyboard for an OpenGL, QT, or any other implementation in Windows.

Issues

Most of the layouts have not been confirmed, and frankly I don't "understand" all of them. Please take time to check your day-to-day keyboard layout and see if the keys are placed in your normal position. The keyboard does not present modifiers and functions keys as shift, control, enter, space, tab etc... I did not use any time to include these so consider the presentation as a proof-of-concept. In some layouts a square is presented as char, that is normally not a Unicode-character, that isn't handled. This may be fixed in a future version. Microsoft keyboard DLLs can have multiple layouts in them, this class does only handle the first item we find.

History

  • v1.00 - First public release.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)