2021SC@SDUSC
文章目录
- 简单介绍
- WeaselTSF.h
从本篇博客开始分析WeaselTFS这个包中的代码。
简单介绍
WeaselTSF.vcproj
This is the main project file for VC++ projects generated using an Application Wizard.
It contains information about the version of Visual C++ that generated the file, and
information about the platforms, configurations, and project features selected with the Application Wizard.
WeaselTSF.vcproj是使用应用程序向导生成的VC++项目的主项目文件。它包含生成文件的VisualC++的版本信息,和有关使用应用程序向导选择的平台、配置和项目功能的信息。
WeaselTSF.cpp
This is the main DLL source file.
WeaselTSF.cpp是主DLL源文件。
Other standard files:
StdAfx.h, StdAfx.cpp
These files are used to build a precompiled header (PCH) file named WeaselTSF.pch and a precompiled types file named StdAfx.obj.
其他标准文件StdAfx.h, StdAfx.cpp,这些文件用于构建WeaselTSF.PCH的预编译头(PCH)文件和StdAfx.obj的预编译类型文件。
WeaselTSF.h
#include <WeaselCommon.h>
#include "Globals.h"
#include "WeaselIPC.h"
class CCandidateList;
class CLangBarItemButton;
class CCompartmentEventSink;
class WeaselTSF:
public ITfTextInputProcessorEx,
public ITfThreadMgrEventSink,
public ITfTextEditSink,
public ITfTextLayoutSink,
public ITfKeyEventSink,
public ITfCompositionSink,
public ITfThreadFocusSink,
public ITfActiveLanguageProfileNotifySink,
public ITfEditSession
{
public:
WeaselTSF();
~WeaselTSF();
/* IUnknown */
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
/* ITfTextInputProcessor */
STDMETHODIMP Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId);
STDMETHODIMP Deactivate();
/* ITfTextInputProcessorEx */
STDMETHODIMP ActivateEx(ITfThreadMgr *pThreadMgr, TfClientId tfClientId, DWORD dwFlags);
/* ITfThreadMgrEventSink */
STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *pDocMgr);
STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *pDocMgr);
STDMETHODIMP OnSetFocus(ITfDocumentMgr *pDocMgrFocus, ITfDocumentMgr *pDocMgrPrevFocus);
STDMETHODIMP OnPushContext(ITfContext *pContext);
STDMETHODIMP OnPopContext(ITfContext *pContext);
/* ITfTextEditSink */
STDMETHODIMP OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord);
/* ITfTextLayoutSink */
STDMETHODIMP OnLayoutChange(ITfContext *pContext, TfLayoutCode lcode, ITfContextView *pContextView);
/* ITfKeyEventSink */
STDMETHODIMP OnSetFocus(BOOL fForeground);
STDMETHODIMP OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten);
STDMETHODIMP OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten);
STDMETHODIMP OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten);
STDMETHODIMP OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pfEaten);
STDMETHODIMP OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pfEaten);
// ITfThreadFocusSink
STDMETHODIMP OnSetThreadFocus();
STDMETHODIMP OnKillThreadFocus();
/* ITfCompositionSink */
STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition);
/* ITfEditSession */
STDMETHODIMP DoEditSession(TfEditCookie ec);
/* ITfActiveLanguageProfileNotifySink */
STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, BOOL isActivated);
///* ITfCompartmentEventSink */
//STDMETHODIMP OnChange(_In_ REFGUID guid);
/* Compartments */
BOOL _IsKeyboardDisabled();
BOOL _IsKeyboardOpen();
HRESULT _SetKeyboardOpen(BOOL fOpen);
/* Composition */
void _StartComposition(com_ptr<ITfContext> pContext, BOOL fCUASWorkaroundEnabled);
void _EndComposition(com_ptr<ITfContext> pContext, BOOL clear);
BOOL _ShowInlinePreedit(com_ptr<ITfContext> pContext, const std::shared_ptr<weasel::Context> context);
void _UpdateComposition(com_ptr<ITfContext> pContext);
BOOL _IsComposing();
void _SetComposition(com_ptr<ITfComposition> pComposition);
void _SetCompositionPosition(const RECT &rc);
BOOL _UpdateCompositionWindow(com_ptr<ITfContext> pContext);
void _FinalizeComposition();
void _AbortComposition(bool clear = true);
/* Language bar */
HWND _GetFocusedContextWindow();
void _HandleLangBarMenuSelect(UINT wID);
/* IPC */
void _EnsureServerConnected();
/* UI */
void _UpdateUI(const weasel::Context & ctx, const weasel::Status & status);
void _StartUI();
void _EndUI();
void _ShowUI();
void _HideUI();
com_ptr<ITfContext> _GetUIContextDocument();
com_ptr<ITfThreadMgr> _GetThreadMgr() { return _pThreadMgr; }
private:
/* TSF Related */
BOOL _InitThreadMgrEventSink();
void _UninitThreadMgrEventSink();
BOOL _InitTextEditSink(com_ptr<ITfDocumentMgr> pDocMgr);
BOOL _InitKeyEventSink();
void _UninitKeyEventSink();
void _ProcessKeyEvent(WPARAM wParam, LPARAM lParam, BOOL *pfEaten);
BOOL _InitPreservedKey();
void _UninitPreservedKey();
BOOL _InitLanguageBar();
void _UninitLanguageBar();
void _UpdateLanguageBar(weasel::Status stat);
void _ShowLanguageBar(BOOL show);
void _EnableLanguageBar(BOOL enable);
BOOL _InsertText(com_ptr<ITfContext> pContext, const std::wstring& ext);
void _DeleteCandidateList();
BOOL _InitCompartment();
void _UninitCompartment();
HRESULT _HandleCompartment(REFGUID guidCompartment);
bool isImmersive() const {
return (_activateFlags & TF_TMF_IMMERSIVEMODE) != 0;
}
com_ptr<ITfThreadMgr> _pThreadMgr;
TfClientId _tfClientId;
DWORD _dwThreadMgrEventSinkCookie;
com_ptr<ITfContext> _pTextEditSinkContext;
DWORD _dwTextEditSinkCookie, _dwTextLayoutSinkCookie;
BYTE _lpbKeyState[256];
BOOL _fTestKeyDownPending, _fTestKeyUpPending;
com_ptr<ITfContext> _pEditSessionContext;
std::wstring _editSessionText;
com_ptr<CCompartmentEventSink> _pKeyboardCompartmentSink;
com_ptr<ITfComposition> _pComposition;
com_ptr<CLangBarItemButton> _pLangBarButton;
com_ptr<CCandidateList> _cand;
LONG _cRef; // COM ref count
/* CUAS Candidate Window Position Workaround */
BOOL _fCUASWorkaroundTested, _fCUASWorkaroundEnabled;
/* Weasel Related */
weasel::Client m_client;
DWORD _activateFlags;
/* IME status */
weasel::Status _status;
};
这里使用了TSF(Text Services Framework),TSF是文本服务框架的简称,基于TSF的每一个输入法IME都是COM组件。支持TSF的应用程序可以在不用感知文本设备的前提下,接受从不同文本设备(键盘、手写、语音等)来源的文本输入。此外,TSF的一个重要优势是对输入法权限的管控程度远远高于IMM-IME,大大有利于系统的安全性。
输入法广泛使用 IMM32 接口,也发展成熟,XP 开始微软推广 TSF,Vista 操作系统默认开启了 TSF 管理器。但 TSF
一直饱受非议,虽然设计的很通用,但使用 COM 组件、接口隐晦、操作麻烦,而且从输入法角度来说,能做的事情似乎并不比 IMM32
强多少。此外,TSF 文档不全,样例单一,个别输入法功能上(比如输入法窗口管理)甚至比 IMM32 还要弱。TSF
似乎一直不怎么受到输入法开发者待见。直到 Win8 之后,METRO 界面只能使用 TSF 接口,于是主流输入法纷纷实现
TSF,用于处理全屏模式、游戏、以及 METRO。
-
TSF 特点
-
使用 COM 组件,扩展性非常好,但上手难度大。
本身着重文字服务,比如手写输入、语音输入,甚至纠错、翻译什么的,都是可以用 TSF 实现。
APP 和 IME 之间有更多信息可以通讯。
IME 相关功能提供不全,如只提供了输入字属性和语言栏修改,输入法窗口均需要自己实现。(因为这一点,主流的纯 TSF 输入法都放弃输入法状态栏)
大部分注册表相关任务(比如注册输入法,注册快捷键)需要自己实现。
输入法文件的本质是个 dll,只要 dll 编译正确,就可以注册进操作系统。而 TSF 输入法,则是一个可注册的,包含 COM 接口的 dll。
COM 是微软的核心接口方案,COM 在 Windows 和 Office 中使用非常广泛,比如看似笨重的 IE 其实在为整个操作系统实现了网页相关的接口。COM 的一大特点是跨进程的,使用 COM 的时候,你不知道对面是跟进程,还是个被操作系统托管的 dll,而 COM 对象而言,也会在不同进程里被使用。
DLL DEF
编译 dll 需要暴露外部接口,暴露的方式就是在编译的时候指定 def 文件。一个 COM 组件的 def 文件至少是这样的:
LIBRARY
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
这个包里的WeaselTFS.def文件即为如此:
其中 DllRegisterServer 和 DllUnregisterServer 是被注册(安装卸载),也就是调用 regsvr32.exe 注册时触发的函数。
而组件实际被调用的时候,DllGetClassObject 会被调用,通常你需要返回一个 ClassFactory,注意引用管理。
DllCanUnloadNow 则是用来计算能否卸载,需要跟引用管理配合。
下篇继续~