使用 Rust 实现 Windows 输入法

本文介绍如何使用 Rust 在 Windows 上基于 TSF 框架、Named Pipe IPC 和 Rime 引擎开发输入法 Winxime,并实现了候选栏 Direct2D 渲染及独特的五笔字根提示功能。

发布于 约 3 分钟

这是一个实验,如何使用 Rust 在 Windows 上实现一个输入法?

重复靠轮子毫无意义,除非,你要学习。

TSF

Text Services Framework (文本服务框架)是一种系统服务,主要用于 Windows 平台的键盘处理器、手写识别和语音识别,当然,也包括输入法的实现。

理论上,最小实现只需要一个实现了 TSF 接口的 DLL 就可以了。使用下面的命令注册到系统中,输入法就可以出现在输入法列表中:

regsvr32 "C:\Program Files\SampleIME\SampleIME.dll"

它的大致原理是,在输入的过程中,各个应用会自动调用这个 DLL 来处理按键输入。

但是一个功能完备的输入法肯定不可能只有一个DLL。下面是微信输入法的组件:

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2026/5/10     11:33                BuildIn
d-----         2026/5/10     11:33                flutter_datas
...
-a----          2026/5/8     19:27        5816936 wetype_installer.exe
-a----          2026/5/8     19:48        6912048 wetype_renderer.exe
-a----          2026/5/8     19:48        8137776 wetype_server.exe
-a----          2026/5/8     19:48        1201704 wetype_service.exe
-a----          2026/5/8     19:48       18203184 wetype_update.exe
-a----          2026/5/8     19:48         166952 WeUIResource.dll
-a----          2026/5/8     19:48         588336 window_manager_plugin.dll
-a----          2026/5/8     19:48        1430056 WXP2PTransfer.dll
-a----          2026/5/8     19:48       17777712 XNet.dll

可以看到,光exe就有5个模块。因为我们需要处理复杂的输入逻辑、候选栏、系统托盘菜单、设置面板等。

IPC 通信

自然地,这需要一个IPC通信机制,来满足多个输入法模块之间的通信。Windows 上的 IPC 常用的应该是 Windows Named Pipe(命名管道)。

同样地,我们使用 Rime 引擎来处理输入逻辑。

所以处理流程如下:

按键 → TSF DLL → IPC → Server → Rime引擎 → 返回结果 → 显示/上屏

系统托盘菜单(Server)的一些状态变更,也是通过 IPC(WNPipe)来通知的。

能使用 Rust 实现吗?

因为 Rime 引擎是 C++ 实现的,而 Windows 接口也是C/C++ 实现的,所以,理论上,最佳的实现输入法语言应该是C++。

那么,Rust 呢? 当然可以。

首先,微软官方有 Rust 的绑定库:https://github.com/microsoft/windows-rs。 其次,Rime 引擎有 C 接口输出。我们只要对 librime 进行 FFI 封装就可以了。

至此,没有什么障碍了。

架构如下:

``mermaid graph TB subgraph “RIME 模块组” LIBRIME_SYS[librime-sys
原生 FFI] LIBRIME[librime
高层封装] RIME[winxime-rime
引擎封装] LIBRIME_C[librime/ 子模块
C++ 库] end

subgraph "核心模块"
    IPC[winxime-ipc<br/>IPC 协议]
    CONFIG[winxime-config<br/>配置 + 日志]
end

subgraph "应用层"
    TSF[winxime-tsf<br/>TSF DLL]
    SERVER[winxime-server<br/>主服务器]
    SETUP[winxime-setup<br/>设置界面]
    REGISTER[winxime-tsf-register<br/>注册工具]
end

subgraph "Windows 系统"
    WIN_TSF[Windows TSF API]
    WIN_PIPE[Named Pipe]
end

LIBRIME_SYS --> LIBRIME
LIBRIME --> RIME
RIME --> LIBRIME_C

IPC --> TSF
IPC --> SERVER
IPC --> SETUP

CONFIG --> TSF
CONFIG --> SERVER
CONFIG --> SETUP

RIME --> SERVER

WIN_TSF --> TSF
TSF --> WIN_PIPE
WIN_PIPE --> SERVER
![](mermaid-diagram.svg)

## 实现
由于输入我们交给了 Rime 引擎 ,所以我们需要做的是大概有以下工作:

1. TSF DLL 通过 IPC 与 server 通信。
2. 实现候选栏
3. 实现系统托盘菜单
4. 实现设置面板

### TSF <=> IPC <=> Server <=> Rime 引擎

TSF 接收到应用程序的键盘事件,通过发送 JSON 到管道(`管道路径为 \\.\pipe\WinximeNamedPipe`),Server 接收请求后,转交给 Rime 引擎处理。

在这个过程中, Server 只是一个中转站,它并不处理输入逻辑,有点类似于 web 开发中的 Controller。

通过 `regsvr32` 注册 TSF DLL,就可以在 Windows 系统中看到输入法。

![设置列表](Screenshot_20260516202944.png)
![输入法实现](Screenshot_20260516213155.png)


### 实现候选栏

候选栏采用 Direct2D + DirectComposition + DXGI SwapChain 的组合方案,而非传统的 GDI 或 WPF。

采用这个方案主要有2点原因:
1. 高性能,无外部依赖,候选栏并不会有太多的交互和复杂的元素,额外引用 GUI 库没有太大的必要。
2. 候选栏实现在 server 模块,而整个server 模块也只有候选栏需要实现UI。

![输入法实现](Screenshot_20260516203737.png)
### 系统托盘菜单与设置菜单

事实上,实现候选栏已经完成了输入法的MVP功能。如果还需要输入法像商业输入法一样的话,就那实现系统托盘菜单了。比如,中/英 切换,输入法配置入口等。

### 显示五笔字根
有一个对我来说最想要实现,而商用输入法都不会实现的需求,就是在**输入法里显示五笔字根**。
原因是我虽然日常打字用五笔,但是由于过早地对不会打的字使用拼音代替,导致后面的使用过程中我忘记了五笔字根在哪个键上了。这也是为什么我之前会自己开发一个 android 五笔输入法- Xime 输入法(https://github.com/ximeiorg/xime) 的原因 - 我需要在键盘上下滑显示字根。

回到 Winxime 输入法,我需要思考的是如何优雅地显示五笔字根。2 种方案:
1. 通过一个快捷键直接在显示器某个区域显示整个键盘的字根。
2. 通过输入一个编码,再通过一个快捷键显示单独的字根。

2种方案各有优缺点:
第1 种方案做点是全局显示所有,一次看所有的字根,很方便,但是代价是这个显示区需要做得很大,比较影响可视区,入侵性太过于强了。
第 2 种方案刚好反过来,单个显示字根,甚至可以做进候选栏,代价就是只能显示一个按键的字根。

其实可以同时实现2个方案,不过我目前只做了第 2 种方案。

和小狼毫类似,我也定义了一个自己的配置文件,用于保存软件配置:
```yaml

wubi_radicals:
  # 字根快捷键配置
  hotkeys:
    # 在候选框可见时,单独按 Ctrl 显示最后按键的字根,松开隐藏
    show_last_key: Ctrl
    show_all_key: None
  # 86 版本的字根
  key_radicals:
    # 第一区:横起笔 (1)
    g: "王龶五一戋"
    f: "土士二干十寸雨"
    d: "大犬三古龵镸石厂丆"
    s: "木丁西"
    a: "工匚戈艹廿龷七弋戈"
  //...

这样,当你输入某个编码但是不确定是否输入正确时,按一下 Ctrl 就可以显示该按键的字根了。

root

这个字根是直接显示在候选栏,通过 Ctrl 键按下松开自动切换。

问题

事实上,这个项目最大的难度不在于开发,而在于调试。

相对于一般的应用开发,输入法开发要难调试一些。由于它并不是一个单体应用,而是由一个 DLL 和一堆 exe 组成。

因为 windows 自带的卸载根本无法干净地卸载,它只能卸载其中一个,而dll和安装目前还存在于电脑中。所以,一个友好的独立卸载工具是必须的。

最后

和 android 版本的 Xime 输入法不同,Windows 版本的开发对齐了 Rime 的配置,因此,其实这个输入法也可以由用户自行使用 Rime 配置,从而实现其他方案成为可能。

实验项目地址: https://github.com/ximeiorg/winxime android 版本: https://github.com/ximeiorg/xime