The New Old Thing

Windows 获取安装的输入法及切换输入法

· Cheng

通过 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)
        {
             // 例如 0x804 对应中文
            auto layoutId = LOWORD(hkl);

            GetIMEsV1(layoutId);
        }
    }

    delete[] list;

    return 0;
}

缺点:

获取 Layout 比较复杂,上述代码只是简单地从低位获取,具体的处理方法可以参考 How to create a list of Keyboards. Extract KLID from HKL?

遍历所有语言及语言下安装的输入法

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;
        }

        // 第一个参数为 0 遍历本机所有语言,否则跟第一版一样,例如 0x804 对应中文
        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;
}

切换到对应的输入法

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("微信输入法"));

    return 0;
}