Make Win32-function `GetKeyboardLayoutName` work in Python
GetKeyboardLayoutName() should give the current KLID… but sometimes it fails. Here’s how we go from confusing docs to a working solution.

Make Win32-function GetKeyboardLayoutName work in Python
Problem
Our Python + Win32 API program tracks the keyboard layout using:
GetKeyboardLayout(thread_id)
This function returns an HKL (handle to a keyboard layout).
We have several XML files describing different keyboard layouts. Each file is named after the KLID (keyboard layout identifier).
Examples:
- US: HKL = 0x4090409, KLID = “00000409”
- Ukrainian: HKL = 0x4220422, KLID = “00000422”
- Ukrainian (Enhanced): HKL = -0xF57FBDE, KLID = “00020422”
Goal: Convert HKL → KLID without hard-coded mapping, using only Win32 API.
What the documentation says
GetKeyboardLayout documentation suggests:
To get the KLID (keyboard layout ID) of the currently active HKL, call GetKeyboardLayoutName.
Retrieves the name of the active input locale identifier (formerly called the keyboard layout) for the calling thread.
BOOL GetKeyboardLayoutNameW(
[out] LPSTR pwszKLID
);
pwszKLID will be a copy of the string provided to the LoadKeyboardLayout function, unless layout substitution took place.
LoadKeyboardLayout syntax:
HKL LoadKeyboardLayoutW(
[in] LPCWSTR pwszKLID,
[in] UINT Flags
);
Problem: LoadKeyboardLayout requires a KLID, but we only have an HKL.
ActivateKeyboardLayout seems promising:
HKL ActivateKeyboardLayout(
[in] HKL hkl,
[in] UINT Flags
);
However, the docs note:
[in] hkl — The input locale identifier must have been loaded by a previous call to the LoadKeyboardLayout function.
Experiment
It works fine for standard layouts:
LoadKeyboardLayout("00000422")
ActivateKeyboardLayout(0x4220422)
print(GetKeyboardLayoutName())
✅ Output: “00000422” — correct.
But fails for enhanced layouts:
LoadKeyboardLayout("00020422")
ActivateKeyboardLayout(-0xF57FBDE)
print(GetKeyboardLayoutName())
❌ Output: default KLID — not the one we expect.
The hidden clue
Buried in the docs:
Since the keyboard layout can be dynamically changed, applications that cache information about the current keyboard layout should process the WM_INPUTLANGCHANGE message to be informed of changes in the input language.
Translation:
You need a Win32 window to receive system messages about layout changes.
Solution
- Create a hidden Win32 window in Python to be able to receive WM_INPUTLANGCHANGE.
- Send it a WM_INPUTLANGCHANGEREQUEST message with your HKL.
- Call GetKeyboardLayoutName after the change to retrieve the correct KLID.
Step 1 — Create a hidden window
import win32gui
hidden_window_hwnd: int = 0
def hidden_window_thread():
wc = win32gui.WNDCLASS()
wc.lpszClassName = "MyHiddenWindowClass"
wc.hInstance = win32gui.GetModuleHandle(None)
class_atom = win32gui.RegisterClass(wc)
global hidden_window_hwnd
hidden_window_hwnd = win32gui.CreateWindow(
class_atom,
"MyHiddenWindow",
0, 0, 0, 0, 0, 0, 0, wc.hInstance, None
)
print(f"Hidden window created, hwnd = {hwnd}")
win32gui.PumpMessages() # run message loop
Documentation references:
If needed, you can add a message handler, but it’s not required:
from win32con import WM_DESTROY
from win32gui import PostQuitMessage, DefWindowProc
def wnd_proc(hwnd, msg, wparam, lparam):
if WM_DESTROY == msg:
PostQuitMessage(0)
return DefWindowProc(hwnd, msg, wparam, lparam)
...
wc.lpfnWndProc = wnd_proc
Run the hidden window non-blocking:
from threading import Thread
...
Thread(target=hidden_window_thread, daemon=True).start()
Step 2 — Send the layout change request
For a given HKL, request the system to change the layout for the hidden window and then call GetKeyboardLayoutName():
from win32api import SendMessage, GetKeyboardLayoutName
from win32con import WM_INPUTLANGCHANGEREQUEST
INPUTLANGCHANGE_SYSCHARSET: int = 0x0001
def hkl_to_klid(hkl: int) -> str:
SendMessage(hidden_window_hwnd,
WM_INPUTLANGCHANGEREQUEST,
INPUTLANGCHANGE_SYSCHARSET,
hkl)
return GetKeyboardLayoutName()
Alternatively, using PostMessage (asynchronous), the KLID can be handled in the window procedure:
from win32con import WM_INPUTLANGCHANGE, WM_DESTROY, \
WM_INPUTLANGCHANGEREQUEST
from win32api import PostMessage, GetKeyboardLayoutName
from win32gui import PostQuitMessage, DefWindowProc
def wnd_proc(hwnd, msg, wparam, lparam):
if WM_INPUTLANGCHANGE == msg:
print(f"Language changed! KLID: {GetKeyboardLayoutName()}")
elif WM_DESTROY == msg:
PostQuitMessage(0)
return DefWindowProc(hwnd, msg, wparam, lparam)
...
PostMessage(hidden_window_hwnd,
WM_INPUTLANGCHANGEREQUEST,
INPUTLANGCHANGE_SYSCHARSET,
hkl)
Test Stand
The full test script on GitHub Gist:
- Creates a hidden window.
- Iterates through available layouts.
- Requests a layout change for that window.
- Reads and prints the KLID using GetKeyboardLayoutName.
References
- Website about keyboard layouts in Windows: Keyboard Layout Info
- GetKeyboardLayout
- GetKeyboardLayoutName
- LoadKeyboardLayout
- ActivateKeyboardLayout
- RegisterClass
- CreateWindow
- PostMessage
- SendMessage
- INPUTLANGCHANGE_SYSCHARSET
- Test script on GitHub Gist: hkl_toklidexample.py
메타데이터
- post_id
- 452e320fca3b
- slug
- make-win32-function-getkeyboardlayoutname-work-in-python-452e320fca3b
- url
- https://medium.com/@asilichenko/make-win32-function-getkeyboardlayoutname-work-in-python-452e320fca3b
- canonical_url
- https://medium.com/@asilichenko/make-win32-function-getkeyboardlayoutname-work-in-python-452e320fca3b
- author_url
- https://medium.com/@asilichenko
- status
- ok
- fetched_at
- 2026-07-18 05:34:24