PS:本篇文章是基于网络上的一些文章、微软官方文档以及自己的理解整理而成,若理解有误,还请大家及时提出。
在了解了TSF的强大之后,很容易产生一个疑问,TSF是如何将应用程序和 Text Service 隔离开的呢?这里简单介绍下TSF 的工作原理。
首先需要知道,基于TSF 框架的输入法 实际上是一个COM程序。也就是说,微软为我们提供了很多的虚基类,然后我们需要实现一个COM 程序。
(1)首先要确认,在应用程序和 Text Service 之间进行传递的是一个 text stream(文本流),既然是 text stream, 肯定要有text(可以理解成 text stream 的载体),比如说notepad,word,各种输入框,都可以理解成是一个text。TSF 的处理是首先由应用程序创建一个 Thread Manager,创建方法是通过 CoCreateInstance 创建一个组件对象,对应的,微软提供的接口是 ITfThreadMgr。
(2)创建好Thread Manager 之后,用 Thread Manager 来创建一个 Document Manager(文档管理器),方法是 ITfThreadMgr::CreateDocumentMgr。应用程序会为每一个不同的Document 创建一个 Document Manager
(3)创建 Document Manager 后,用 ITfDocumentMgr 来创建一个 edit context,方法是 ITfDocumentMgr::CreateContext。
实际上,Thread Manager 为每个 Document Manager 都维护了一个 context stack,新创建的context 被压入到了栈中。
那么,Text Service 是如何往context 中写入text stream 的呢?对于这个问题,首先Text Service 要获得一个context。
当 Text Service 获取一个context 时,很容易想到,此时可能有很多个Document Manager,可能有更多的context,到底获取哪一个呢?
(1)首先获取当前处于焦点的Document Manager,采用的方法是ITfThreadMgr::GetFocus,得到一个Document Manager 对象
(2)获取之前得到的 Document Manager 的 context stack 中的栈顶 context,方法是 ITfDocumentMgr::GetTop
至此,应用程序和 Text Service 通过 Thread Manager 创建的某个 context 产生了连接。
那么,TSF 是如何进行 Text Stores(文本存储)呢?这个问题,微软也为我们提供了相应的接口。
比如说,我们可以实现一个TTsfTextStore,可以继承 ITextStoreAcp,这个接口中有一些函数,可以在TTsfTextStore 中实现这些函数,这些函数中就有传递 text stream 的实现。给出一个ITextStoreAcp 中的函数列表:
/* ITextStoreACP Interfaces */
HRESULT STDMETHODCALLTYPE AdviseSink(REFIID riid, IUnknown* punk, DWORD dwMask);
HRESULT STDMETHODCALLTYPE UnadviseSink(IUnknown* punk);
HRESULT STDMETHODCALLTYPE RequestLock(DWORD dwLockFlags, HRESULT* phrSession);
HRESULT STDMETHODCALLTYPE GetStatus(TS_STATUS* pdcs);