Skip to content

Making sense of the Windows Layout Registration

Marc Durdin edited this page Aug 14, 2020 · 1 revision

Making sense of the Windows TIP registration

Windows has a variety of input method registration functions. They use a bewildering variety of terminology, no doubt coming from multiple teams over many years, terms such as "Activate", "Enable", "Load", "Add", "Install" and "Register", and it is not clear what each of these terms means. This document will attempt to capture the exact registry changes that each of these functions make in Windows 10, as well as the changes made to the current session.

There are three different kinds of input methods supported by Windows: Keyboards, Input Method Editors (IMEs) and Text Input Processors (TIPs). Modern IMEs may often support both TIP and IME interfaces.

Keyboards

Keyboards are implemented with a static table description stored in a DLL file, named, for example kbdus.dll, and stored in the system32 folder. These have changed little since Windows NT 4 days (or perhaps even earlier). Keyboards have a unique identifier, made up of a LANGID and a Device ID. For example, kbdus.dll has LANGID 0x0409 and Device ID 0x0000. These combine to form a keyboard layout ID, KLID of 0x00000409. US English (Dvorak) has Device ID 0x0001, so its KLID is 0x00010409.

Windows allows for substitution of keyboard layouts for a given language. The implementation of this is a bit messy!

QUERY: how is Chakma keyboard registered? (it doesn't have its own langid)

IMEs

IMEs are usually given a file extension of .ime and are normally also stored in the system32 folder. They are designed for 'picker' style input and are best suited to languages such as Chinese, Japanese and Korean, but are used sometimes for other languages as well. IMEs are given device IDs starting at 0xE000.

TIPs

TIPs are registered entirely separately to Keyboards and IMEs, in the Text Services Framework (sometimes wrongly called Foundation), TSF. Each TIP is a COM object with a registered CLSID, and it can be registered against many languages, with a profile GUID associated with each language. TIPs were originally implemented for Microsoft Office and there is still some disconnect between the style of Office API design used in the Text Services Framework and typical Windows API designs.

Older APIs that work with Keyboards and IMEs do not list TIPs, but the TSF does list Keyboards and IMEs in its enumeration methods.

The various input method installation states

While the documentation and APIs use a variety of terms, I'll use the following terms:

  1. NotPresent: The input method is not available. While files for implementing the input method may be on the system, they have not been registered or loaded.
  2. Registered: The input method has been added to the Local Machine registry to make it available for use by users.
  3. Installed: The input method will be loaded for the current user when the user logs in. All going well, the input method is listed in Language Preferences.
  4. Loaded: The input method is available in the current session and can be activated by the user. An input method can be Loaded without being Installed, and vice-versa. The input method is listed in the language picker.
  5. Activated: A Loaded input method is currently selected for use in the current focus.

There is a set of flags that allow for input methods to be loaded for a given thread or process. I'm not digging into those at this time.

Windows 8+

All three of these types of input methods are registered against 16-bit LANGIDs. Microsoft have moved, like the rest of the industry, to using BCP 47 tags. So Windows 8 introduced another layer for registering input methods against BCP 47 tags. All existing LANGIDs have a corresponding BCP 47 tag (which you can translate between with functions such as [LCIDToLocaleName] and [LocaleNameToLCID]). However, many BCP 47 tags which have no corresponding LANGID. Microsoft introduced the concept of transient LANGIDs to allow input methods to be registered for these tags. This means that these LANGIDs will not necessarily have the same meaning on different systems; they may even have different meanings for different users on the same system. Furthermore, there are a very limited set of transient LANGIDs available: LOCALE_TRANSIENT_KEYBOARD1 (0x2000), LOCALE_TRANSIENT_KEYBOARD2 (0x2400), LOCALE_TRANSIENT_KEYBOARD3 (0x2800), LOCALE_TRANSIENT_KEYBOARD4 (0x2C00). Attempts to register more than four transient locales will not return a failure but will not add the input method as expected.

There is no Win32 or COM API for allocating transient LANGIDs to BCP 47 tags; they are controlled entirely through a set of Powershell cmdlets such as Set-WinUserLanguageList.

Asychronous Behaviour

Because changes to loaded input methods can be made by any process, there is a degree of asynchronicity involved. This can lead to race conditions where two processes each overwite the other's settings. To make things more complex, TSF does not refresh settings made by other processes, it appears, except when process activation changes (behind the scenes, on Windows 10, it looks like perhaps when WM_ACTIVATEAPP is received by a helper window MSCTFIME_UI, a coalescing timer is set that triggers a refresh of the data).

This is mostly an issue because registration of a input method requires elevation, while installation should be done in the user context. This is most easily implemented by running an elevated child process to register the input method. Relying on the refresh behaviour described above is risky because it is undocumented. It is probably safer to start a same-integrity process after the child process completes, rather than waiting for eventually consistent data.

Some data does not appear to be refreshed until a process is restarted, for example locale names for transient locales.

A consequence of this is that the registry data may be inconsistent with what TSF APIs and the *-WinUserLanguageList cmdlets report, when changes are made in other processes. It also appears that the *-WinUserLanguageList cmdlets do not refresh their in-memory lists of registered input methods without a parent PowerShell process restart. This can lead to the Set-WinUserLanguageList cmdlet deleting input methods that it cannot find, so what should be a no-op Get-WinUserLanguageList | Set-WinUserLanguageList -f actually ends up losing configuration.

Registry Locations

  • HKCU\Keyboard Layout: Legacy Keyboard and IME installations, present since Windows 2000. Identifiers here are KLIDs.
  • HKCU\Software\Microsoft\CTF: This contains the installation and loaded data for TSF TIPs. Identifiers are profile GUIDs. CTF stands for Cicero Text Framework, the internal codename for TSF before it was released.
  • HKCU\Control Panel\International\User Profile: This lists a merge of the above two keys, plus data for transient LANGIDs. Identifiers are BCP 47 tags.
  • HKLM\System\CurrentControlSet\Control\Keyboard Layouts: List of registered Keyboard DLLs.
  • HKLM\Software\Microsoft\CTF: Registration data for TSF TIPs.

APIs

Most of these methods do not handle errors well. It is very easy to pass invalid data for which a method returns success, but which leaves the Windows language configuration in fragile states. These states can cause Explorer to crash, although the recovered Explorer process normally does better. It can be quite difficult to cleanup after some of these errors. When the APIs do return failure, they do not return detailed error codes.

This is a short list of APIs used for registering, installing and loading keyboard layouts. There are other parallel APIs such as [ITfInputProcessorProfiles] which is an earlier version of ITfInputProcessorProfileMgr and appears to have broadly similar functionality.

LoadKeyboardLayout Win32 API

Added in the olden days before Windows 2000.

LoadKeyboardLayout only loads the specified keyboard layout (or IME) into the current session. It does not make any registry changes. Input is a KLID. It will read the installation data in HKCU\Keyboard Layout in order to make substitutions (if KLF_SUBSTITUTE_OK is set).

ITfInputProcessorProfileMgr::RegisterProfile TSF COM API

Added in Windows 2000 / Microsoft Office.

ITfInputProcessorProfileMgr::RegisterProfile registers a TIP or keyboard layout profile on the local machine. It requires elevated access to write to HKLM. It is idempotent -- that is, it can be run multiple times with the same outcome.

The following registry values are modified:

  HKLM\SOFTWARE\Microsoft\CTF\TIP\{<clsid>}\LanguageProfile\0x<langid>\{<guid>}
    Description (REG_SZ)
    Enable (REG_DWORD)
    HiddenInSettingUI (REG_DWORD)
    SubItemInSettingUI (REG_DWORD)

If bEnabledByDefault is TRUE then the profile is also Loaded, but not Installed for the current user in HKCU. However, the profile is treated as Installed for all users, and will be Loaded on reboot because the Enable flag is set in HKLM. The profile will be visible in the language switch UI but not in System Preferences. This quasi-installed state is very confusing for users and bEnabledByDefault should be avoided for input method TIPs (there are other kinds of TIPs where enabled by default is probably appropriate); instead use ITfInputProcessorProfileMgr::RegisterProfile followed by InstallLayoutOrTip (or Set-WinUserLanguageList) so that the user can see and control the input method in the Language Preferences applet.

InstallLayoutOrTip Win32 API

Added in Windows Vista.

This jack-of-all-trades function Installs (or uninstalls) a Registered layout for the current user and optionally Loads it in the current session. It is idempotent.

InstallLayoutOrTip does not require elevated access, as it writes only to HKCU. Importantly, InstallLayoutOrTip rewrites the entire input method configuration for the current user, which means that changes made by other processes may be lost (see Asynchronous Behaviour, above).

Note: Despite the design of TSF, it does not appear to be possible to install a TIP registered for another locale (e.g. by swapping profile GUIDs); while the method returns success, the input method is not Loaded or Installed. This is true even for TIPs registered against the LANGID 0xFFFF, which are documented as being available for installation for any language.

The following registry keys are modified:

HKCU\Control Panel\International\User Profile
HKCU\Software\Microsoft\CTF
HKCU\Keyboard Layout

Set-WinUserLanguageList cmdlet

Added in Windows 8.

Set-WinUserLanguageList is a wrapper for InstallLayoutOrTip which adds support for transient LANGIDs, and works with BCP47 tags rather than LANGIDs. By design, it rewrites the entire language list for the current user.

The following registry keys are modified:

HKCU\Control Panel\International\User Profile
HKCU\Software\Microsoft\CTF
HKCU\Keyboard Layout

Clone this wiki locally