夜雨聆风学习资料网

ARTICLE · 1259577

迭代篇:八套插件 Skill 换底座——24,847 页 VBA、7,234 份 PIA 类型页、两道真机闸门,AI 写加载项不再靠猜

迭代篇:八套插件 Skill 换底座——24,847 页 VBA、7,234 份 PIA 类型页、两道真机闸门,AI 写加载项不再靠猜

前面那篇 Excel 文档库篇,我留了一句话:AI 强不强,不在记忆,在文档。 那篇讲的是"实时操作"——AI 在你已经打开的表、文档、演示里动手。 这一篇讲另一半:写插件。 同一句 Range.Find,实时操作里是脚本,插件里是 C#;坑是同一批坑,只是换了语言、换了宿主、换了交付形态。 所以这一轮,我把八套插件 Skill 的底座掀了重铺:文档搬进来,真机装进去,闸门立起来。


一、先交代:这轮改的不是功能,是底子

先把八套名字摆出来,免得你对不上号:

  • word-com-addin-builder:Word 传统 COM 加载项
  • ppt-com-addin-builder:PowerPoint 传统 COM 加载项
  • exceldna-addin-builder:Excel / WPS 的 XLL 插件
  • outlook-com-addin-builder:Outlook 传统 COM 加载项
  • excel-vba-addin-builder:Excel VBA / xlam 加载项
  • word-vba-addin-builder:Word VBA 加载项
  • powerpoint-vba-addin-builder:PPT VBA 加载项
  • pbi-desktop-tool-builder:Power BI Desktop 外部工具

八套,横跨三条技术线:C# COM 加载项、VBA 宏、Power BI 外部工具。

为什么八套一起改?

因为它们大多共用同一套对象模型、同一批宿主差异、同一种验证难题。

分开改,等于把同一个坑,踩八遍。

这轮改了什么?

一句话:加的功能不算多,改的是"AI 凭什么写对"。

拆成三层:

1. 文档层:VBA 官方文档、PIA 官方互操作文档、OpenXML 官方文档,全部离线随包;

2. 语料层:把实时操作那边已经跑通的实测,翻成插件这边可以直接抄的 C# 配方;

3. 验证层:逻辑冒烟 + 全量部署冒烟 + 人工 UI,三道关,前两道全自动。

先看一张表:

Skill
这轮补的"原典"
这轮补的"实测"
交付前的关卡
Word COM
PIA 1,924 份类型页 + OpenXML 全量索引
40 条 C# 配方(+9 条 OpenXML)
逻辑冒烟 → 全量部署 → 人工
PPT COM
PIA 1,056 份类型页 + OpenXML 全量索引
22 条 C# 配方(+7 条 OpenXML)
同上
ExcelDNA
PIA 2,169 份类型页
3 条 smoke(共用底座 27 条配方)
同上(仅 netfx)
Outlook COM
PIA 2,085 份类型页
5 套蓝图 × 3 条 smoke
逻辑冒烟 → 全量 → 人工(仅 Office)
Excel VBA
VBA 文档 9,306 页
3 套蓝图,冒烟 12/12
编译探针 → 真跑 → 人工
Word VBA
VBA 文档 9,503 页
6 套蓝图
同上
PPT VBA
VBA 文档 6,038 页
6 套蓝图
同上
PBI 外部工具
TOM 422 / DAX 509 / PQ 770
29 个实测脚本
构建 → 参数冒烟 → 真机

说实话,这轮最累的不是写脚本,是承认一件事:以前的参考文档,不够 AI 用。

不是写得少,是写得太像"给人看的"。

人看文档,看到"调用 Find.Execute 做替换",就够了;剩下的他会自己试、自己调、自己问。

AI 不会。

它会把这句当成完整指令,直接写下去;参数顺序错了,它不知道;WPS 下不生效,它也不知道;等你发现,它已经交付了。

给 AI 的文档,不能只写"怎么做",还得写"哪里会错、错了怎么认、这个宿主行不行"。

这就是这轮要补的东西。

这轮的总账,先记在这儿:

  • 24,847 页 VBA 官方文档,覆盖 Excel / Word / PowerPoint 三个宿主;
  • 7,234 份 PIA 类型页,88,088 条成员口径,1,843 个枚举,17,933 个常量;
  • OpenXML 全量 API 索引:4,314 个类型、33,945 条成员、63 篇官方 how-to;
  • 89 条 C# 主干配方(Word 40 + PPT 22 + Excel 底座 27),另加 16 条 OpenXML 配方;Outlook 侧是 5 套蓝图各带 3 条 smoke,不重复计入;
  • 两道自动关卡,把插件开发的反馈环从"分钟级"压到"秒级",实测提速 20–40 倍;
  • 文件层批量处理,Word 快 约 1,700 倍,PPT 快 684 倍。

数字先放这儿,下面一条条讲清楚它们是怎么来的、能干什么、不能干什么。

从 6 月第一套 VBA Skill 到现在,四个多月;这八套不是一天长成的,是一轮一轮补出来的。

二、实时操作和写插件,本来就是同一套知识

先说一个我这两年越来越清楚的判断:

实时操作,是 AI 替你现在动手;写插件,是 AI 教软件以后自己动手。

看起来是两件事,其实是同一套知识的两种用法。

你想想,一个 Word 加载项在干什么?

  • 它拿到当前的 Application、Document、Selection;
  • 它调 Range.Find 做替换,调 Tables.Add 建表,调 Fields.Add 插域;
  • 它把结果写回文档,再回读一遍确认。

这跟"实时操作"里的每一步,有区别吗?

没有。

同一句 Range.Find.Execute,在实时操作里是 pywin32 的一行调用,在插件里是 C# 的强类型签名;参数一样、枚举一样、WPS 的脾气也一样。

差别只在三处:

  • 谁在动手:实时操作是 AI 现在动手,插件是软件以后自己动手;
  • 什么形态:实时操作是脚本,插件是 DLL / XLL / xlam;
  • 怎么验收:实时操作看眼前这份文件,插件要看"装到别人机器上还灵不灵"。

但知识本体,是同一套。

举个最小的例子。

你要在 Word 里,把全文的"旧公司名称"换成"新公司名称"。

实时操作里,是 pywin32 的一行:

doc.Content.Find.Execute("旧公司名称", False, False, False, False, False, True, 1, False, "新公司名称",2)

十一个参数,一个都不能省。你要是图省事写命名参数,它返回 True,但替换不生效——这是 pywin32 的封送坑。

到了 C# 插件里,同一个需求:

doc.Content.Find.Execute(
FindText:"旧公司名称", ReplaceWith: "新公司名称",Replace:WdReplace.wdReplaceAll);

命名参数可以用,可选参数由编译器封送;同一个坑,消失了。

但另一边,又长出新的:Range.Value 不能直接用、Range.Sort 的默认方向是错的、Style 得走 set_Style。

同一个需求,两种语言,两套坑。

所以"Office 自动化知识"不能只维护一份:语言变了,封送层变了,坑也跟着变。

问题就出在这儿。

过去这两份知识是分头长的:

  • 实时操作那边,Word 有 128 个对象模型语料函数、Excel 有 416 个(om_* 模块),还有成体系的证据等级、双宿主实测;
  • 插件这边,多数时候只有"模板 + 示例代码",参数靠记忆、行为靠猜、WPS 差异靠用户报错。

这就好比:同一个师傅,白天在客户现场干活,晚上回家教徒弟,却只给徒弟一本产品说明书。

说明书不是手艺。

所以这一轮,我把两条线并在了一起:把实时操作那边"跑通过的",变成插件这边"抄得到的"。

不是把 Python 代码翻成 C#,那太浅了。

是把"验证过的行为",翻成"可复用的判断"。

三、第一层:把"原典"搬进来

先说文档。

这一轮补了三座库:VBA、PIA、OpenXML。

VBA:24,847 页,先让 AI 学会"母语"

宿主
条目
对象
枚举
常量
Excel
9,306
470
424
4,272
Word
9,503
492
508
5,612
PowerPoint
6,038
417
279
3,411
合计24,8471,3791,21113,295

你可能会问:写 C# 插件,为什么要塞 VBA 文档?

因为 VBA 文档是 Office 对象模型的"母语说明书"。

C# 的 PIA 是它的强类型投影:对象一样、层级一样、枚举值一样;差别只在类型系统和封送层。

很多行为,PIA 的 XML 里只有一句签名,VBA 文档里才有 Remarks、才有示例、才告诉你"这个参数在什么情况下会被忽略"。

而且,VBA 那三套 Skill 本身就要用。

查法做成了命令行,六种:

--id      完整 api id,如 Excel.Range.Merge
--object  对象名,如 Range / Shapes
--member  成员名,配合 --object
--enum    枚举名,如 XlDirection
--const   常量反查,如 xlUp
--kw      关键词搜标题与摘要

举个真例子。AI 要写 xlUp,以前是背 -4162;现在是查:

xlUp =-4162   (枚举 XlDirection;晚绑定直接写数值 -4162)
  含义:Up.
  枚举页:api/Excel.XlDirection.md

背的会错,查的不会。

文档里到底有什么?

不只是"一个 API 一页":

  • 对象页:方法、属性、事件、返回值、Remarks;
  • 枚举页:成员、数值、适用对象;
  • 常量页:属于哪个枚举、值是多少;
  • 语言参考:语句、函数、常量、VBIDE 模型;
  • 概念篇:这个对象模型为什么这么设计。

连"语言参考"都有用。

AI 写 VBA,最容易犯的错不是不会调 API,是风格上的坏习惯:On Error Resume Next 一把梭、Select / Activate 满天飞、循环里反复读单元格。

这些,文档里都有正确写法;它不查,就会按训练数据里最常见的烂代码写。

文档不是给 AI 背书,是给它刹车。

常量更典型。Excel 有 4,272 个、Word 有 5,612 个、PPT 有 3,411 个;靠记忆,迟早写错一个数字,然后花半小时排查一个"看起来完全正确"的宏。

现在不用背了。

PIA:7,234 份类型页,强类型的官方口径

宿主
类型页
成员条目
枚举
常量
Excel
2,169
30,578
512
4,839
Word
1,924
23,779
623
6,367
PowerPoint
1,056
11,436
287
2,941
Outlook
2,085
22,295
421
3,786
合计7,23488,0881,84317,933

PIA(Primary Interop Assembly,主互操作程序集)是写 C# 加载项的"强类型官方口径":C# 签名、ref object 可选参数怎么展开、接口/委托/枚举结构、DispId、枚举数值——全在本地。

六种查法:--id / --object / --member / --enum / --const / --kw。

查一个成员,长这样:

==P:Microsoft.Office.Interop.Word.Range.Style[property]
Range.Style
   摘要:Returns or sets the style for the specified object.
C#: public objectStyle{ get; set;}

查一个常量:

wdAlertsAll =-1   (枚举 WdAlertLevel;C# 直接写数值 -1)

不过,有三条结构规则,不知道就会白查:

1. coclass 和接口分列:Application 是 coclass 视图,成员多半是空壳;真正的成员在 _Application、_Document 这类接口上。查 --object Application 得到 0 成员,不代表没有,去掉或加上下划线再查。

2. 事件在 *Events*_Event 接口:比如 ApplicationEvents4_Event,按事件名搜。

3. **可选参数是 ref object**:C# 里传 Type.Missing,或者用 dynamic;枚举常量用 --const 拿到数值后,可以直接写数字。

这三条不是 API 知识,是"查法知识"。

查法不对,AI 会得出"这个成员不存在"的错误结论,然后转身去编一个。

但我要提醒一句:文档不是圣经。

这轮实测,撞出三类"文档和编译器不一致"的情况,每一类都值得说。

第一类:文档写 Boolean,PIA 是 int。

VBA 文档里,PageSetup.DifferentFirstPageHeaderFooter 是 Boolean;到了 PIA,它是 int。你写 true,编译报 CS0029。

正确写法是 -1(开)/ 0(关)。

更阴的是 Borders.Enable:写 1,编译过、运行也过,但回读不等于 1;必须写 -1。

这就是"看起来对、跑起来错"的典型。

第二类:文档写 property,编译器让你用访问器。

Range.Style 在 PIA 的 XML 里,确实是一个属性;但你写 rng.Style = style,编译报 CS1545。

原因是它同时有 propput 和 propputref 两种形式,C# 只生成 set_Style(obj) / get_Style()。

听谁的?

听编译器的。然后回来,把这条写进语料。

第三类:文档有,代码没有。

Application.Ready,微软文档里明明白白写着;但在 COM 自动化里,IDispatch 和 PIA 双证据都读不到——它只存在于 VBA 宿主内。

所以判活不要用 Ready,用 Version。

这也是为什么,PBI 那边的 tom-docs 要做成"先反射本机 DLL,再抓文档页":查到的每个 API,本机一定存在;不存在于本机的,文档里也不给你。

一句话:文档告诉你"应该是什么",编译器告诉你"到底是什么",真机告诉你"现在能不能用"。三方对账,才算数。

OpenXML:文件层的另一把刀

COM 很强,但 COM 不是万能。

这轮给 Word 和 PPT 各补了一座 OpenXML 官方文档库:

  • index.json 6.7 MB,4,314 个类型、33,945 条成员;
  • 63 篇官方 how-to(PowerPoint 侧 60 篇);
  • 六种查法:--type / --member / --enum / --const / --kw / --howto。

查一个类型:

[类型]DocumentFormat.OpenXml.Packaging.SlidePart
ns:DocumentFormat.OpenXml.Packaging
summary:Defines the SlidePart
  成员 35 个(--type SlidePart --member <名> 查明细)

为什么要两把刀?

因为有些事,COM 根本做不到。

比如 PPT 的"节"(Section)。PIA 里没有强类型成员,dynamic 走 IDispatch 也抛 RuntimeBinderException——这是一个负能力,不是你不会写。

再比如批量性能。Word 里写 300 个段落:

  • COM 逐段写:3,419 ms;
  • OpenXML 一次性建包:2 ms。

约 1,700 倍。

PPT 侧同类场景,684 倍。

所以现在插件 Skill 的判断链是:当前窗口的交互走 COM,文件包的批量处理走 OpenXML,COM 够不到的地方才换路线。

不是"哪个高级用哪个",是"哪个合适用哪个"。

一条纪律:修错两次,先查库

文档库再大,不用也白搭。

所以八套 Skill 都写进了同一条纪律:

同一个问题,凭记忆修错两次,就停止盲试。

先查本地库;库里没有,再联网;联网还没有,再凭通用知识写,但写完必须实测。

这不是为了省 token。

是为了省"幻觉"。

模型最危险的地方,不是它不会,是它会一本正经地编。你让它猜第三次,它不会告诉你"我在猜",它只会给你一个更像答案的答案。

把"查"变成流程里的硬动作,它才没有机会编。

四、第二层:把"实测"搬过来

文档解决"查得到",但不解决"对不对"。

所以第二层,是把实时操作那边已经跑通的代码,翻成插件这边可以直接抄的配方。

一条配方,长什么样

不是一段示例代码,是四样东西:

1. 方法体:可以整段抄进加载项的 Logic 层;

2. 回读断言:写完怎么证明它真的生效;

3. 坑注:这条在什么宿主、什么版本、什么参数下会翻车;

4. 双宿主战绩:Office 过没过、WPS 过没过、差异属于哪一类。

配方文件的头部,会把来源和坑直接标出来,比如:

// recipe: Word.OpenXml.CreateDocRoundtrip
// source: 16 号「文件层负责离线处理」标准建文档模式
// pitfall: WordprocessingDocument.Create 后 Dispose 才落盘;
//          InnerInnerText 不存在,用 InnerText

AI 看到这条,就不用"设计"怎么写;它只需要判断"这条合不合我的场景"。

合用,抄;不合用,改;改完,跑。

这就是"语料"和"示例"的区别:示例是给人看的,语料是给 AI 抄的。

配方里的回读,长这样:

// 写
range.Text=&amp;quot;自动化改写成功&amp;quot;;

// 读(独立回读,不复用写入时的 Range)
string text = doc.Content.Text;
lab.Assert(text.Contains(&amp;quot;自动化改写成功&amp;quot;),&amp;quot;改写未生效&amp;quot;);

注意那个"不复用":写之前拿到的 Range、Content.End,写之后未必还对。

后面你会看到,这轮全量冒烟模板自己就栽在这上面。

转写不是翻译:坑也会迁徙

这一轮最值钱的发现,不是"抄了多少条",是坑的迁徙图。

我们把 Word 实时操作那边的五个经典坑,逐条翻成 C# 重测,结果是:

两个消失,三个留下,另外还长出十几个新的。

Python 侧的坑
C# PIA 的裁决
Find.Execute
 命名参数静默失效
不复现
:PIA 下编译器帮你封送可选参数,命名参数双宿主生效。pywin32 专属封送坑
OpenDataSource
 密码参数必须传空串
不复现
:PIA 下直接省略(Type.Missing)即可——但 WPS 宿主替换回读失败,属 B 类差异
插域后 Content.Text 偏移错位
保留
:C# 同样必须每次取新 Range、用 Find 定位,不能按文本偏移算
页眉 PAGE 域读数为 0
保留
:无界面会话没有分页渲染,冒烟不要断言页码数值
单元格文本带 \r\x07
保留
:C# 读回同样带隐藏标记,比较前必须清洗

新长出来的,更狠:

  • Range.Value 是参数化属性,rng.Value 直接编译不过,一律用 Value2;
  • ws.Cells[r, c] 索引器返回 object,必须 cast 到 Range;
  • Range.Sort 的 PIA 缺省 Orientation 生成错误,省略就变成"按行排序",必须显式写 xlSortColumns;
  • VBA 的 True 在 PIA 的 int 属性上一律写 -1;
  • Range.Style 编译报 CS1545,改 set_Style;
  • PowerPoint 不允许隐藏应用窗口,Visible = msoFalse 抛 0x80048240;
  • 同名字段跨宿主三型:Font.Bold 在 Excel 是 bool、Word 是 int、PPT 是 MsoTriState。

你看出问题了吗?

如果只是把 Python 代码丢给 AI,说一句"翻成 C#",它会把那三个"留下的坑"原样带过去,再踩上这十几个新的——而且它不会告诉你,它只是"翻"了,没有"验"。

跨语言不是翻译,是重新验证。

这就是为什么,这轮不是"让 AI 翻译",是"让 AI 在真宿主里重跑一遍,再把结论写成配方"。

三条线的战绩:89 条配方

线
配方数
Office
WPS
Word
40
38 通过 + 2 人工
30 通过 + 2 人工 + 5 差异
PowerPoint
22
22 / 22
15 通过 + 4 差异
Excel 底座
27
27 / 27
23 / 27

注意,差异不是"失败"。

我们把差异分成三类:

  • A 类·能力缺失:宿主根本没有这个能力,比如 WPS 下 InsertCaption 报 E_FAIL;
  • B 类·语义差异:能力有,行为不一样,比如 WPS 的 ListRows.Add() 之后计数不增长;
  • C 类·静默失败:最危险的一类——不报错、不生效,比如 WPS 的 MailMerge.Execute,数据源打开了、执行也不报错,就是不替换。

A 类告诉你"换路线",B 类告诉你"加探针",C 类告诉你"别信它"。

这三类写进配方,AI 才知道:什么时候可以照抄,什么时候必须分宿主写,什么时候要先跑一次探针。

负能力:知道"做不到"更值钱

我越来越觉得,一份好的技术文档,不是告诉你"能做什么",是告诉你"什么做不到、为什么、该换哪条路"。

这轮我们专门收了一批"负能力":

  • PPT 的节 API:PIA 无强类型成员,dynamic 也进不去——走 OpenXML,或注入 VBA 宏;
  • WPS 演示的复制页:Duplicate 和 Copy+Paste 都静默无效,页数不变、也不报错——走重放构建或 OpenXML;
  • **Word 的 Application.Ready**:COM 自动化不可见——用 Version 判活;
  • Excel 的数据模型:程序化"播种"不可行——只对已有模型做读改,不负责从零造。

AI 最怕的,不是"不会",是"不知道自己不会"。

负能力写进文档,它才知道什么时候该刹车、该换道,而不是一头撞上去,撞完还告诉你"已完成"。

五、第三层:把"真机"装进开发环

文档有了,配方有了,还剩最后一个问题:写完怎么验?

这一轮最大的工程改动,就在这儿。

旧流程:改一行,等两分钟

以前的插件冒烟,是一条很长的链:

改代码 → build → 注册 COM → 重启 Word / Excel / PPT → 点 Ribbon → 看日志 → 关宿主 → 卸载。

一轮 1–2 分钟。

人一天能忍十轮;AI 呢?

它会在第三轮开始"猜"——因为试错太贵,它宁愿一次多改一点,也不愿意为了验证一个小函数重启一次 Office。

试错成本,决定试错密度;试错密度,决定交付质量。

这是我做这一轮底座升级最原始的动机。

新底座:台架先跑,路试后跑

我把实时操作那边的 excel-com-base 搬了过来,做成一个叫 CsLab 的实验室:

  • net462 + Roslyn CSharpScript 宿主;
  • 进程外附着或新建真实 Office 实例;
  • 引用与加载项同一套 PIA;
  • 每条配方独立跑、独立回读、证据落 JSONL。

于是冒烟变成两道关卡。

第一道·逻辑冒烟(秒级)

构建用户工程(不注册、不加载 XLL)
  → CsLab 引用用户 DLL
  → 跑 smoke_logic.csx
  → 独立回读断言
  → smoke-evidence/*.jsonl

不注册、不重启、不点按钮。改一次逻辑,跑一次。

这就是"非 UI 部分的接口测试":不开窗格、不走 Ribbon,直接把 Logic 层当接口调。

实测数据:

  • 单条配方 68–1449 ms;
  • 11 条种子配方,约 4 秒/宿主;
  • 对比完整 build + 注册冒烟 1–2 分钟/轮,提速 20–40 倍;
  • 写一条配方到拿到证据,不超过 30 秒。

而且它稳:

  • 90 秒看门狗,防宿主挂死;
  • 退出码 42(宿主硬退)自动换实例重试;
  • Office 孤儿进程按 PID 差量清理,不误杀你正在用的窗口。

这套东西能成立,靠的是一条"可测性约定":

  • Logic 层方法 public static;
  • 宿主对象(Document / Range / Slide)强类型参数直收;
  • 返回基元或字符串,不返回 COM 对象;
  • 不碰 Ribbon(功能区)、CTP(自定义任务窗格)、日志单例、当前选区;
  • 写操作由冒烟脚本独立回读,写入和读取分开。

哪些代码该进 Logic 层?

我的判断很简单:

  • 能脱离 UI 单独描述的业务规则;
  • 入参出参都是数据,或者宿主对象(Document / Range / Slide);
  • 不依赖"当前选区""当前窗口""当前用户点了哪个按钮"。

反过来说:Ribbon 回调、CTP 显示隐藏、WebView2 导航、日志单例——这些留在 UI 层,不往 Logic 里塞。

不是为了测试好看,是为了让 AI 有个可验证的核心:

界面可以最后接,逻辑必须先跑通。

类型统一也实测过了:EmbedInteropTypes=true 的用户 DLL,可以被实验室直接强类型调用,不需要 dynamic;真要用 dynamic 兜底,工程必须显式引用 Microsoft.CSharp,否则报 CS0656。

第二道·全量部署冒烟(分钟级)

注册表安装 → 宿主加载 → COMAddIns.Connect → Automation 主链。

这一道验的是"装得上、连得上、调得动",交付前跑一次。

第三道·人工验收

Ribbon 长什么样、CTP 会不会被宿主抢焦点、WebView2 退出会不会白屏、窗口切换后任务窗格跟不跟手——这些只能人看。

最讽刺的一件事:冒烟脚本自己也会错

这轮端到端实测,抓到一个很讽刺的 bug:全量冒烟模板自己有问题。

原来的断言,用的是改写前捕获的 Content.End 去截范围。

替换文本比原文长的时候,尾字会被裁掉。

实测:7 个字的「自动化改写成功」,被裁成 6 个字,断言失败——但文档其实改对了。

修法很朴素:改写后重新取端点。

这条我写进了文档,因为它说明一件事:

测试也会骗你。

所以断言必须独立回读,不能用写之前的快照;回读坐标和目标坐标,必须逐个对照。

这也是我不太相信"AI 说自己测过了"的原因——测过,和测对,是两件事。

边界:台架不是路试

说清楚三条边界:

1. CsLab 只支持 netfx(net462 / net48)。net6+ 的 CLR 与它不互通,逻辑冒烟不可用,直接走第二道;

2. lab 不覆盖进程内行为:Ribbon 回调、CTP 生命周期、WebView2、COM 事件——这些只有真加载项能验;

3. 台架不替代路试:逻辑冒烟过了,只说明"这段逻辑对";装到别人机器上灵不灵,还得靠第二道和人工。

但话说回来:

台架不是路试,可没有台架,你只能靠路试撞运气。

两道关卡不是"更严",是"更快"。因为快,AI 才愿意多测;因为多测,交付才稳。

到这轮为止,插件开发的工作流,已经收敛成五步:

1. 问清:宿主、入口、UI、安装方式;

2. 查/选:查文档,选最接近的蓝图;

3. 抄/写:抄配方写 Logic,抄不到的才新写;

4. 逻辑冒烟:秒级,改一次跑一次;

5. 全量 + 人工:交付前跑一次,UI 自己看一眼。

这五步里,AI 能自己完成前四步;第五步的"看一眼",还是得你来。

六、同一套能力,三种吃法

八套 Skill,不是八套各干各的。

说是"换底座",其实换的不是同一个零件:C# 加载项换的是验证引擎,VBA 换的是文档与冒烟链路,PBI 换的是知识域。

但底层思路是同一条。

八套铺完,我回头看,发现它们其实是同一套实时操作能力的三种吃法。

C# 加载项:复用"验证底座"

ExcelDNA、Word、PPT、Outlook,四家 C# 加载项共用同一套 CsLab 底座。

ExcelDNA 这边有 11 套蓝图,我常跟人说:这不是模板库,是教材库。

UDF 六域样本、工作簿自动化、OpenXML 批量读写、报表模板、完整工作台、异步公式与命令、共享缓存、注册管线、配置状态、组织树集合、netfx 基线——每一套都对应一类高频场景。

选哪套,只决定你的项目骨架;写业务时,还得去别的蓝图里翻 helper、翻状态模型、翻异常处理。

Excel 底座那 27 条配方,是 CsLab 的母本语料;ExcelDNA 复用同一个实验室,项目内另有 3 条 smoke 用例随蓝图生成,开箱即跑。

ExcelDNA 这边,先翻一段旧账:轨道对齐。

之前有个很隐蔽的坑:runtime 里的 XLL 是 net6 宿主(二进制里带 hostfxr),但编译的是 net462 的 dll。表面上 UDF、IntelliSense、Ribbon 都正常,可 Excel 端一创建 CTP 就报 0x80004005,WPS 端反而成功。

同一份代码,两个宿主,两种命运。

后来用源码重编了 netfx 宿主,问题才收口;顺带确认:netfx 路线在 WPS 个人版免关沙箱。

这也是为什么,现在 ExcelDNA 默认推荐 net462,除非需求明确命中 .NET 6+ 专属能力。

Word COM 这轮的关键词是"文件层收口"。

OpenXML 方案从"路线说明"变成"实测可抄":9 条独立回读,CsLab 双宿主全绿;Logic/WordOpenXmlOps.cs 里带着"离线编辑会话 + 跨 run 正解 + 状态读写"。

七大坑也一并写进去了,包括"跨 run 替换"这个金坑、3.x 双程序集 CS0012、IdPartPair 在 3.x 没有 Uri、包部件遍历只返直接关系件、图遍历不去重会递归栈溢出静默死进程。

还有那个 1,700 倍:写 300 段,COM 3,419 ms,OpenXML 2 ms。

UI 侧也有它的老规矩:Word 是 SDI,任务窗格要按"活动窗口"维护,不能做成全局单例;CTP 外层保持 WinForms UserControl,里面用 ElementHost 承载 WPF——这套骨架,蓝图里已经搭好了。

PPT COM 这轮的关键词是"负能力正解"。

7 条 OpenXML 配方,把"COM 做不到"的三件事讲清楚了:复制页、节、落盘竞态。

  • 复制页要三件事:克隆部件、挂关系、修 ID;
  • 节在 extLst 的 p14 命名空间里;
  • 落盘会撞 0x80070570,要 sleep + 重试 + 降级人工。

性能 684 倍。

另外,0x80048240 这条也值得记住:PowerPoint 不允许隐藏应用窗口,而 Excel 允许。跨宿主移植,连"窗口可见性"这种常识都要重测。

Outlook COM 这轮的关键词是"对象联动"。

Outlook 不是"一个文档",是五个对象世界:邮件、日程、联系人、任务、便签。

所以这套 Skill 给了五个工作台蓝图,每个都按对象联动设计:邮件生成跟进草稿、日程生成会后任务、联系人生成外联邮件、任务生成汇报邮件、便签转任务。

PIA 2,085 份类型页、22,295 条成员口径;Ribbon 要按 Explorer / Inspector 分流,右键菜单按对象挂到 ContextMenuMailItem 这类上下文菜单上。

逻辑冒烟这块,Outlook 只跑 Office 宿主——WPS 没有 Outlook 等价物;还有一个细节:单例 COM 刻意不 Quit,进程交给 PID 差量清理,避免误杀你正在用的 Outlook。

VBA:复用"操作链路"

VBA 三套,天生就是实时操作流。

它的冒烟不是 mock,是真的让 Excel / Word / PPT 把宏跑一遍:

  • 编译探针:把模块和窗体导入 VBProject,插一个探针过程强制编译;
  • 运行探针:Application.Run 真跑;
  • 内容断言:回读单元格、段落、形状;
  • 示例文件断言:连示例工作簿一起验。

Excel VBA 这轮还补了一套 chart-pivot-reporting-addin 蓝图:图表、透视表、字典聚合、CSV 导出。

实测 12/12:柱形图 ChartType=51 落在图表页、透视表行标签含品类与总计、字典聚合金额精确、CSV 以 UTF-16 落盘。

这三套 VBA Skill 的文档库,就是前面那 24,847 页:Excel 9,306、Word 9,503、PPT 6,038。

Word 和 PPT 各有六套蓝图,Excel 三套;蓝图不是"模板",是"教 AI 写正确 VBA 的样本"——先选一套当骨架,再按索引去别的蓝图里抄 helper、抄状态模型、抄异常处理。

比如 Word 那边,合同模板、会议纪要、内容控件、表格规范化、公文评审、窗体录入,各有一套;PPT 那边,汇报 deck、评审、版式、形状批量格式、窗体录入,也各有一套。

为什么不合并成一套"万能模板"?

因为 VBA 的坑,是按场景长的:合同要看条款编号和页脚,纪要要看标题层级和待办,内容控件要看占位符和锁定——你把这六个场景揉进一个模板,AI 反而不知道哪个写法对应哪个需求。

分开,是为了让它"照着这一类抄",而不是"从一大锅里舀"。

Power BI 外部工具:复用"知识域"

PBI 这套最特殊。

它不是 COM 加载项,是一个独立的 WPF 程序,靠 pbitool.json 注册到 Power BI 的"外部工具"功能区,再用 %server% / %database% 连回模型。

所以它复用的不是 CsLab,是整个 PBI 实时操作的知识域:

  • 25 篇 references(18–42 号):活会话附加、TOM 对象模型、57 条体检规则、DAX 最佳实践、VertiPaq 性能、写回策略、PBIP-TMDL、DMV 目录、自动化三件套、BPA 覆盖图;
  • 29 个实测 Python 脚本:live_core、om_tom_ops、adomd_query、batch_ops、trace_ops、model_health、fix_apply……
  • 五座库:TOM 422 个类型、DAX 509 条、PQ 770 条、BPA 71 条官方规则、DMV 74 个 rowset;外加 SQLBI 的 488 个 DAX 函数版本矩阵。

为什么要这么重?

因为外部工具不是"一个按钮",是"一把手术刀"。

它要改的是别人的模型:加度量值、调格式、改关系、刷新区、跑 DAX、看执行计划。改错一步,用户可能一周后才发现数字不对。

所以这轮把实时操作里最硬的三件事也搬了过来:

  • 事务批量原子写:defer + 单次 SaveChanges,失败 UndoLocalChanges,零残留;
  • Trace 性能诊断:FE / SE 并集拆分、并行度、xmSQL、缓存命中,按官方口径读;
  • ADOMD 查询防挂死:强制 CommandTimeout,不让一个查询把工具拖死。

还有两个小坑,也一并写进了主文档:

  • 参数两侧有引号:Power BI 传过来的是 -server "localhost:52874" -database "9d4b2f0c-...",解析时必须 Trim('"');不 trim,连接串就多两个引号。
  • 无参数启动 = 手动模式:用户直接双击 exe 时,不能报错退出,要给他一个"运行中的实例选择器"。

这些不是"高深技术",是"装到别人机器上第一天就会遇到的事"。

还有一份"官方 Modeling MCP 21 组工具兼容矩阵"——不是照抄,是逐项对照:它有什么、我们有什么、它在哪一步比我们省事、我们在哪一步比它更懂模型。

把别人的接口文档吃透,不是为了复刻,是为了知道哪条路该自己走。

一张总表

Skill
文档库
实测语料
蓝图
交付前怎么验
Word COM
PIA 1,924 + OpenXML
40 条 C#(+9 OpenXML)
2
逻辑 → 全量 → 人工
PPT COM
PIA 1,056 + OpenXML
22 条 C#(+7 OpenXML)
2
逻辑 → 全量 → 人工
ExcelDNA
PIA 2,169
3 条 smoke(共用底座 27 条)
11
逻辑 → 全量 → 人工
Outlook COM
PIA 2,085
5 套蓝图 × 3 条 smoke
5
逻辑 → 全量 → 人工(仅 Office)
Excel VBA
VBA 9,306
3 套蓝图(12/12)
3
编译 → 真跑 → 人工
Word VBA
VBA 9,503
6 套蓝图
6
编译 → 真跑 → 人工
PPT VBA
VBA 6,038
6 套蓝图
6
编译 → 真跑 → 人工
PBI 工具
TOM 422 / DAX 509 / PQ 770
29 个脚本
4
构建 → 参数 → 真机

七、为什么 Flash 模型也能交付

东西齐了,我最想验证的一件事是:

如果模型根本不知道这些 API,它还能不能写出能用的插件?

答案是可以。

不是因为它突然变强了,是因为能力被外置了。

外置成三样东西:

1. 文档:不懂就查,本地就有,不用联网、不用碰运气;

2. 配方:查到就抄,抄的是别人跑通过的写法,不是它自己编的;

3. 闸门:抄完就跑,跑不过就改,改完再跑。

我拿一个最小需求走一遍。

你说:"给 PPT 加一个按钮,把选中的文本框字号统一改成 28。"

它会这么走:

1. 查:--object TextRange --member Font,确认 Font.Size 是 float,不是 int;再 --const msoTrue,确认三态枚举的值;

2. 抄:去 powerpoint-interop-recipes 里找"形状文本格式"那一条,把方法体抄进 Logic 层;

3. 跑:逻辑冒烟构造一个带窗口的演示、放一个文本框、调方法、回读 Font.Size;

4. 改:如果 WPS 下 TextRange.Replace 一类 API 有差异,配方里已经标了"先探针",它就不会硬上;

5. 交付:全量部署冒烟过一遍,你打开 PPT 点一下按钮。

整个过程,它不需要"记住"PPT 的对象模型;它只需要会查、会抄、会跑。

这三样在,Flash 模型和顶级模型的差别,就变成了:

  • 查得快不快;
  • 一次写对的概率高不高;
  • 复杂需求下,架构判断准不准。

而不是"能不能交付"。

说白了:

顶级模型的上限更高,但交付的下限,应该由闸门来决定,而不是由模型的记忆来决定。

这也是我为什么坚持"本地语料",而不是"等模型更新":

  • 模型会换代,语料不会;
  • 今天用 Flash,明天用顶级,底座是同一套;
  • 模型不知道的,语料知道;模型记错的,实测纠正。

但我也得说句实话:

闸门保证下限,不保证上限。

它能拦住"参数写错""宿主不支持""写完没生效",拦不住"这个功能一开始就不该这么做"。

业务判断、架构取舍、什么该自动化、什么该留给人——这些还是得你来。

工具能替你验错,不能替你负责。

八、边界与门槛:丑话说在前面

第一,还是 Windows 专属。

COM、PIA、VBA、Power BI 外部工具,都是 Windows 桌面体系的能力。Mac 上跑不了,这不是我不努力,是路线决定的。

第二,需要本机装 Office 或 WPS。

实时操作要真宿主,插件冒烟也要真宿主。CsLab 不会给你模拟一个"假 Word"。

第三,逻辑冒烟只支持 netfx。

net462 / net48 可以;net6+ 直接走全量部署冒烟。这不是偷懒,是 CLR 不互通。

第四,文档库第一次用要解压。

VBA、PIA、HandyControl、OpenXML 的文档大多打包成 zip 母本;首跑由 AI 自己执行 --extract,线程池并行解压,校验通过后自动删母本。删不掉就静默跳过,不影响检索。

第五,文档有版权,也有边界。

VBA 文档来自 MicrosoftDocs/VBA-Docs(CC BY 4.0);PIA 文档来自微软官方 PIA 参考库(MIT/CC);OpenXML 文档来自 OfficeDev/open-xml-docs(MIT)。原典保留署名、页面内容零修改,Skill 只做本地检索,不搬运文字。

第六,文档不是保证。

官方文档也会漏、会错、会过时。所以每条关键写法都要过实测;文档和实测冲突时,以实测为准,再回来更新文档。

第七,它不测 UI。

Ribbon 好不好看、CTP 会不会闪、WebView2 会不会白屏——这些只能人看。自动化能替你省掉 80% 的重复验证,省不掉最后那 20% 的眼睛。

第八,它不替你做业务判断。

工具做什么、给谁用、什么叫做"好用"——这些是你的决定。Skill 只负责把它做出来、验出来。

第九,版本会漂。

文档会更新,宿主会升级,今天的"已验证"可能明天就变成"待复测"。所以每套 Skill 都有 CHANGELOG 和证据文件;看到"verified",也请看它是哪一天、在哪个宿主上验的。

九、写在最后:把"手艺"变成"底座"

这轮做完,我最大的感受不是"又补了多少页文档"。

是:我以前太依赖"我记得"。

我记得 xlUp 是 -4162;我记得 Range.Style 要写 set_Style;我记得 WPS 的 ListObjects.Add 会忽略命名参数;我记得 PPT 不能隐藏窗口。

可这些"我记得",对 AI 有什么用?

它不在我脑子里。

它能用的,只有两样:手边的文档,和跑得动的真机。

所以这一轮,我做的事情,说到底就三句:

  • 把"我知道",变成"它查得到";
  • 把"我试过",变成"它能复现";
  • 把"我相信",变成"它过得了"。

这才叫底座。

不是多了几个函数,不是多了几个按钮,是换了一种让 AI 写代码的方式:

不懂就查,查到就抄,抄完就跑,跑不过就改。

以前它靠猜,现在它靠证据。

手艺人的本事,从来不在手上,在标准里。

标准立住了,谁来干,都不会太差。

这套底座也不是终点。

新的宿主、新的文档、新的配方,都可以往里接;骨架不用改,闸门不用改,AI 换一代,也不用改。

这才是"底座"两个字的意思:不是今天能跑,是明天还能长。

叫上身边的人,一起来把 AI 编程玩转。

想体验全套免费 Skill?欢迎加好友进入免费社群,关注公众号,后台回复「Skill」,不要理会AI自动回复,等我发联系方式,加好友进群自助获取。

相关学习资料