乐于分享
好东西不私藏

给WPS写一个COM加载项图表插件

给WPS写一个COM加载项图表插件

给 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 : IDTExtensibility2IRibbonExtensibility {    // IDTExtensibility2 — 插件生命周期   publicvoidOnConnection(...) { }    publicvoidOnDisconnection(...) { }    // IRibbonExtensibility — 返回 Ribbon XML    publicstringGetCustomUI(...) { }  }

关键点: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: 创建新实例    }  }

解决方案资源管理器 

两条图表路径

插件提供了两种创建图表的方式,各有侧重:

原生图表 (AddChart)
形状图表 (Shape Builder)
调用方式
slide.Shapes.AddChart()AddShape() + AddLine() + AddTextbox()
编辑方式
双击打开数据表
每个形状独立可调
柱形颜色
受限于预设
任意 RGB
间距控制
固定比例
精确 pt
数据标签
固定位置
任意偏移
瀑布图
需 hack
直接构建
甘特图
需 hack
直接构建

形状图表的本质就是用 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 的终端输出,展示四步安装流程

安装步骤

  1. 下载 publish/ThinkCharts/ 目录全部文件
  2. PowerShell 运行 install.ps1(无需管理员)
  3. 重启 WPS 演示文稿
  4. 顶部出现 ThinkCharts 选项卡即完成

排查技巧:如果选项卡不出现,先运行 diagnose.ps1 看注册表项是否存在,再运行 check-loadbehavior.ps1 确认 LoadBehavior 没有被 WPS 改成 2。

后续计划

目前插件完成了基础调用链路,几件事排在后面:

1. 形状图表支持更多类型(瀑布图、甘特图、马赛克图)

2. 完善 WebView2 前端面板,让数据编辑体验更好

3. 支持从 Excel 直接读取数据创建图表

4. 探索 WPS JSAPI 加载项方案,降低安装门槛

项目开

截图清单

#
内容
怎么截
WPS Ribbon 选项卡
WPS 顶部 ThinkCharts 标签页,五组按钮
图表数据编辑器
WinForms 弹窗,DataGridView + 四个按钮
幻灯片图表效果
原生图表和形状图表并排
注册表编辑器
regedit → AddinsWL 键
VS 项目结构
解决方案资源管理器
API 测试弹窗
"WPS 版本"按钮 MessageBox
安装脚本运行
PowerShell 终端 install.ps1 输出

ThinkCharts · WPS COM Add-in 开发实践