title: Windows 插件实现与打包 date: 2026-08-27 tags:

  • AiPPT
  • Windows
  • PowerPoint
  • COM
  • NSIS
  • 打包 status: active

Windows 插件实现与打包

快速打包

WebView前置条件

Publish-Windows.ps1只消费已经同步到Windows仓库的离线页面,不会构建前端或拉取offline.zip。WebView有改动时,先执行WebView实现与打包 > 快速打包,再把ZIP内容同步到:

src/AiPPTAddin/offline/

同步时清空旧的Hash文件,并更新offline/0sync.txt

常用入口:

powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass `
  -File .\build\Publish-Windows.ps1

不传-Sources时显示22个渠道的勾选列表,默认全部选中:

↑/↓  移动
空格  勾选或取消
A     全选
N     全不选
Enter 确认
Esc   取消

指定部分渠道:

.\build\Publish-Windows.ps1 `
  -Version 2.0.5.0 `
  -Sources PLUGIN,lenovo,soft360

默认产物:

artifacts/windows/<version>/AiPPT-Setup-<version>_<source>.exe

正式发布:

.\build\Publish-Windows.ps1 `
  -Version 2.0.5.0 `
  -GitRelease `
  -Push `
  -Tag v2.0.5.0 `
  -NonInteractive

发布要求工作区干净、Tag不存在、签名和混淆都启用。全部渠道构建成功后,脚本提交版本文件、创建annotated Tag,并用git push --atomic同时推送分支和Tag。

测试环境缺少签名证书时,可以显式生成未签名包:

.\build\Publish-Windows.ps1 `
  -Version 2.0.5.0 `
  -Sources PLUGIN `
  -SkipSign `
  -NonInteractive

-SkipReactor会同时跳过混淆。只要跳过签名或混淆,脚本就禁用Git Release和Push。

只检查工具和参数,不构建:

.\build\Publish-Windows.ps1 `
  -Version 2.0.5.0 `
  -Sources PLUGIN `
  -SkipSign `
  -SkipReactor `
  -PreflightOnly `
  -NonInteractive

实现概览

Windows端是Add-in Express COM插件。PowerPoint加载COM组件,插件用Office Interop操作PPT,WPF和WebView2负责页面。

PowerPoint
→ Add-in Express COM
→ AddinModule
→ Ribbon/事件
→ WPF或Custom Task Pane
→ WebView2
→ C# WebviewJsCallback
→ Office Interop

仓库:

aippt_windows_addin

1. 代码分层

PowerPoint入口

src/AiPPTAddin/
├── AddinModule.cs
├── AddinModule.Ribbon.cs
├── AddinModule.RibbonCallbak.cs
├── AddinModule.ContextMenuCallbacks.cs
└── Ribbon.xml

AddinModule负责插件初始化、PowerPoint事件、Ribbon和窗口管理。

Web窗口

src/AiPPTAddin/WebView/
src/AiPPTAddin/TaskPane/

两种容器:

容器用途
Add-in Express CTP停靠在PowerPoint里的侧边栏
WPF BrowserView独立浮动窗口

两者都使用WebView2加载共享前端页面。

Web和C#通信

Web页面
→ impl/cpp.ts
→ postMessageToClient(module.method)
→ WebView2消息
→ WebviewJsCallback
→ Office Interop

C#处理器在:

src/AiPPTAddin/WebviewJsCallback/

独立桌面程序

src/AiPPTAddin.App/

产物:

AiPPTAddin.App.exe

桌面程序能复用Web页面和下载逻辑,但不能代替PowerPoint内的选区、Slide和Shape事件。

2. 开发依赖

依赖用途
Windows 11开发和验证系统
Visual Studio编译解决方案和MFC插件
MSBuildRelease构建
.NET SDK 8运行Cake Frosting打包项目
Office/PowerPoint插件调试
Add-in ExpressCOM和CTP框架
WebView2 Runtime/SDKWeb页面容器
NSIS安装包
.NET Reactor混淆DLL和EXE
Windows SDK signtoolAuthenticode签名
WinRARyundong渠道SFX包

NSIS还需要项目的MFC UI插件:

NSISUIPlugin/MFCUIPlugin
→ nsMfcPlugin.dll
→ NSIS Plugins/x86-unicode

3. 源码构建

构建项目:

src/AiPPTAddin.sln

Cake入口:

build/src/Build.csproj

任务顺序:

Clean
→ Restore
→ SyncConfig
→ MSBuild Release
→ CollectInstallerResources
→ CopyFiles
→ ReactorFiles
→ CompressToZip

SyncConfig会改哪些文件

配置来源:

build/build.json

主要字段:

{
  "ProductVersion": "2.0.5.0",
  "InstallerSource": "PLUGIN",
  "ProductApiUrl": "https://sop-plugin.aippt.cn/api/"
}

构建时会同步到:

src/Directory.Build.props
src/AiPPTSilentInstaller/Program.cs
src/AiPPTAddin/Core/ProductInfos.cs
InstallSuccess/Program.cs
InstallFailed/Program.cs
UninstallSuccess/Program.cs

这些改动属于构建输入,不应把最后一个渠道值误提交到主分支。

4. 混淆和程序签名

.NET Reactor

任务:

ReactorFiles

处理范围:

  • AiPPTAddin.*.dll.exe
  • adxloader*.dll
  • 排除测试程序集;
  • 不混淆AiPPTAddin.Installer.exe
  • loader文件不做Reactor保护。

当前保护参数包括:

控制流混淆
调用隐藏
方法虚拟化
防调试
防篡改

necrobit和资源加密关闭,没有使用UPX、Themida或VMProtect一类传统PE压缩壳。

正式构建应检查:

dotNET_Reactor.Console.exe存在
许可证已激活
进程退出码为0

-SkipReactor只用于本地测试。跳过混淆的产物不应创建正式Tag。

Authenticode

签名封装:

build/common/SignUtils.cs

签名对象:

  • 混淆后的DLL和EXE;
  • InstallSuccess/InstallFailed/UninstallSuccess;
  • WebView2Checker;
  • AiPPTAddin.Installer.exeCommandLine.dll
  • adxregistrator.exepingo.exe
  • uninstall.exe;
  • 最终Setup.exe。

正式构建应检查:

signtool.exe存在
证书在CurrentUser或LocalMachine证书库
证书包含私钥
证书未过期
时间戳服务可访问

-SkipSign只生成测试包。未签名包不能进入正式发布流程。

5. NSIS安装包

打包项目:

build/setup/Build.csproj

模板:

NSISAPPSetup/App-Placeholder.nsi
NSISAPPSetup-soft360/App-Placeholder.nsi
NSISAPPSetup-box/App-Placeholder.nsi

模板选择:

渠道模板
soft360NSISAPPSetup-soft360
huajunyundongwinNSISAPPSetup-box
其他普通渠道NSISAPPSetup
yundongWinRAR SFX专用流程

普通包流程:

复制AiPPT-public到模板App目录
→ 生成安装和卸载文件清单
→ 替换App-Placeholder.nsi占位符
→ 生成App-Build.nsi
→ makensis
→ 生成Setup.exe
→ signtool签名Setup.exe

离线WebView如何进入Setup

输入目录:

src/AiPPTAddin/offline/

完整链路:

aippt-frontend-addin的dist/offline.zip
→ 人工清空并解压到src/AiPPTAddin/offline
→ 更新0sync.txt
→ AiPPTAddin.csproj以CopyToOutputDirectory复制offline/**
→ MSBuild输出到src/Publish/AiPPT-Release/offline
→ CopyFiles复制到src/Publish/AiPPT-public/offline
→ BuildSetup复制AiPPT-public到NSISAPPSetup*/App
→ 自动生成NSIS File清单
→ Setup.exe安装到$INSTDIR/offline

运行时:

$INSTDIR/offline
→ DirectorySync复制到LocalAppData的offline目录
→ WebView2加载offline/<page>.html

桌面程序默认加载offline/desktop.html,插件窗口按业务加载offline/<page>.html。启动后还会检查服务端的offline包版本;有更新时下载offline.zip并覆盖LocalAppData中的离线目录。

人工边界只有第一步:前端offline.zip同步到src/AiPPTAddin/offline/。后续MSBuild、Publish、NSIS和安装均由Windows打包链路自动完成。

当前缺失处理:

  • src/AiPPTAddin/offline/缺失时,MSBuild可能继续执行;
  • NSIS生成的文件列表或Payload不完整时,makensis失败;
  • 运行时页面缺失时记录错误并回退到offline/page-not-found.html

COM注册和运行文件

CollectInstallerResources除WebView2Checker外,还收集:

src/AiPPTAddin.Installer/bin/Release/AiPPTAddin.Installer.exe
src/AiPPTAddin.Installer/bin/Release/CommandLine.dll
build/runtime/adxregistrator.exe
build/runtime/files/pingo.exe

用途:

文件用途
AiPPTAddin.Installer.exe注册和卸载AiPPT COM Add-in
CommandLine.dllInstaller命令行参数解析
adxregistrator.exeAdd-in Express注册辅助程序
pingo.exe图片压缩

缺少任一文件时,构建直接失败。线上2.0.3.0包与修复后的测试包对比结果为:线上存在而测试包缺少的文件数为0。

6. uninstall.exe流程

三套安装模板分别维护预生成的卸载器:

NSISAPPSetup/uninstall.exe
NSISAPPSetup-soft360/uninstall.exe
NSISAPPSetup-box/uninstall.exe

普通包流程:

读取对应模板的uninstall.exe
→ 正式模式下签名并验证Authenticode
→ 复制到App Payload
→ makensis嵌入Setup.exe

yundong使用默认目录下的uninstall.exe

当前约束:

  • 修改卸载逻辑后要同步更新三份卸载器;
  • 正式模式要求签名验证通过;
  • -SkipSign只用于测试包;
  • 缺少卸载器时直接失败,不静默生成不完整安装包。

7. 22个渠道包

当前批量入口:

build/BuildAllSetup/Program.cs

渠道:

PLUGIN
mircrosoft
officetools
AiPPT
baoyin
ycj
jzbc
txyyb
qiuye
wepe
soft360
lenovo
ludashi
aikit
soft51ppt
baoySEM
rjjl
txrjgj
hlfh
yundong
huajun
yundongwin

大部分渠道的Payload相同,区别是InstallerSource。该值写入注册表,并随安装、启动、卸载和业务请求发送到服务端,用于渠道归因。

渠道例外:

渠道差异
baoySEM无独立SDK,仅InstallerSource=baoySEM归因
soft360专用NSIS模板和UI资源
lenovo保留x86/x64运行时目录
yundongWinRAR SFX、静默安装器和运行时特殊逻辑
huajunyundongwinbox模板和对应UI资源

8. yundong SFX

代码:

build/BuildYunDong/

依赖:

Rar.exe
SFXConfig.txt
AiPPTSilentInstaller.exe
check_dotnet_and_install.cmd
payload目录

流程:

准备payload
→ 签AiPPTSilentInstaller.exe
→ Rar.exe生成SFX
→ 签最终SFX EXE

Windows自带ZIP和Compress-Archive不能生成兼容的WinRAR SFX EXE。Windows构建环境可安装:

winget install --id RARLab.WinRAR --exact

正式发布还要确认WinRAR授权。

9. 产物检查

每个渠道应有一个文件:

AiPPT-Setup-<version>_<source>.exe

检查项:

  • 版本和渠道名正确;
  • 22个文件没有缺失或重复;
  • Setup.exe签名有效;
  • 安装后的uninstall.exe签名有效;
  • 内部业务EXE和DLL签名有效;
  • AiPPTAddin.Installer.exeCommandLine.dlladxregistrator.exefiles/pingo.exe存在;
  • Setup Payload包含offline/desktop.htmloffline/settings.htmloffline/0sync.txt
  • soft360boxyundong能走各自安装流程;
  • 构建失败时不保留半批产物;
  • 跳过签名或混淆时不提交版本、不打Tag。

单渠道烟测通过标准:

安装进程ExitCode=0
注册表LoadBehavior=3
PowerPoint COMAddIns中AiPPTAddin.Release的Connect=true

10. 安装后的主要位置

插件安装后会写入PowerPoint加载和AiPPT运行所需文件,并把版本、渠道等设置写入当前用户注册表。

主要注册表项:

HKCU\Software\AiPPT\AiPPT\AiPPTAddin.Client

字段:

InstallerSource
version

开始菜单目录按安装权限写入All Users或Current User:

Programs/AiPPT/
├── AiPPT插件.lnk
└── 卸载 AiPPT插件.lnk

安装前递归清理旧的Programs/AiPPT/目录,再创建新快捷方式,避免升级后残留历史名称或乱码文件。

11. 常见失败

错误检查
找不到MSBuildVisual Studio工作负载、vswhere、MSBuild路径
找不到nsMfcPluginMFC插件是否编译并复制到NSIS插件目录
Reactor返回101.NET Reactor未激活
找不到签名证书USB Key、证书库、私钥和Thumbprint
找不到uninstall.exe对应NSIS模板目录是否包含预生成卸载器
COM Add-in显示运行时加载错误检查Installer、CommandLine、adxregistrator、loader位数和注册表
开始菜单快捷方式乱码NSIS模板是否被Windows PowerShell按ANSI读取;模板必须显式按UTF-8读取和写入
找不到StdUtils.nsh/dllNSIS Includes和Plugins搜索目录
yundong找不到Rar.exeWinRAR未安装或路径未配置
22包数量不对BuildAllSetup渠道列表和中途失败日志

12. 相关代码

build/build.json
build/src/Program.cs
build/setup/Program.cs
build/BuildAllSetup/Program.cs
build/BuildYunDong/
build/common/SignUtils.cs
NSISAPPSetup*/App-Placeholder.nsi
src/AiPPTAddin/
src/AiPPTAddin.App/
src/webview2-checker/

相关文档: