public void UpdateAvailableKeyboards()
        {
            var curKeyboards = KeyboardController.Instance.Keyboards.OfType <WinKeyboardDescription>().ToDictionary(kd => kd.Id);

            foreach (InputLanguage inputLanguage in InputLanguage.InstalledInputLanguages)
            {
                string      keyboardId;
                LayoutName  keyboardLayoutName;
                CultureInfo culture;
                string      cultureName;
                try
                {
                    culture            = new CultureInfo(inputLanguage.Culture.Name);
                    cultureName        = culture.DisplayName;
                    keyboardId         = $"{culture.Name}_{inputLanguage.LayoutName}";
                    keyboardLayoutName = GetBestAvailableKeyboardName(inputLanguage);
                }
                catch (CultureNotFoundException)
                {
                    // This can happen for old versions of Keyman that created a custom culture that is invalid to .Net.
                    // Also see http://stackoverflow.com/a/24820530/4953232
                    culture            = new CultureInfo("en-US");
                    cultureName        = "[Unknown Language]";
                    keyboardId         = $"{cultureName}_{inputLanguage.LayoutName}";
                    keyboardLayoutName = new LayoutName(inputLanguage.LayoutName, inputLanguage.LayoutName);
                }

                WinKeyboardDescription existingKeyboard;
                if (curKeyboards.TryGetValue(keyboardId, out existingKeyboard))
                {
                    if (!existingKeyboard.IsAvailable)
                    {
                        existingKeyboard.SetIsAvailable(true);
                        existingKeyboard.SetLocalizedName(keyboardLayoutName.LocalizedName);
                    }
                    curKeyboards.Remove(keyboardId);
                }
                else
                {
                    // Prevent a keyboard with this id from being registered again.
                    // Potentially, id's are duplicated. e.g. A Keyman keyboard linked to a windows one.
                    // For now we simply ignore this second registration.
                    // A future enhancement would be to include knowledge of the driver in the Keyboard definition so
                    // we could choose the best one to register.
                    KeyboardDescription keyboard;
                    if (!KeyboardController.Instance.Keyboards.TryGet(keyboardId, out keyboard))
                    {
                        KeyboardController.Instance.Keyboards.Add(
                            new WinKeyboardDescription(keyboardId, GetDisplayName(keyboardLayoutName.LocalizedName, cultureName), keyboardLayoutName.Name, culture.Name, true,
                                                       new InputLanguageWrapper(inputLanguage), this));
                    }
                }
            }

            // Set each unhanandled keyboard to unavailable
            foreach (var existingKeyboard in curKeyboards.Values)
            {
                existingKeyboard.SetIsAvailable(false);
            }
        }
        private IEnumerable <LayoutName> GetAvailableKeyboardNames(InputLanguage inputLanguage)
        {
            IEnumTfInputProcessorProfiles profilesEnumerator;

            try
            {
                profilesEnumerator = ProfileManager.EnumProfiles((short)inputLanguage.Culture.KeyboardLayoutId);
            }
            catch (Exception e)
            {
                Debug.WriteLine($"Error looking up keyboards for language {inputLanguage.Culture.Name} - {(short)inputLanguage.Culture.KeyboardLayoutId}");
                yield break;
            }

            TfInputProcessorProfile[] profiles = new TfInputProcessorProfile[1];
            bool returnedLanguage = false;

            while (profilesEnumerator.Next(1, profiles) == 1)
            {
                // We only deal with keyboards; skip other input methods
                if (profiles[0].CatId != Guids.Consts.TfcatTipKeyboard)
                {
                    continue;
                }

                if ((profiles[0].Flags & TfIppFlags.Enabled) == 0)
                {
                    continue;
                }

                if (profiles[0].ProfileType == TfProfileType.Illegal)
                {
                    continue;
                }

                LayoutName layoutName;
                if (profiles[0].Hkl == IntPtr.Zero)
                {
                    returnedLanguage = true;
                    layoutName       = new LayoutName(inputLanguage.LayoutName,
                                                      ProcessorProfiles.GetLanguageProfileDescription(ref profiles[0].ClsId, profiles[0].LangId,
                                                                                                      ref profiles[0].GuidProfile), profiles[0]);
                    yield return(layoutName);
                }
                else
                {
                    layoutName = WinKeyboardUtils.GetLayoutNameEx(profiles[0].Hkl);
                    if (layoutName.Name != string.Empty)
                    {
                        layoutName.Profile = profiles[0];
                        returnedLanguage   = true;
                        yield return(layoutName);
                    }
                }
            }
            if (!returnedLanguage)
            {
                yield return(new LayoutName(inputLanguage.LayoutName, inputLanguage.Culture.DisplayName));
            }
        }
        private IEnumerable <LayoutName> GetAvailableKeyboardNames(InputLanguage inputLanguage)
        {
            IEnumTfInputProcessorProfiles profilesEnumerator;
            string cultureName;
            var    culture = GetCultureInfoFromInputLanguage(inputLanguage, out cultureName);

            if (cultureName == UnknownLanguage)
            {
                Debug.WriteLine($"Error looking up keyboards with layout {inputLanguage.LayoutName} - invalid culture");
                yield break;
            }
            try
            {
                profilesEnumerator = ProfileManager.EnumProfiles((short)culture.KeyboardLayoutId);
            }
            catch
            {
                Debug.WriteLine($"Error looking up keyboards for language {culture.Name} - {(short)culture.KeyboardLayoutId}");
                yield break;
            }

            TfInputProcessorProfile[] profiles = new TfInputProcessorProfile[1];
            bool returnedLanguage = false;

            while (profilesEnumerator.Next(1, profiles) == 1)
            {
                // We only deal with keyboards; skip other input methods
                if (profiles[0].CatId != Guids.Consts.TfcatTipKeyboard)
                {
                    continue;
                }

                if ((profiles[0].Flags & TfIppFlags.Enabled) == 0)
                {
                    continue;
                }

                if (profiles[0].ProfileType == TfProfileType.Illegal)
                {
                    continue;
                }

                LayoutName layoutName;
                if (profiles[0].Hkl == IntPtr.Zero)
                {
                    try
                    {
                        layoutName = new LayoutName(inputLanguage.LayoutName,
                                                    ProcessorProfiles.GetLanguageProfileDescription(ref profiles[0].ClsId, profiles[0].LangId,
                                                                                                    ref profiles[0].GuidProfile), profiles[0]);
                    }
                    catch
                    {
                        // this exception has happened in testing, doesn't seem to be anything we can do
                        // except just ignore this keyboard
                        continue;
                    }
                    returnedLanguage = true;
                    yield return(layoutName);
                }
                else
                {
                    layoutName = WinKeyboardUtils.GetLayoutNameEx(profiles[0].Hkl);
                    if (layoutName.Name != string.Empty)
                    {
                        layoutName.Profile = profiles[0];
                        returnedLanguage   = true;
                        yield return(layoutName);
                    }
                }
            }
            if (!returnedLanguage)
            {
                yield return(new LayoutName(inputLanguage.LayoutName, culture.DisplayName));
            }
        }
        private void GetInputMethods()
        {
            IEnumerable <Tuple <TfInputProcessorProfile, ushort, IntPtr> > imes;

            if (ProfileMgr != null)
            {
                // Windows >= Vista
                imes = GetInputMethodsThroughTsf();
            }
            else
            {
                // Windows XP
                imes = GetInputMethodsThroughWinApi();
            }

            var allKeyboards = KeyboardController.Instance.Keyboards;
            Dictionary <string, WinKeyboardDescription> curKeyboards = allKeyboards.OfType <WinKeyboardDescription>().ToDictionary(kd => kd.Id);

            foreach (Tuple <TfInputProcessorProfile, ushort, IntPtr> ime in imes)
            {
                TfInputProcessorProfile profile = ime.Item1;
                ushort langId = ime.Item2;
                IntPtr hkl    = ime.Item3;

                CultureInfo culture;
                string      locale;
                string      cultureName;
                try
                {
                    culture     = new CultureInfo(langId);
                    cultureName = culture.DisplayName;
                    locale      = culture.Name;
                }
                catch (CultureNotFoundException)
                {
                    // This can happen for old versions of Keyman that created a custom culture that is invalid to .Net.
                    // Also see http://stackoverflow.com/a/24820530/4953232
                    culture     = new CultureInfo("en-US");
                    cultureName = "[Unknown Language]";
                    locale      = "en-US";
                }

                try
                {
                    LayoutName layoutName;
                    if (profile.Hkl == IntPtr.Zero && profile.ProfileType != TfProfileType.Illegal)
                    {
                        layoutName = new LayoutName(ProcessorProfiles.GetLanguageProfileDescription(
                                                        ref profile.ClsId, profile.LangId, ref profile.GuidProfile));
                    }
                    else
                    {
                        layoutName = GetLayoutNameEx(hkl);
                    }

                    string id = GetId(layoutName.Name, locale);
                    WinKeyboardDescription existingKeyboard;
                    if (curKeyboards.TryGetValue(id, out existingKeyboard))
                    {
                        if (!existingKeyboard.IsAvailable)
                        {
                            existingKeyboard.SetIsAvailable(true);
                            existingKeyboard.InputProcessorProfile = profile;
                            existingKeyboard.SetLocalizedName(GetDisplayName(layoutName.LocalizedName, cultureName));
                        }
                        curKeyboards.Remove(id);
                    }
                    else
                    {
                        // Prevent a keyboard with this id from being registered again.
                        // Potentially, id's are duplicated. e.g. A Keyman keyboard linked to a windows one.
                        // For now we simply ignore this second registration.
                        // A future enhancement would be to include knowledge of the driver in the Keyboard definition so
                        // we could choose the best one to register.
                        KeyboardDescription keyboard;
                        if (!allKeyboards.TryGet(id, out keyboard))
                        {
                            KeyboardController.Instance.Keyboards.Add(
                                new WinKeyboardDescription(id, GetDisplayName(layoutName.Name, cultureName),
                                                           layoutName.Name, locale, true, new InputLanguageWrapper(culture, hkl, layoutName.Name), this,
                                                           GetDisplayName(layoutName.LocalizedName, cultureName), profile));
                        }
                    }
                }
                catch (COMException)
                {
                    // this can happen when the user changes the language associated with a
                    // Keyman keyboard (LT-16172)
                }
            }

            foreach (WinKeyboardDescription existingKeyboard in curKeyboards.Values)
            {
                existingKeyboard.SetIsAvailable(false);
            }
        }