给 WPS 写一个 COM 加载项图

背景
日常工作里经常需要在 WPS 演示文稿中插入图表。WPS 自带的图表功能基本够用,但有几个痛点:
第一,瀑布图、甘特图这些类型 WPS 原生不支持,只能手动画矩形拼,对齐全凭眼力。第二,图表的配色和字体改起来很麻烦,每次都要进属性面板逐项调整。第三,如果想在 PPT 里用统一风格的图表,几乎只能靠复制粘贴。
市面上有一些商业插件能解决这些问题,但要么只支持 Microsoft Office,要么价格不低。于是决定自己动手,写一个 WPS COM 加载项。
安装后的效果
插件安装完成后,WPS 演示文稿顶部会出现一个 ThinkCharts 选项卡,分五个按钮组:插入图表、高级图表、专业图表、原生操作、API 测试。

截取 ThinkCharts 选项卡完整区域,展示五个按钮分组
目前完成了哪些功能

COM 加载项开发过程
这是本文的重点。WPS 的 COM 加载项机制和 Microsoft Office 基本一致,但有几个坑需要留意。
项目结构
一个 .NET Framework 4.8 类库项目,实现两个核心接口:
// Connect.cs — 插件入口[ComVisible(true)][ProgId("ThinkChartsAddin.Connect")][Guid("{B2C3D4E5-F6A7-8901-BCDE-F12345678901}")][ClassInterface(ClassInterfaceType.AutoDispatch)]public class Connect : IDTExtensibility2, IRibbonExtensibility {// IDTExtensibility2 — 插件生命周期publicvoidOnConnection(...) { }publicvoidOnDisconnection(...) { }// IRibbonExtensibility — 返回 Ribbon XMLpublicstringGetCustomUI(...) { }}
关键点:ClassInterfaceType 必须设为 AutoDispatch,否则 WPS 无法通过 IDispatch 调用 Ribbon 按钮的回调方法。一开始用的 None,按钮点击完全没反应,调了半天才发现。
注册表:AddinsWL 和 Addins 双路径
WPS 用两套注册表结构来识别加载项,这和 Office 不太一样:
# AddinsWL — WPS 白名单键,下面放 REG_SZ 值HKCU\Software\Kingsoft\Office\WPP\AddinsWL└── ThinkChartsAddin.Connect = "" ← 值名即 ProgID,值数据空即可# Addins — 传统路径,创建子键承载属性HKCU\Software\Kingsoft\Office\WPP\Addins\ThinkChartsAddin.Connect├── FriendlyName = "ThinkCharts"├── Description = "..."├── LoadBehavior = 3 ← 启动时加载└── CommandLineSafe = 1
踩坑记录:LoadBehavior 设为 3 后,如果插件加载时崩溃,WPS 会静默改成 2(禁用),之后再也不加载。排查时用 check-loadbehavior.ps1 检查各路径的 LoadBehavior 值。
COM 组件注册
.NET 程序集通过 RegAsm.exe 注册为 COM 组件。因为是 32 位 WPS,必须用 32 位 RegAsm:
# 注册(32 位 RegAsm)C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase ThinkChartsAddin.dll# 注销C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /unregister ThinkChartsAddin.dll
安装脚本 install.ps1 会自动处理这些步骤,包括生成 HKCU 版本的 .reg 文件写入 CodeBase 路径。

regedit 定位到 HKCU\Software\Kingsoft\Office\WPP\AddinsWL,展示 ThinkChartsAddin.Connect 值
获取 WPS 运行实例
这里也踩了一个坑。一开始用 Activator.CreateInstance 创建 WPS 对象,但这个方法会启动一个新的 WPS 进程,而不是连接到已经在运行的实例。结果调用 ActivePresentation 时返回 HRESULT E_FAIL。
正确做法是用 Marshal.GetActiveObject:
dynamicGetWpsApp() {try {return Marshal.GetActiveObject("KWPP.Application");} catch {// fallback: 创建新实例}}

解决方案资源管理器
两条图表路径
插件提供了两种创建图表的方式,各有侧重:
slide.Shapes.AddChart() | AddShape() + AddLine() + AddTextbox() | |
形状图表的本质就是用 WPS 最基本的图形 API 拼出图表:矩形做柱形、线条做坐标轴、文本框做标签。每个元素都是独立 Shape,可以单独选中改颜色、调大小、换字体。虽然写起来比 AddChart 繁琐,但自由度完全不在一个量级。
用到的技术
C# .NET 4.8
IDTExtensibility2
IRibbonExtensibility
COM Interop
WPS COM
KWPP.Application
Marshal.GetActiveObject
dynamic 晚期绑定
WinForms
ChartDataForm
DataGridView
AxHost 图标
PowerShell
install/uninstall
diagnose/check
RegAsm + reg import
项目还包含一个 React + ECharts 的前端预览面板,通过 WebView2 承载。目前开发阶段用得不多,但为后续更复杂的交互预留了空间。

运行 install.ps1 的终端输出,展示四步安装流程
安装步骤
下载 publish/ThinkCharts/目录全部文件PowerShell 运行 install.ps1(无需管理员)重启 WPS 演示文稿 顶部出现 ThinkCharts 选项卡即完成
排查技巧:如果选项卡不出现,先运行 diagnose.ps1 看注册表项是否存在,再运行 check-loadbehavior.ps1 确认 LoadBehavior 没有被 WPS 改成 2。
后续计划
目前插件完成了基础调用链路,几件事排在后面:
1. 形状图表支持更多类型(瀑布图、甘特图、马赛克图)
2. 完善 WebView2 前端面板,让数据编辑体验更好
3. 支持从 Excel 直接读取数据创建图表
4. 探索 WPS JSAPI 加载项方案,降低安装门槛
项目开
截图清单
ThinkCharts · WPS COM Add-in 开发实践
夜雨聆风