windows 如何创建键盘列表,从HKL中提取KLID?

qhhrdooz  于 2023-03-09  发布在  Windows
关注(0)|答案(2)|浏览(186)

windows 10/C ++/Win32
我需要列出"已安装"的键盘。
我的"列表"的组成部分需要包含:

  • 键盘的HKL。
  • 键盘的KLID。
  • 从以下位置获取的注册表值的值:
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts
  • 示例:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000409
  • 值:布局文件
  • 值:布局文本

我所知道的枚举键盘的唯一方法是通过GetKeyboardLayoutList(),它返回HKL的列表。
适用于"标准"键盘的方法(HKL的04090409、04070407等)。

TCHAR Buffer[50];
TCHAR Buffer2[50];
HKL LoadedKeyboardLayout;

// Hkl: 04090409
_stprintf_s(Buffer, (sizeof(Buffer)/sizeof(TCHAR)), _T("%08X"), (((UINT)Hkl >> 16) & 0xFFFF));
// Buffer: "0000409"
LoadedKeyboardLayout = LoadKeyboardLayout(Buffer, (UINT)0);
// It Loads

ActivateKeyboardLayout(LoadedKeyboardLayout, KLF_SETFORPROCESS);
// It Activates

GetKeyboardLayoutName(Buffer2); 
// Buffer2: "00000409"

// I can now fish the registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000409

当"设备标识符"不是0000时,此操作无效。
来自LoadKeyboardLayout()文档:
要加载的输入法区域设置标识符的名称。此名称是由语言标识符的十六进制值组成的字符串(低位字)和器械标识符例如,美国英语具有语言标识符0x0409,因此主要的美式英语布局命名为"00000409"。美式英语布局的变体(如Dvorak布局)命名为"00010409"、"00020409",依此类推。
如果一个人创建了一个"自定义"键盘,我发现不可能从自定义键盘HKL获得"设备标识符"。
例如:

  • 用MKLCS创建一个自定义键盘US。
  • 安装它。
  • "KLID"将为"A0000409"。
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\a0000409
  • HKL将为0xF0C00409(在GetKeyboardLayoutList()生成的HKL列表中返回)。
  • 为了加载LoadKeyboardLayout()的键盘,需要'A0000409'。
  • 似乎无法从F0C00409创建A0000409。
  • 我还创建了自己的键盘布局没有MKLCS。
  • 我随意地将其命名为00060409。
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00060409
  • 它的HKL是FFFE0409(在GetKeyboardLayoutList()生成的HKL列表中返回)。
  • 似乎无法从FFFE0409创建00060409。

话虽如此,如何从HKL获得KLID?
或者,是否有其他方法可以创建已安装键盘的列表?
//======================================== 2020年11月25日新增。
谢谢你丽塔。
看起来好像GetKeyboardLayoutList()创建了一个"已加载"键盘布局列表。

  • 系统加载的键盘布局,0x0409409,....
  • 通过MKLCS安装安装键盘布局。

似乎在以下注册表项中定义的任何键盘都将在引导时加载。

  • HKEY_CURRENT_USER\Keyboard Layout记录预加载和替换值。

(Note注册表中还有许多其他的"键盘布局"键,所以我不确定HKEY_CURRENT_USER\Keyboard Layout是否是定义PreLoad-Substitates的实际注册表键。

  • HKEY_USERS\.DEFAULT\Keyboard Layout
  • HKEY_USERS\S-1-5-18(我的用户帐户)
  • 1米14分1秒)

因此,我认为Rita的例子是有效的,这是毫无疑问的,因为她的HKEY_CURRENT_USER\Keyboard布局包含以下条目:预加载:1 d0010804
替代品:编号0010804和编号0000804
这些条目可能是由MKLCS安装程序设置的,也可能是通过设置-〉时间和语言-〉点击首选语言-〉选项-〉添加键盘来添加键盘
来自ActivateKeyboardLayout()文档:
输入法区域设置标识符必须已由以前对LoadKeyboardLayout函数的调用加载。
自a0000804(香港长度:F0C00804)实际上已经加载了ActivateKeyboardLayout()工程。
自从我的KLID:00060409未在任何X\键盘布局预加载和替换中引用,我必须物理调用LoadKeyBoardLayout(L"00060409"),以便它出现在GetKeyboardList() HKL中。
再次感谢丽塔。

fhity93d

fhity93d1#

如何向香港牌照申请KLID?
似乎没有现成的直接方法来实现它。
解决方法是检索HKL列表,然后通过HKL激活布局,然后获取其KLID。以下是一个示例:

int cnt = GetKeyboardLayoutList(sizeof(hklArr)/sizeof(hklArr[0]), hklArr);
if(cnt > 0)
{
    printf("keyboard list: \n");
    for (UINT i = 0; i < cnt; i++)
    {
        printf("%x\n", (LONG_PTR)hklArr[i]);
        if (ActivateKeyboardLayout(hklArr[i], KLF_SETFORPROCESS))
        {
            WCHAR pName[KL_NAMELENGTH];
            if (GetKeyboardLayoutName(pName))
            {
                wprintf(L"layout name (KLID): %s\n", pName);
            }
        }
    }
}

结果是这样的:

更新:

更新2:分享我的创建和安装步骤。

我使用键盘布局创建者1.4.
1.加载一个现有的键盘进行测试。(您可以根据它进行修改或完全创建自己的键盘。)

1.验证和测试键盘,以确保它的工作,如您所期望的。然后建立DLL和安装包。

1.运行步骤2生成的setup.exe。安装完成后,您将在注册表中看到相关的预加载键盘布局项。

mhd8tkvw

mhd8tkvw2#

it is not documented and can be changed after Windows updates以来,没有安全的方法来执行此操作。
正确的算法已发布here
以下是我目前在Windows 10版本21H2(2021年11月更新)上运行的代码:

std::string GetKlidFromHkl(HKL hkl)
{
    wchar_t klid[KL_NAMELENGTH] = { 0 };

    WORD device = HIWORD(hkl);

    if ((device & 0xf000) == 0xf000) // `Device Handle` contains `Layout ID`
    {
        WORD layoutId = device & 0x0fff;

        HKEY key;
        CHECK_EQ(::RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key), ERROR_SUCCESS);

        DWORD index = 0;
        wchar_t buffer[KL_NAMELENGTH];
        DWORD len = (DWORD)std::size(buffer);
        while (::RegEnumKeyExW(key, index, buffer, &len, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
        {
            wchar_t layoutIdBuffer[MAX_PATH] = {};
            DWORD layoutIdBufferSize = sizeof(layoutIdBuffer);
            if (::RegGetValueW(key, buffer, L"Layout Id", RRF_RT_REG_SZ, nullptr, layoutIdBuffer, &layoutIdBufferSize) == ERROR_SUCCESS)
            {
                if (layoutId == std::stoul(layoutIdBuffer, nullptr, 16))
                {
                    _wcsupr(buffer);
                    wcscpy(klid, buffer);
                    //DBGPRINT("Found KLID %ls by layoutId=0x%04x", klid, layoutId);
                    break;
                }
            }
            len = (DWORD)std::size(buffer);
            ++index;
        }

        CHECK_EQ(::RegCloseKey(key), ERROR_SUCCESS);
    }
    else
    {
        // Use input language only if keyboard layout language is not available. This
        // is crucial in cases when keyboard is installed more than once or under
        // different languages. For example when French keyboard is installed under US
        // input language we need to return French keyboard identifier.
        if (device == 0)
        {
            device = LOWORD(hkl);
        }

        std::swprintf(klid, std::size(klid), L"%08X", device);
        //DBGPRINT("Found KLID %ls by langId=0x%04x", klid, device);
    }

    return utf8::narrow(klid);
}

您还可以使用此代码枚举LAYOUTORTIPPROFILE.szId中带有KLID的布局配置文件:

typedef struct tagLAYOUTORTIPPROFILE {
    DWORD dwProfileType;
    LANGID langid;
    CLSID clsid;
    GUID guidProfile;
    GUID catid;
    DWORD dwSubstituteLayout;
    DWORD dwFlags;
    WCHAR szId[MAX_PATH];
} LAYOUTORTIPPROFILE;

// Flags used in LAYOUTORTIPPROFILE::dwProfileType
#define LOTP_INPUTPROCESSOR 1
#define LOTP_KEYBOARDLAYOUT 2

// Flags used in LAYOUTORTIPPROFILE::dwFlags.
#define LOT_DEFAULT 0x0001
#define LOT_DISABLED 0x0002

std::vector<LAYOUTORTIPPROFILE> EnumLayoutProfiles()
{
    // http://archives.miloush.net/michkap/archive/2008/09/29/8968315.html
    // https://learn.microsoft.com/en-us/windows/win32/tsf/enumenabledlayoutortip
    typedef UINT(WINAPI* EnumEnabledLayoutOrTipFunc)(LPCWSTR pszUserReg, LPCWSTR pszSystemReg, LPCWSTR pszSoftwareReg, LAYOUTORTIPPROFILE* pLayoutOrTipProfile, UINT uBufLength);
    static EnumEnabledLayoutOrTipFunc EnumEnabledLayoutOrTip = reinterpret_cast<EnumEnabledLayoutOrTipFunc>(::GetProcAddress(::LoadLibraryA("input.dll"), "EnumEnabledLayoutOrTip"));

    if (!EnumEnabledLayoutOrTip)
        return {};

    const UINT count = EnumEnabledLayoutOrTip(nullptr, nullptr, nullptr, nullptr, 0);

    std::vector<LAYOUTORTIPPROFILE> layouts;
    layouts.resize(count);

    const UINT written = EnumEnabledLayoutOrTip(nullptr, nullptr, nullptr, layouts.data(), count);

    CHECK_EQ(count, written);

    return layouts;
}

布局的LAYOUTORTIPPROFILE.szId字符串格式为:

文字服务配置文件(IME)的字符串格式为:
电话:{xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} {xxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx}
更多信息请点击此处。

相关问题