Why does GetKeyboardLayoutName return the same name after a layout change?

Viewed 168

In this program written below, using the GetKeyboardLayoutName from user32.dll. When I type the first symbol using the "English USA" layout, I get 00000409. This is fine. But when I change my layout to something else, like "English UK" or "Russian", GetKeyboardLayoutName returns the code for "English USA" - 00000409.

I tested this and if I input the first symbol in "Russian", it returns 00000419 and if I switch back to "English USA" and input a second symbol, GetKeyboardLayoutName still returns the code for "Russian" - 00000419.

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApp2
{

    class Program
    {

        const int KL_NAMELENGTH = 9;

        [DllImport("user32.dll")]
        private static extern long GetKeyboardLayoutName(StringBuilder pwszKLID);
        public static string GetLayoutCode()
        {
            var name = new StringBuilder(KL_NAMELENGTH);
            GetKeyboardLayoutName(name);

            return name.ToString();
        }

        static void Main(string[] args)
        {
            Console.ReadKey();
            var res = GetLayoutCode();
            Console.WriteLine("\n" + res);

            Console.ReadKey();
            res = GetLayoutCode();
            Console.WriteLine("\n" + res);
        }
    }
}
3 Answers

The problem is that a console application doesn't allow you to handle windows messages.
You could convert your application to application, make the main form hidden and subclass WndProc to handle the WM_INPUTLANGCHANGE message that triggers on the keyboard layout change:

public class MyHiddenForm : Form
{
    ...

    protected override void WndProc(ref Message m)
    {
        const int WM_INPUTLANGCHANGE = 0x0051;
        if (m.Msg == WM_INPUTLANGCHANGE)
        {
            Console.WriteLine("{0:X8} {1:X8}", m.WParam.ToInt64() , m.LParam.ToInt64());
        }
        base.WndProc(ref m);
    }
}

Another approach is to create a hidden window via WinAPI in the console application that will receive messages. But i'd personally prefer the first approach.

If you don't need a KLID (keyboard layout identifier) specifically, and you just need a way to identify keyboard layouts then you can use the following to get the HKL (input locale identifier):

var focusedHWnd = GetForegroundWindow();
var activeThread = GetWindowThreadProcessId(focusedHWnd, IntPtr.Zero);
var hkl = GetKeyboardLayout(activeThread);

Thats because GetKeyboardLayoutName() is requesting layout for the current thread (each thread have its own keyboard layout thats why we have AttachThreadInput() etc API). Your console app doesn't have thread with Win32 window. Only focused Win32 windows can natively change their keyboard layout by hotkey. There is no way to detect such language change in console windows as far I know - becase they are hosted in special cmd.exe child window (or other terminal app).

Related