#Input Method
Listing and Switching Input Methods on Windows
Approach 1: Via Keyboard Layout Limitation Extracting the layout ID is non-trivial — the code above uses a simplified low-word extraction. For a thorough…
Apr 20, 2024
Listing and Switching Input Methods on Windows

Listing and Switching Input Methods on Windows

April 20, 2024

Approach 1: Via Keyboard Layout

BOOL GetIMEsV1(int layoutId)
{
    BOOL result = TRUE;
 
    BOOL initialized = FALSE;
 
    ITfInputProcessorProfiles* lpProfiles = nullptr;
 
    IEnumTfLanguageProfiles* lpEnum = nullptr;
 
    __try
    {
        HRESULT hr = CoInitialize(nullptr);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        initialized = TRUE;
 
        hr = CoCreateInstance(
            CLSID_TF_InputProcessorProfiles,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_ITfInputProcessorProfiles,
            (VOID**)&lpProfiles
        );
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = lpProfiles->EnumLanguageProfiles(layoutId, &lpEnum);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        if (lpEnum != nullptr)
        {
            TF_LANGUAGEPROFILE profile;
            ULONG fetched = 0;
 
            while (lpEnum->Next(1, &profile, &fetched) == S_OK)
            {
                BSTR bstrDesc = nullptr;
                BOOL bEnabled = FALSE;
 
                hr = lpProfiles->IsEnabledLanguageProfile(profile.clsid, profile.langid, profile.guidProfile, &bEnabled);
 
                if (SUCCEEDED(hr) && bEnabled) {
                    hr = lpProfiles->GetLanguageProfileDescription(profile.clsid, profile.langid, profile.guidProfile, &bstrDesc);
 
                    if (SUCCEEDED(hr))
                    {
                        _tprintf(_T("%s\n"), bstrDesc);
 
                        SysFreeString(bstrDesc);
                    }
                }
            }
        }
    }
    __finally
    {
        if (lpProfiles != nullptr)
        {
            lpProfiles->Release();
 
            lpProfiles = nullptr;
        }
 
        if (lpEnum != nullptr)
        {
            lpEnum->Release();
 
            lpEnum = nullptr;
        }
 
        if (initialized)
        {
            CoUninitialize();
        }
    }
 
    return result;
}
 
int main()
{
    _tsetlocale(LC_ALL, _T("zh-CN"));
 
    auto num = GetKeyboardLayoutList(0, nullptr);
 
    HKL* list = new HKL[num];
 
    GetKeyboardLayoutList(num, list);
 
    for (auto i = 0; i < num; i++)
    {
        //@see HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts
        auto hkl = list[i];
 
        auto device = HIWORD(hkl);
 
        if ((device & 0xF000) != 0xF000)
        {
            // e.g. 0x804 = Simplified Chinese
            auto layoutId = LOWORD(hkl);
 
            GetIMEsV1(layoutId);
        }
    }
 
    delete[] list;
 
    return 0;
}

Limitation

Extracting the layout ID is non-trivial — the code above uses a simplified low-word extraction. For a thorough treatment see How to create a list of Keyboards. Extract KLID from HKL?

Approach 2: Enumerate All Languages and Their IMEs

BOOL GetIMEsV2()
{
    BOOL result = FALSE;
    BOOL initialized = FALSE;
 
    ITfInputProcessorProfiles* lpProfiles = nullptr;
    ITfInputProcessorProfileMgr* lpMgr = nullptr;
    IEnumTfInputProcessorProfiles* lpEnum = nullptr;
 
    __try
    {
        HRESULT hr = CoInitialize(nullptr);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = CoCreateInstance(
            CLSID_TF_InputProcessorProfiles,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_ITfInputProcessorProfileMgr,
            (LPVOID*)&lpMgr
        );
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = CoCreateInstance(
            CLSID_TF_InputProcessorProfiles,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_ITfInputProcessorProfiles,
            (LPVOID*)&lpProfiles
        );
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        // Pass 0 to enumerate all installed languages; pass e.g. 0x804 for Chinese only
        hr = lpMgr->EnumProfiles(0, &lpEnum);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        TF_INPUTPROCESSORPROFILE profile = { 0 };
 
        ULONG fetched = 0;
 
        while (lpEnum->Next(1, &profile, &fetched) == S_OK)
        {
            BSTR bstrDesc = nullptr;
            BOOL bEnabled = FALSE;
 
            hr = lpProfiles->IsEnabledLanguageProfile(profile.clsid, profile.langid, profile.guidProfile, &bEnabled);
 
            if (SUCCEEDED(hr) && bEnabled) {
                hr = lpProfiles->GetLanguageProfileDescription(profile.clsid, profile.langid, profile.guidProfile, &bstrDesc);
 
                if (SUCCEEDED(hr))
                {
                    _tprintf(_T("%s\n"), bstrDesc);
 
                    SysFreeString(bstrDesc);
                }
            }
 
            ZeroMemory(&profile, sizeof(TF_INPUTPROCESSORPROFILE));
        }
    }
    __finally
    {
        if (lpMgr != nullptr)
        {
            lpMgr->Release();
 
            lpMgr = nullptr;
        }
 
        if (lpProfiles != nullptr)
        {
            lpProfiles->Release();
 
            lpProfiles = nullptr;
        }
 
        if (lpEnum != nullptr)
        {
            lpEnum->Release();
 
            lpProfiles = nullptr;
        }
 
        if (initialized)
        {
            CoUninitialize();
        }
    }
 
    return result;
}
 
int main()
{
    _tsetlocale(LC_ALL, _T("zh-CN"));
 
    GetIMEsV2();
 
    return 0;
}

Switching to a Specific IME

BOOL SetIME(_In_ LPCTSTR name)
{
    BOOL result = FALSE;
    BOOL initialized = FALSE;
 
    ITfInputProcessorProfiles* lpProfiles = nullptr;
    ITfInputProcessorProfileMgr* lpMgr = nullptr;
    IEnumTfInputProcessorProfiles* lpEnum = nullptr;
 
    __try
    {
        HRESULT hr = CoInitialize(nullptr);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = CoCreateInstance(
            CLSID_TF_InputProcessorProfiles,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_ITfInputProcessorProfileMgr,
            (LPVOID*)&lpMgr
        );
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = CoCreateInstance(
            CLSID_TF_InputProcessorProfiles,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_ITfInputProcessorProfiles,
            (LPVOID*)&lpProfiles
        );
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        hr = lpMgr->EnumProfiles(0, &lpEnum);
 
        if (FAILED(hr))
        {
            result = FALSE;
 
            __leave;
        }
 
        TF_INPUTPROCESSORPROFILE profile = { 0 };
 
        ULONG fetched = 0;
 
        while (lpEnum->Next(1, &profile, &fetched) == S_OK)
        {
            BSTR bstrDest = nullptr;
 
            hr = lpProfiles->GetLanguageProfileDescription(
                profile.clsid,
                profile.langid,
                profile.guidProfile,
                &bstrDest
            );
 
            if (SUCCEEDED(hr))
            {
                if (_tcscmp(name, bstrDest) == 0)
                {
                    hr = lpMgr->ActivateProfile(
                        TF_PROFILETYPE_INPUTPROCESSOR,
                        profile.langid,
                        profile.clsid,
                        profile.guidProfile,
                        NULL,
                        TF_IPPMF_FORSESSION | TF_IPPMF_DONTCARECURRENTINPUTLANGUAGE
                    );
 
                    if (SUCCEEDED(hr))
                    {
                        result = TRUE;
 
                        break;
                    }
                    else {
                        result = FALSE;
 
                        __leave;
                    }
 
                }
 
                SysFreeString(bstrDest);
            }
 
            ZeroMemory(&profile, sizeof(TF_INPUTPROCESSORPROFILE));
        }
    }
    __finally
    {
        if (lpMgr != nullptr)
        {
            lpMgr->Release();
 
            lpMgr = nullptr;
        }
 
        if (lpProfiles != nullptr)
        {
            lpProfiles->Release();
 
            lpProfiles = nullptr;
        }
 
        if (lpEnum != nullptr)
        {
            lpEnum->Release();
 
            lpProfiles = nullptr;
        }
 
        if (initialized)
        {
            CoUninitialize();
        }
    }
 
    return result;
}
 
int main()
{
    SetIME(TEXT("WeChat Input Method"));
 
    return 0;
}