> ## Documentation Index
> Fetch the complete documentation index at: https://www.hellomaggie.top/llms.txt
> Use this file to discover all available pages before exploring further.

# MathType 的批量格式化功能：它做了什么，以及为什么这么慢

> 从一次点击开始，拆开 MathType 批量格式化的真实执行路径：Word 如何找到公式、如何与 MathType 通信、为什么只能逐个删旧插新，以及这套设计在今天会卡在哪里。

如果你用 Word 写过论文或教材，大概遇到过这种需求：全文几十上百个 MathType 公式，字体要从 Times New Roman 统一成某种正文字体，字号要从 12pt 改成 10.5pt。一个一个双击改，不现实。于是你会去找 MathType 菜单里的 **Format Equations**——批量格式化。

它能用。很多人也一直在用。但这篇文章不是功能说明书，而是想把一件事讲清楚：

**当你点下「批量格式化」之后，电脑里到底发生了什么？**

把这条路径看明白，后面那些常见抱怨——卡、慢、一次只能跑一个文档、不敢中途打断——就不再是玄学，而是这套设计的直接后果。

<Tip>
  若你的目标是批量改配置并刷新显示，更直接的路径是 [mathtype-re-render](/blog/mathtype-re-render-skill)，而不是死盯官方批量格式化进度条。
</Tip>

## 它表面上在做什么

从用户视角，流程很短：

1. 在 MathType 里定好目标样式（字体、字号、间距等偏好）
2. 回到 Word，打开批量格式化
3. 选择「整篇文档」或「仅选区」
4. 等进度跑完

结束后，文档里的公式看起来都换成了新样式。功能定位很明确：**按同一套偏好，统一改文档中已有 MathType 公式的外观。**

问题在于：它统一外观的方式，并不是「在原公式上改几个样式字段」，而是另一套更重的机制。

## 公式在 Word 里究竟是什么

理解批量格式化，先要理解 MathType 公式在 Word 中的存在形式。

它们通常不是 Word 原生公式（OMML），而是 **OLE 嵌入对象**。OLE（Object Linking and Embedding）是 Windows 上一套老资格的对象嵌入协议：宿主程序（这里是 Word）保存一块「属于别的程序」的数据；需要编辑时，再把控制权交给那个程序（这里是 MathType）。

在对象模型里，这类公式大致长这样：

* 出现在文档的 `InlineShapes`（行内嵌入图形）集合中
* 带有 `OLEFormat`
* 程序标识（ProgID）通常是 `Equation.DSMT4`

所以 Word 并不「理解」公式内部的数学结构。它只知道：这里嵌了一块 MathType 的东西，要改它，就得找 MathType。

这一点决定了后面所有设计——**格式化无法由 Word 单独完成，必须把 MathType 拉进流程。**

## 点下批量格式化之后：逐步发生了什么

官方批量格式化（包括 SDK 里无界面版本的 `FormatEquations`）本质上是一个循环。对文档里每一个目标公式，大致重复下面几步。

### 1. 在 Word 里定位下一个公式

插件或 VBA 遍历当前文档（或选区）里的嵌入对象，筛出 ProgID 为 `Equation.DSMT4` 的项。每找到一个，就进入单公式处理。

### 2. 连上 MathType

Word 侧并不是直接「调用一个纯函数改样式」。常见链路是：

```text theme={null}
Word 菜单 / VBA 命令
        ↓
MathType 的 Word 模板（stub → WordCmds.dot）
        ↓
MathPage.WLL（Word 加载项）
        ↓
MT6.DLL（负责与 MathType 进程通信）
        ↓
MathType 进程（OLE 服务器）
```

`MT6.DLL` 的职责很具体：管理与 MathType 的连接，必要时启动 MathType，再把请求转过去。API 层对应的是连接/断开一类调用（如 `MTAPIConnect` / `MTAPIDisconnect`，或经 WLL 封装的 `MTInitAPI` / `MTTermAPI`）。

也可以从 OLE 对象本身出发：对公式执行 `DoVerb`，按注册表里的 verb 把 MathType 服务器拉起来，再通过 `IDataObject` / `IOleObject` 读写数据。殊途同归——**都要唤醒并占用 MathType 这个进程。**

### 3. 取出公式数据

连接建立后，需要把「这个公式现在是什么」交给 MathType。常见路径包括：

* 把公式内容放到剪贴板，再让 MathType 从剪贴板读取
* 或通过 OLE 的 `IDataObject::GetData` 取出 MTEF（MathType 内部格式）、MathML 等

此时真正的数学内容和样式偏好，才进入 MathType 的处理范围。

### 4. 按新偏好生成结果

MathType 套用你在批量格式化里指定的偏好源——文档内偏好、MathType 默认偏好、剪贴板上的样例公式，或偏好文件——对当前公式做变换。

注意：这一步的产品语义是「格式化」，实现上往往更接近 **用新偏好重新生成一份公式结果**，而不是在原对象上做一次轻量属性更新。

### 5. 写回 Word：删旧，插新

这是整条链路里最「笨」、也最影响体感的一步。

处理完一个公式后，Word 里原来的 OLE 对象通常会被 **删除**，再在同一位置 **插入** 一个符合新配置的新公式对象。版面要重排，对象集合要更新，文档要变脏。然后循环进入下一个公式，从头再来。

所以批量格式化的真实节奏是：

```text theme={null}
定位公式 n
  → 与 MathType 通信
  → 取出数据
  → 按偏好变换
  → 删除旧对象
  → 插入新对象
  → 定位公式 n+1
  → …
```

不是「扫一遍，批量改字段」，而是 **对每个公式完整走一遍跨进程往返**。

## 「与 MathType 通信」到底指什么

日常说「插件要和 MathType 通信」，容易让人联想到网络 API。这里不是。

这里的通信是 **同一台机器上的进程间协作**，建立在 Windows 的 **COM / OLE** 之上：

| 角色       | 是谁                                 | 做什么                 |
| -------- | ---------------------------------- | ------------------- |
| 宿主       | Word                               | 保存 OLE 对象，提供遍历与删插   |
| 加载项 / 模板 | MathType 的 `.dot` + `MathPage.WLL` | 把菜单命令落到具体调用         |
| 通信层      | `MT6.DLL`                          | 启动/连接 MathType，转发请求 |
| 服务器      | MathType 进程                        | 解析公式、套用偏好、产出新结果     |

一次成功的单公式格式化，至少涉及：

1. Word 对象模型操作（找 shape、删、插）
2. COM/OLE 激活与数据交换
3. 有时还有系统剪贴板作为中转

每一环都有成本。公式一多，成本线性叠加。更关键的是：这些环节大多 **不适合并行**。

## 为什么它只能逐个做

「不支持多线程」「不能多文档同时跑」听起来像功能缺失，其实更像架构后果。

**第一，MathType 服务器倾向于被独占使用。**\
官方 API 里能看到类似「MathType 已在使用中」的错误状态。设计假设是：一边连着一个 MathType，把活干完，而不是开一堆 worker 同时改公式。

**第二，Office 自动化和 OLE 嵌入对象，通常活在串行友好的环境里。**\
Word 的对象模型、UI 线程、嵌入对象的激活与关闭，对并发本来就挑剔。两个线程同时删插同一文档里的 shape，或同时抢同一条 OLE 会话，很容易变成不可预期的状态。

**第三，剪贴板是共享资源。**\
如果格式化路径依赖剪贴板中转，那它在系统级就是全局的。两个任务同时「把公式放进剪贴板再取出来」，会互相踩。

**第四，删旧插新依赖文档的当前结构。**\
每处理完一个公式，文档的 shapes 集合和布局都可能变。下一步「下一个公式」的定位，建立在上一步完成之后的文档状态上。这天然是队列，不是 map-reduce。

于是批量格式化的并行度，基本上就是：**一次一个公式，通常还一次一个文档。**

## 为什么在普通电脑上特别卡

慢，不只是因为 MathType「老」。是宿主、通信、写回三层叠在一起。

**Word 本身就重。**\
批量过程中，Word 要反复参与定位、删除、插入、刷新显示。文档越大、公式越多、机器越普通，UI 卡顿和内存压力越明显。你以为在跑 MathType，其实很大一部分时间 Word 也在干活。

**跨进程往返很贵。**\
每个公式都可能触发：激活服务器、读数据、写数据、关闭会话。对用户来说是「改个字号」；对系统来说是一次完整的 OLE 事务。

**删插比原地修改更伤。**\
如果只是改对象内部的样式字段，成本会小得多。删掉再插入，等于让 Word 不断重构局部文档结构。这是可观察的：公式很多时，进度不是「匀速算一下」，而更像「一下一下地砸文档」。

把这三件事放在一起，批量格式化的体验就稳定地落在：能用，但沉，而且很难靠「再等等」变成现代工具该有的那种安静后台任务。

## 这套设计从哪来，卡在哪

站在 MathType 诞生和流行的年代看，这套路径说得通：

* 公式是专用编辑器的私有对象
* Word 通过 OLE 托管第三方内容是主流做法
* 「交给原厂程序处理，再嵌回来」是可靠的集成方式

它优先保证的是：**结果仍是合法的 MathType 对象，样式来源可控，行为与手动编辑一致。**

它没有优先保证的是：

* 在弱机器上批量处理数百个公式依然流畅
* 多文档队列、多线程加速
* 不打开 Word UI 也能离线改格式

所以今天会觉得它「古早」，往往不是因为某个按钮难用，而是因为 **问题规模变了，约束没变**。论文公式密度更高，批量需求更频繁，用户对「后台跑完、机器还能干别的」的预期也更高。而批量格式化仍绑定在「Word + 单路 MathType 会话 + 逐个删插」这条路上。

## 读懂之后，你会怎么选

如果你只是偶尔改十来个公式，官方批量格式化仍然是最省事的选择：路径成熟，结果符合 MathType 自己的规则。

如果你经常面对这些情况——

* 单文档公式数量很大
* 需要一次处理很多文档
* 机器配置一般，开着 Word 批量跑会明显拖死工作节奏
* 想把格式化嵌进自己的工具链，而不是盯着 Word 进度条

——那「绕过官方批量格式化」就不是为了标新立异，而是为了换一条更轻的路径：尽量少打扰 Word UI，尽量减少逐个 OLE 往返，尽量避免删旧插新式的写回。

前提是你先承认一件事：**官方批量格式化慢，常常不是实现写烂了，而是它忠实地走完了一条注定偏重的架构。**

看清这条架构，才谈得上要不要绕开它，以及绕开之后你必须自己接管哪些责任。
