65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Windows 键盘 DLL 的多语言屏幕键盘

starIconstarIconstarIconstarIconstarIcon

5.00/5 (13投票s)

2012年11月24日

Ms-PL

5分钟阅读

viewsIcon

79630

downloadIcon

9466

Windows 内置支持许多键盘语言,此类使我们能够以简单的方式提取该信息并将其呈现到屏幕上。

Multi language - Hello World preview

引言

这个多语言屏幕键盘是“懒惰”的结果,不想重复造轮子。它读取 Windows 目录中所有可用的键盘布局,并以类似 Apple / Android / Windows 8 的外观呈现布局。死键呈黄色,显示在单独的一行上,并显示所有可用按键。

该键盘已在 Windows XP / 2003 服务器 / Vista / 7 / 2008 服务器 / 8 上进行了测试,所有操作系统似乎都能按预期工作。

背景

我最初的开始是手动创建自己的键盘映射,逐一处理每种语言,使用 Microsoft 屏幕键盘。在我开始制作第三个键盘映射后,我感到厌倦,并开始寻找读取现有布局的解决方案。

在我的探索过程中,我发现了在 x86/x64 系统上加载键盘时的一个 bug,解决方案可以在此处找到。此应用程序中使用了相同的技术。解决方案是两个不同的 kbd.h 版本,一个用于 x86,一个用于 x64。我想知道是什么导致了这个问题,所以我联系了 Microsoft 键盘专家 Michael Kaplan,但他的回答是否定的,因为这是“未文档化的”。他的反对使我更加坚持要完成这个项目,我们在这里 Smile。另一位聪明的家伙“kwhat”提出了一个解决方案,更清晰地展示了 x86/x64 位移位,他的代码也可与 GNU Compiler Collection (GCC) 一起使用。

(注意:如果您知道任何获取键盘布局的好 API,请分享。我正在寻找提取扫描码 + 字符关系的实现/API/函数;请在下方评论您的解决方案!)

扫描码

扫描码是键盘上按键的实际物理参考,一个十六进制数字。请看图片以获得概览,英文布局使用 0x10 作为“q”键,0x11 是“w”,0x1E 是“a”,依此类推……

此类完成的主要工作是整理所有字符并将它们链接到正确的扫描码。如维基中所述,有三个不同的扫描码寄存器,Microsoft 使用集 1;IBM PC XT。

使用代码

代码分为加载键盘布局和呈现键盘布局。

加载键盘 DLL

有一个类负责加载 DLL 布局;CKLL(键盘布局加载器)。LoadDLL 函数加载键盘布局,然后使用 GetCharFromSC 函数根据扫描码和键盘状态检索 VK_CHAR。返回的变量是

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

键盘状态是常见修饰符的变体。

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

每个键盘布局 DLL 都有一个文件描述,使其对布局有一个合理的描述。可以通过调用 GetKeyboardName()(仅在 MFC 版本中可用)来检索此信息。

演示

呈现由一个名为 CKeyboardStaticCStatic 派生类完成。它有一个名为 CKeyButton 的子类,其中包含每个带有扫描码、字符和状态的字符。按键的位置与数组 OSKScanCodes 相关联,在此版本中使用了标准的 qwerty 布局。更改此表将直接影响呈现。

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

不同的呈现只是为了好玩 Smile | <img src=。下图显示了呈现的变化,键盘的功能在这三个中是相同的。

通过使用 GDI+,可以使用 AA 和 ARGB 着色创建外观精美的图形,这就是这里使用的技术。Apple 风格需要大量的绘制才能复制,但它确实看起来是这三者中最好的(在我看来……)CKeyboardStatic 具有这些公共函数,阅读其描述的注释。

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

使用 CKLL 类,您可以通过 SetKey() 链接到“物理”按键来设置扫描码 + 字符,基于 OSKScanCodes 数组。SetKeyboardPresentation() 根据 OSK_KEYBOARD_STATES 数组的值更改字符。SetKeyboardLayout() 更改键盘的呈现。

MFC 或不 MFC

CKLL 类有两种风格,一种是 MFC 版本,一种是不带 MFC 的版本。无 MFC 的 CKLL 使用基于向量的数组来跟踪扫描码和字符,而不是 CPtrArrayCKeyboardStatic 是纯 MFC,不易转换为其他类型。项目“ConsoleKeyboard”是一个纯 Win32 控制台应用程序,CKLL 之间的字符串处理已更改为 wstring(用于此修改)。它的目的是展示呈现键盘所需的代码量很少。请参阅下图的结果。

您必须将控制台字体更改为支持 Unicode 的字体,选择 Lucida Console 或 Consolas。右键单击标题栏并选择“属性”进行更改。获取此呈现的源代码很简单(为便于阅读,已剥离错误处理的代码)。

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

通过使用 CKLL 类,您可以轻松地为 Windows 中的 OpenGL、QT 或任何其他实现制作多语言屏幕键盘。

问题

大多数布局尚未得到确认,坦率地说,我并不“理解”所有这些布局。请花时间检查您日常使用的键盘布局,看看按键是否位于正常位置。键盘不显示修饰键和功能键,如 shift、control、enter、space、tab 等。我没有花时间包含这些,所以请将此呈现视为概念验证。在某些布局中,方框被显示为字符,这通常**不是** Unicode 字符,这尚未处理。这可能会在未来版本中修复。Microsoft 键盘 DLL 可能包含多个布局,此类仅处理我们找到的第一个项目。

历史

  • v1.00 - 首次公开发布。
© . All rights reserved.