开发TSF文本服务遇TF_E_NOLOCK错误:ec中cookie无效如何解决?
TF_E_NOLOCK Error When Calling
ITfContext.GetStart() in TSF Edit Session 我来帮你分析下这个问题的根源和解决办法:
问题根源
你遇到的TF_E_NOLOCK错误,核心原因是你在错误的时机访问了无效的编辑Cookie。TSF的EditSession是异步执行机制:当你调用context.RequestEditSession()后,TSF会在合适的时机(比如当前没有其他编辑锁冲突时)才会调用你的EditSession.DoEditSession()方法,并传入有效的ec(编辑Cookie)。而你在OnTestKeyDown里调用RequestEditSession后立刻访问editSession.EditCookie,这时候DoEditSession大概率还没执行,EditCookie还是默认的0,自然是无效的,导致后续GetStart调用失败。
此外,所有TSF上下文的编辑操作(比如GetStart、SetText)都应该放在DoEditSession方法内部执行——因为只有在这个方法里,TSF才确保了当前线程持有正确的编辑锁,ec是有效的。
修复方案
我们需要重构EditSession,把编辑逻辑移到DoEditSession内部,并通过构造函数传递需要的上下文和数据:
1. 重构EditSession类
[ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public class EditSession : BaseObject, ITfEditSession { private readonly ITfContext _targetContext; private readonly string _textToInsert; // 通过构造函数传入需要的上下文和要插入的文本 public EditSession(ITfContext targetContext, string textToInsert) { _targetContext = targetContext; _textToInsert = textToInsert; } public HRESULT DoEditSession(uint ec) { // 所有上下文操作都在这里执行,ec是TSF传入的有效编辑Cookie HRESULT res = _targetContext.GetStart(ec, out var range); if (res.Failed) { Debug.WriteLine($"GetStart failed: {(ManagerReturnValues)res.Code}"); return res; } char[] charArray = _textToInsert.ToCharArray(); res = range.SetText(ec, 0, charArray, charArray.Length); Debug.WriteLine($"SetText result: {(ManagerReturnValues)res.Code}"); return res; } }
2. 修改KeyEventSink的OnTestKeyDown方法
public HRESULT OnTestKeyDown(ITfContext pic, UIntPtr wParam, IntPtr lParam, out bool pfEaten) { pfEaten = false; uint vkCode = wParam.ToUInt32(); char nonVirtualKey = (char)MapVirtualKey(vkCode, 2); Debug.WriteLine(nonVirtualKey); if (nonVirtualKey == 'F') { HRESULT res = _threadMgr.GetFocus(out var documentMgr); if (res.Failed) { return res; } res = documentMgr.GetTop(out var context); if (res.Failed) { return res; } // 创建EditSession时传入上下文和要插入的文本 var editSession = new EditSession(context, "་"); res = context.RequestEditSession(_clientId, editSession, TF_ES.TF_ES_READWRITE, out var phrSession); HRESULT phrSessionHresult = new HRESULT { Code = phrSession }; if (phrSessionHresult.Succeeded) { pfEaten = true; // 标记按键已被处理 } return S_OK(); } return S_OK(); }
关键说明
- 异步执行逻辑:
RequestEditSession只是发起一个编辑请求,TSF会在后台合适的时机执行DoEditSession,所以你不能在请求后立刻访问EditSession的内部状态。 - 锁上下文有效性:只有在
DoEditSession方法内,ec才是有效的编辑Cookie,TSF已经处理了锁的获取和释放,避免了并发冲突。 - 结果判断:
RequestEditSession返回的phrSession是会话执行的最终结果,可以用来判断编辑操作是否成功完成。
内容的提问来源于stack exchange,提问作者nayaku
相关产品推荐
相关产品推荐

