On Windows 11, how can I reliably determine the on/off status of the IME?

yezi lan 65 信誉分
2026-04-19T15:27:48.63+00:00

Hello Microsoft team,

I am developing a Windows desktop application using TSF (Text Services Framework) in C++.

I would like to understand the correct and supported way to detect the current IME input state in real time.

Specifically, I need to determine whether the IME is in:

Open state (IME enabled / composition enabled, typically Chinese input mode)

Closed state (IME disabled / direct keyboard input, typically English mode)

Any official guidance or documentation would be greatly appreciated.

Windows 开发 | Windows API - Win32

问题作者接受的答案
Taki Ly (WICLOUD CORPORATION) 5,055 信誉分 Microsoft 外部员工 审查方
2026-04-21T07:25:44.6966667+00:00

Hello @yezi lan ,

While using GUID_COMPARTMENT_KEYBOARD_OPENCLOSE is indeed the officially supported and standard way to detect whether an IME is completely enabled (Open) or disabled (Closed/direct input), I think it might only be half of the solution for some modern scenarios. If you are specifically targeting the Chinese input mode, such as Microsoft Pinyin, I think you might notice a technical distinction: it seems that when the user presses the Shift key to temporarily toggle between Chinese and English, the IME does not actually close. Instead, I think the OPENCLOSE compartment remains open (1), and the IME simply changes its internal conversion mode.

So, to reliably detect whether the user is typing Chinese characters or English letters inside an active IME, I suggest monitoring a second compartment alongside it: GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION. By checking this compartment's value for the TF_CONVERSIONMODE_NATIVE flag, you can accurately track when the user toggles between native characters and Alphanumeric (English) mode. According to the official Predefined Compartments documentation, these values are managed globally per thread and serve as the primary source of truth for the current input mode.

To track these states dynamically as the user presses Shift, you need to implement and register an ITfCompartmentEventSink for both compartments mentioned above. The recommended workflow begins by obtaining the ITfCompartmentMgr from your active ITfThreadMgr and attaching your sink using ITfSource::AdviseSink for both GUIDs:

// Instantiate our custom Event Sink
pSink = new CCompartmentEventSink(pCompMgr);
// 1. Register Sink for OPEN/CLOSE
if (SUCCEEDED(pCompMgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, &pCompOpenClose))) {
    if (SUCCEEDED(pCompOpenClose->QueryInterface(IID_ITfSource, (void**)&pSourceOpenClose))) {
        pSourceOpenClose->AdviseSink(IID_ITfCompartmentEventSink, pSink, &dwCookieOpenClose);
    }
}
// 2. Register Sink for CONVERSION MODE (For modern Win10/11 Shift toggle)
if (SUCCEEDED(pCompMgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION, &pCompConversion))) {
    if (SUCCEEDED(pCompConversion->QueryInterface(IID_ITfSource, (void**)&pSourceConversion))) {
        pSourceConversion->AdviseSink(IID_ITfCompartmentEventSink, pSink, &dwCookieConversion);
    }
}

Inside your ITfCompartmentEventSink::OnChange callback, you can then check which GUID triggered the event, call GetValue(), and update your application's state accordingly to differentiate between a complete IME closure and a simple English toggle:

STDMETHODIMP OnChange(REFGUID rguid) override {
    ITfCompartment* pCompartment = nullptr;
    if (SUCCEEDED(m_pCompMgr->GetCompartment(rguid, &pCompartment))) {
        VARIANT var;
        VariantInit(&var);
        if (SUCCEEDED(pCompartment->GetValue(&var)) && var.vt == VT_I4) {
            // 1. Handle overall Open/Close state (e.g., Alt + ~ for Japanese)
            if (rguid == GUID_COMPARTMENT_KEYBOARD_OPENCLOSE) {
                if (var.lVal == 0) { /* CLOSED (Direct Input) */ }
                else { /* OPEN (Composition Enabled) */ }
            }
            // 2. Handle Conversion Mode (e.g., Pressing SHIFT in MS Pinyin)
            else if (rguid == GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION) {
                // Check if the NATIVE flag is set (Chinese characters)
                if (var.lVal & TF_CONVERSIONMODE_NATIVE) {
                    /* CONVERSION: Native Mode (Chinese) */
                } else {
                    /* CONVERSION: Alphanumeric Mode (English) */
                }
            }
        }
        VariantClear(&var);
        pCompartment->Release();
    }
    return S_OK;
}

Regarding consistency, this dual-compartment architecture is guaranteed by design across all TSF-based input methods on modern Windows versions. Native Microsoft IMEs (such as Pinyin, Bopomofo, and Japanese) as well as most reputable third-party TSF Text Services strictly synchronize their states with these two compartments. However, please be aware that if a user employs legacy low-level keyboard hook software which intercepts keystrokes outside of the standard OS architecture, TSF cannot track its internal state. For further reading on properly implementing these sinks, please refer to the official Compartments (Text Services Framework) guide.

I hope this clarifies your question. If you found my response helpful, I would greatly appreciate it if you could provide feedback by following this guide

Thank you.

此答案是否有帮助?

0 个注释 无注释

0 个其他答案

排序依据: 非常有帮助

你的答案

提问者可以将答案标记为“已接受”,审查方可以将答案标记为“已推荐”,这有助于用户了解答案是否解决了提问者的问题。