Description
When you are using Microsoft Word with Mozc for Windows, you may experience an issue that Mozc stops responding to the key events when it's turned on. Once you enter this state, you cannot recover from the state without restarting the Word process.
Even in this state, built-in MS-IME can continue working.
The issue often happens after moving the "First Line Indent" handle in the Ruler, but there might be other ways to trigger this situation.
Steps to reproduce
- Build and install Mozc for Windows.
- Launch Microsoft Word
- Make sure that Mozc is selected and enabled.
- Make sure that "Ruler" is shown
- Move the "First Line Indent" to 6 or higher
- Try to enable Mozc
- Repeat 5 then 6
Expected behavior
- You can continue typing without any issue with Mozc.
Actual behavior
- At the step 6, you may notice that nothing happens with your key stroke.
- Even in this state, you can switch the current IME to MS-IME and continue typing.
- After switching back to Mozc, the symptom persists.
Screenshots

Version or commit-id
6d80d75
Environment
- OS: Windows 11 22H2
- Related Applications: Microsoft Word for Microsoft 365 MSO (Version 2309 Build 16.0.16827.20166) 64 bit
Detailed Analysis
So far, this looks to be a bug in Microsoft Word. When Microsoft Word enters this state, ITfContext::RequestEditSession() at the line 526 starts failing.
|
bool OnOutputReceivedImpl(TipTextService *text_service, ITfContext *context, |
|
Output new_output, EditSessionMode mode) { |
|
if (new_output.has_callback() && |
|
new_output.callback().has_session_command() && |
|
new_output.callback().session_command().has_type()) { |
|
// Callback exists. |
|
const SessionCommand::CommandType &type = |
|
new_output.callback().session_command().type(); |
|
switch (type) { |
|
case SessionCommand::CONVERT_REVERSE: |
|
return TurnOnImeAndTryToReconvertFromIme(text_service, context); |
|
case SessionCommand::UNDO: |
|
return UndoCommint(text_service, context); |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
auto edit_session = MakeComPtr<SyncEditSessionImpl>(text_service, context, |
|
std::move(new_output)); |
|
|
|
DWORD edit_session_flag = TF_ES_READWRITE; |
|
switch (mode) { |
|
case kAsync: |
|
edit_session_flag |= TF_ES_ASYNC; |
|
break; |
|
case kSync: |
|
edit_session_flag |= TF_ES_SYNC; |
|
break; |
|
case kDontCare: |
|
edit_session_flag |= TF_ES_ASYNCDONTCARE; |
|
break; |
|
default: |
|
DCHECK(false) << "unknown mode: " << mode; |
|
break; |
|
} |
|
|
|
HRESULT edit_session_result = S_OK; |
|
const HRESULT hr = context->RequestEditSession( |
|
text_service->GetClientID(), edit_session.get(), edit_session_flag, |
|
&edit_session_result); |
|
return SUCCEEDED(hr) && SUCCEEDED(edit_session_result); |
|
} |
Here are parameters when it starts failing.
dwFlags == TF_ES_READWRITE | TF_ES_SYNC
edit_session_result == TF_E_SYNCHRONOUS
This means that Mozc is requesting a sync document lock and Microsoft Word is explicitly rejecting it. However, this is strange because 1) Microsoft Word does allow sync document lock in the initial state and 2) Text Services Framework explicitly requires applications to support sync document lock in this scenario.
Regarding 1), the call stack actually differs between successful cases and failing cases, meaning that Microsoft Word's internal state has permanently changed regarding how to dispatch key events to Text Input Processors.
Successful case
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::OnOutputReceivedImpl(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, mozc::commands::Output new_output, mozc::win32::tsf::`anonymous-namespace'::EditSessionMode mode) Line 526 C++
mozc_tip64.dll!mozc::win32::tsf::TipEditSession::OnOutputReceivedInKeyEventHandler(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, mozc::commands::Output new_output) Line 603 C++
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::OnKey(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, bool is_key_down, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 465 C++
mozc_tip64.dll!mozc::win32::tsf::TipKeyeventHandler::OnKeyDown(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 491 C++
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::TipTextServiceImpl::OnKeyDown(ITfContext * context, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 824 C++
msctf.dll!CTip::OnKeyboardEvent(struct IInputContextPrivate *,enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::_CallKeyEventSink(unsigned long,struct IInputContextPrivate *,enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::_KeyStroke(enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::_AppFedKeyStroke(enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::KeyDown(unsigned __int64,__int64,int *) Unknown
WWLIB.DLL!00007ffafff85e0a() Unknown
WWLIB.DLL!00007ffaffed1695() Unknown
WWLIB.DLL!00007ffaffef01b9() Unknown
WWLIB.DLL!00007ffb00805c56() Unknown
WWLIB.DLL!00007ffafff85bd3() Unknown
WWLIB.DLL!00007ffaffed175b() Unknown
WWLIB.DLL!00007ffaffecfc1b() Unknown
WWLIB.DLL!00007ffb0017562b() Unknown
WINWORD.EXE!00007ff7a59b12b6() Unknown
WINWORD.EXE!00007ff7a59b1592() Unknown
kernel32.dll!BaseThreadInitThunk() Unknown
ntdll.dll!RtlUserThreadStart() Unknown
Failing case
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::OnOutputReceivedImpl(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, mozc::commands::Output new_output, mozc::win32::tsf::`anonymous-namespace'::EditSessionMode mode) Line 526 C++
mozc_tip64.dll!mozc::win32::tsf::TipEditSession::OnOutputReceivedInKeyEventHandler(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, mozc::commands::Output new_output) Line 603 C++
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::OnKey(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, bool is_key_down, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 465 C++
mozc_tip64.dll!mozc::win32::tsf::TipKeyeventHandler::OnKeyDown(mozc::win32::tsf::TipTextService * text_service, ITfContext * context, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 491 C++
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::TipTextServiceImpl::OnKeyDown(ITfContext * context, unsigned __int64 wparam, __int64 lparam, int * eaten) Line 824 C++
msctf.dll!CTip::OnKeyboardEvent(struct IInputContextPrivate *,enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::_CallKeyEventSink(unsigned long,struct IInputContextPrivate *,enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!CThreadInputMgr::_KeyStroke(enum KeyEventFlags,unsigned __int64,__int64,int *) Unknown
msctf.dll!SYSTHREAD::OnKeyboardEvent(unsigned __int64,__int64,bool,unsigned short,unsigned long *) Unknown
msctf.dll!TF_Notify() Unknown
user32.dll!CtfHookProcWorker(int,unsigned __int64,__int64,unsigned __int64) Unknown
user32.dll!CallHookWithSEH(struct _GENERICHOOKHEADER *,void *,unsigned long *,unsigned __int64) Unknown
user32.dll!__fnHkINLPCHARHOOKSTRUCT() Unknown
ntdll.dll!KiUserCallbackDispatcherContinue() Unknown
win32u.dll!NtUserPeekMessage() Unknown
user32.dll!_PeekMessage() Unknown
user32.dll!PeekMessageW() Unknown
msctf.dll!CThreadInputMgr::PeekMessageW(struct tagMSG *,struct HWND__ *,unsigned int,unsigned int,unsigned int,int *) Unknown
Mso20win32client.dll!00007ffb070a1a12() Unknown
WWLIB.DLL!00007ffafff5ba42() Unknown
WWLIB.DLL!00007ffaffecfb34() Unknown
WWLIB.DLL!00007ffb0017562b() Unknown
WINWORD.EXE!00007ff7a59b12b6() Unknown
WINWORD.EXE!00007ff7a59b1592() Unknown
kernel32.dll!BaseThreadInitThunk() Unknown
ntdll.dll!RtlUserThreadStart() Unknown
In successful case, you can find that CThreadInputMgr::KeyDown is directly called from WWLIB.DLL, while msctf.dll!CThreadInputMgr::PeekMessageW is called from Mso20win32client.dll in failing case. It is quite strange that the call stack differs in this way just by moving the "First Line Indent" handle.
Regarding 2), here is a clear statement that apps must grant a synchronous lock in this scenario.
There are only a few instances where your application must grant a synchronous lock request. Your application must grant a synchronous lock if your application (or control) asks a text service to do something such as correcting text (via ITfFunctionProvider, the use of which is outside the scope of this article). Your application must also grant a synchronous lock whenever you call any method on the ITfKeystrokeMgr interface. Your application must also grant a synchronous lock when you do not use ITfKeystrokeMgr, and the lock request comes from within GetMessage or PeekMessage.
Aside from these exceptions, your application is not required to grant a synchronous lock request; it is perfectly allowable to only grant asynchronous lock requests. It is also perfectly allowable to treat asynchronous lock requests as synchronous lock requests.
Support Dictation With Text Services Framework - MSDN Magazine 2007 July
That said, there is another quote from the same author, which implies that Microsoft Word is a real outlier...
The second rule of writing a text service is: Avoid Synchronous edit sessions. There are a number of text stores (namely, the 500 pound gorilla of text stores - Microsoft Word) that never grant synchronous edit sessions. (The only exception is when Microsoft Word asks for reconversions.) If you start your design with this assumption, you will avoid a painful redesign when you start testing with Microsoft Word, and you find that nothing works.
Rules of Text Services - TSF Aware
Description
When you are using Microsoft Word with Mozc for Windows, you may experience an issue that Mozc stops responding to the key events when it's turned on. Once you enter this state, you cannot recover from the state without restarting the Word process.
Even in this state, built-in MS-IME can continue working.
The issue often happens after moving the "First Line Indent" handle in the Ruler, but there might be other ways to trigger this situation.
Steps to reproduce
Expected behavior
Actual behavior
Screenshots
Version or commit-id
6d80d75
Environment
Detailed Analysis
So far, this looks to be a bug in Microsoft Word. When Microsoft Word enters this state,
ITfContext::RequestEditSession()at the line 526 starts failing.mozc/src/win32/tip/tip_edit_session.cc
Lines 488 to 530 in 6d80d75
Here are parameters when it starts failing.
dwFlags==TF_ES_READWRITE | TF_ES_SYNCedit_session_result==TF_E_SYNCHRONOUSThis means that Mozc is requesting a sync document lock and Microsoft Word is explicitly rejecting it. However, this is strange because 1) Microsoft Word does allow sync document lock in the initial state and 2) Text Services Framework explicitly requires applications to support sync document lock in this scenario.
Regarding 1), the call stack actually differs between successful cases and failing cases, meaning that Microsoft Word's internal state has permanently changed regarding how to dispatch key events to Text Input Processors.
Successful case
Failing case
In successful case, you can find that
CThreadInputMgr::KeyDownis directly called fromWWLIB.DLL, whilemsctf.dll!CThreadInputMgr::PeekMessageWis called fromMso20win32client.dllin failing case. It is quite strange that the call stack differs in this way just by moving the "First Line Indent" handle.Regarding 2), here is a clear statement that apps must grant a synchronous lock in this scenario.
Support Dictation With Text Services Framework - MSDN Magazine 2007 July
That said, there is another quote from the same author, which implies that Microsoft Word is a real outlier...
Rules of Text Services - TSF Aware