Change keyboard layout from C# code with .NET 4.5.2

Solution 1:

Switching the keyboard layout requires some P/Invoke; you´ll need at least the following Windows functions to get it working: LoadKeyboardLayout, GetKeyboardLayout and ActivateKeyboardLayout. The following import declarations worked for me...

[DllImport("user32.dll", 
    CallingConvention = CallingConvention.StdCall, 
    CharSet = CharSet.Unicode, 
    EntryPoint = "LoadKeyboardLayout", 
    SetLastError = true, 
    ThrowOnUnmappableChar = false)]
static extern uint LoadKeyboardLayout(
    StringBuilder pwszKLID, 
    uint flags);

[DllImport("user32.dll", 
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode, 
    EntryPoint = "GetKeyboardLayout", 
    SetLastError = true, 
    ThrowOnUnmappableChar = false)]
static extern uint GetKeyboardLayout(
    uint idThread);

[DllImport("user32.dll", 
    CallingConvention = CallingConvention.StdCall, 
    CharSet = CharSet.Unicode, 
    EntryPoint = "ActivateKeyboardLayout", 
    SetLastError = true, 
    ThrowOnUnmappableChar = false)]
static extern uint ActivateKeyboardLayout(
    uint hkl,
    uint Flags);

static class KeyboardLayoutFlags
{
    public const uint KLF_ACTIVATE = 0x00000001;
    public const uint KLF_SETFORPROCESS = 0x00000100;
}

Whenever I have to use native API methods I try to encapsulate them in a class that hides their declaration from the rest of the project´s codebase. So, I came up with a class called KeyboardLayout; that class can load and activate a layout by a given CultureInfo, which comes in handy...

internal sealed class KeyboardLayout
{
    ...

    private readonly uint hkl;

    private KeyboardLayout(CultureInfo cultureInfo)
    {
        string layoutName = cultureInfo.LCID.ToString("x8");

        var pwszKlid = new StringBuilder(layoutName);
        this.hkl = LoadKeyboardLayout(pwszKlid, KeyboardLayoutFlags.KLF_ACTIVATE);
    }

    private KeyboardLayout(uint hkl)
    {
        this.hkl = hkl;
    }

    public uint Handle
    {
        get
        {
            return this.hkl;
        }
    }

    public static KeyboardLayout GetCurrent()
    {
        uint hkl = GetKeyboardLayout((uint)Thread.CurrentThread.ManagedThreadId);
        return new KeyboardLayout(hkl);
    }

    public static KeyboardLayout Load(CultureInfo culture)
    {
        return new KeyboardLayout(culture);
    }

    public void Activate()
    {
        ActivateKeyboardLayout(this.hkl, KeyboardLayoutFlags.KLF_SETFORPROCESS);
    }
}

If you only need to have the layout be active for a short while - and you want make sure to properly restore the layout when done, you could write some kind of a scope type using the IDiposable interface. For instance...

class KeyboardLayoutScope : IDiposable
{
    private readonly KeyboardLayout currentLayout;

    public KeyboardLayoutScope(CultureInfo culture)
    {
        this.currentLayout = KeyboardLayout.GetCurrent();
        var layout = KeyboardLayout.Load(culture);
        layout.Activate();
    }

    public void Dispose()
    {
        this.currentLayout.Activate();
    }
}

Than you can use it like this...

const int English = 1033;
using (new KeyboardLayoutScope(CultureInfo.GetCultureInfo(English))
{
    // the layout will be valid within this using-block
}

You should know that in newer versions of Windows (beginning in Windows 8) the keyboard layout cannot be set for a certain process anymore, instead it is set globally for the entire system - and the layout can also be changed by other applications, or by the user (using the Win + Spacebar shortcut).

I would also recommend to not use SendKeys (or its native counterpart SendInput) since it simulates keyboard input which will be routed to the active/focused window. Using the SendMessage function instead is suitable, but you might want combine that with functionality that can properly determine the target window; but to explain such technique would go beyond the scope of the this question and answer. This answer here illustrates a possible solution: How to send keystrokes to a window?

Solution 2:

Change class @Matze,

internal class KeyboardLayoutScope : IDisposable, IKeyboardLayoutScope
    {
        private readonly KeyboardLayout currentLayout;

        public KeyboardLayoutScope() { }

        public KeyboardLayoutScope(CultureInfo culture)
        {
            currentLayout = KeyboardLayout.GetCurrent();
            KeyboardLayout.Load(culture).Activate();
        }

        public KeyboardLayoutScope(KeyboardLayout currentLayout) => this.currentLayout = currentLayout;

        public void Dispose() => currentLayout.Activate();
    }
    internal interface IKeyboardLayoutScope
    {
        void Dispose();
    }

Other Class

internal sealed class KeyboardLayout
    {
        private readonly uint hkl;

        private KeyboardLayout(CultureInfo cultureInfo) => hkl = NativeMethods.LoadKeyboardLayout(new StringBuilder(cultureInfo.LCID.ToString("x8")), KeyboardLayoutFlags.KLF_ACTIVATE);

        private KeyboardLayout(uint hkl) => this.hkl = hkl;

        public uint Handle => hkl;

        public static KeyboardLayout GetCurrent() => new KeyboardLayout(NativeMethods.GetKeyboardLayout((uint)Thread.CurrentThread.ManagedThreadId));

        public static KeyboardLayout Load(CultureInfo culture) => new KeyboardLayout(culture);

        public void Activate() => NativeMethods.ActivateKeyboardLayout(hkl, KeyboardLayoutFlags.KLF_SETFORPROCESS);
    }

Solution 3:

Try thisone:

        public void switch_keyboard(string lang)
    {
        string keyboard_lang = InputLanguage.CurrentInputLanguage.Culture.TwoLetterISOLanguageName;
        if (keyboard_lang != lang) 
        {
            int en_index = -1;
            for (int i = 0; i < System.Windows.Forms.InputLanguage.InstalledInputLanguages.Count - 1; i++)
            {
                try  
                {
                    if (System.Windows.Forms.InputLanguage.InstalledInputLanguages[i].Culture.TwoLetterISOLanguageName == lang)
                    { en_index = i;  break; }
                }
                catch { break; }
            }
            if (en_index != -1)
            { InputLanguage.CurrentInputLanguage = System.Windows.Forms.InputLanguage.InstalledInputLanguages[en_index]; }
        }
    }