Vue Office 插件Inno Setup脚本解释文档
一、脚本概述
1.1 核心定位
本脚本适配 Inno Setup 6.6.1,是通用Vue Office插件安装/卸载自动化脚本,基于官方原生属性与方法开发,无第三方依赖,可直接编译生成可执行安装包。核心用于Windows系统下Vue Office插件的分发部署,实现"安装/卸载"二合一自动化管控,包含环境检测、权限校验、日志记录、注册表配置、网络共享管理等全流程功能。
1.2 核心特性
- 二合一功能:集成安装/卸载选项,无需单独制作安装包和卸载包
- 环境自适应:自动识别Office 2013+版本、Windows LanmanServer文件共享服务状态,兼容Win10/11系统
- 安全合规:自动注册Excel可信目录,避免插件被Office安全策略拦截
- 日志溯源:实时输出操作日志(界面显示+本地文件保存至{app}\install.log),便于问题排查
- 权限管控:强制要求管理员权限运行,保障共享创建、注册表修改等关键操作生效
- 兼容适配:自动启用SMB1.0协议,兼容老旧Windows系统的文件共享逻辑
二、脚本完整结构拆解
2.1 宏定义区(全局常量)
作用
统一管理脚本中可复用的常量,降低后期维护成本,所有宏定义均可根据实际需求自定义修改。
| 宏名称 | 默认取值 | 核心作用 |
|---|---|---|
| MyAppName | "Vue Office 插件" | 应用名称,用于向导标题、完成页提示文本、程序组名称 |
| MyAppVersion | "1.1" | 插件版本号,标识当前插件版本 |
| MyAppPublisher | "开发者" | 发布者名称,用于安装程序属性展示、注册表备注 |
| MyAppURL | "https://example.com/" | 官方链接,用于安装程序的"支持/更新"链接跳转 |
| ShareName | "VueOfficeAddin" | 网络共享名称,插件目录的共享标识(Excel访问插件依赖此共享) |
| DefaultInstallDir | "{commonappdata}\VueOfficeAddin" | 默认安装路径,公共应用数据目录(无需用户手动选择) |
| FixedGUID | "{01254BD0-0689-43DE-9274-8D3368DF7ADF}" | Excel可信目录注册表唯一标识,避免与其他插件注册表项冲突(可通过Inno Setup的Tools→Generate GUID生成新值替换) |
2.2 Setup核心配置区([Setup])
作用
配置安装程序的基础行为、界面样式、权限要求、压缩规则等核心参数,所有配置项均为Inno Setup 6.6.1官方原生支持属性,无自定义扩展。
完整配置项说明
| 配置项 | 取值示例 | 核心作用 |
|---|---|---|
| AppId | {{01254BD0-0689-43DE-9274-8D3368DF7ADF}} | 应用唯一标识,与FixedGUID一致,关联系统的安装/卸载记录 |
| AppName | {#MyAppName} | 引用宏定义的应用名称,统一维护 |
| AppVersion | {#MyAppVersion} | 引用宏定义的版本号 |
| DefaultDirName | {#DefaultInstallDir} | 引用宏定义的安装路径,固定安装位置 |
| DisableDirPage | yes | 禁用目录选择页面,防止用户修改安装路径 |
| DisableProgramGroupPage | yes | 禁用程序组创建页面(插件无需创建开始菜单组) |
| DisableReadyMemo | yes | 禁用"准备安装"备注页,简化安装流程 |
| DisableReadyPage | yes | 禁用"准备安装"确认页,点击下一步直接执行安装 |
| DisableStartupPrompt | yes | 禁用安装程序启动时的提示框,直接进入向导 |
| OutputBaseFilename | VueOfficeAddin_Setup | 生成的安装包文件名(最终输出为VueOfficeAddin_Setup.exe) |
| Compression | lzma | 采用LZMA高压缩算法,减小安装包体积 |
| SolidCompression | yes | 启用固实压缩,进一步提升压缩率 |
| PrivilegesRequired | admin | 强制要求管理员权限运行(创建共享、修改注册表必须管理员权限) |
| PrivilegesRequiredOverridesAllowed | commandline | 允许通过命令行参数覆盖权限要求(仅用于调试) |
| Uninstallable | yes | 支持卸载功能,生成卸载程序 |
| WizardStyle | modern dynamic polar | 现代动态极地风格向导界面,优化用户体验 |
| AllowNoIcons | yes | 允许无快捷方式(插件无需桌面/开始菜单快捷方式) |
| AppPublisher | {#MyAppPublisher} | 引用宏定义的发布者名称 |
| AppPublisherURL | {#MyAppURL} | 引用宏定义的官方URL(安装程序属性中的"发布者链接") |
| AppSupportURL | {#MyAppURL} | 引用宏定义的官方URL(安装程序属性中的"支持链接") |
| AppUpdatesURL | {#MyAppURL} | 引用宏定义的官方URL(安装程序属性中的"更新链接") |
| DefaultGroupName | Vue Office 插件 | 默认程序组名称(虽禁用创建,但预留配置) |
| SetupIconFile | .\favicon.ico | 安装程序图标路径(需替换为自身图标文件路径) |
| WizardImageFile | .\images\WizardImage.bmp | 向导页面大图路径(需替换为自身图片路径) |
| WizardSmallImageFile | .\images\WizardSmallImage.bmp | 向导页面小图路径(需替换为自身图片路径) |
2.3 语言配置区([Languages])
Languages
Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
作用
配置安装程序的显示语言,此处仅启用简体中文,直接引用Inno Setup编译器内置的简体中文语言文件,无需额外配置。
2.4 文件复制配置区([Files])
Files
Source: "manifest.xml"; DestDir: "{app}"; Flags: ignoreversion
作用
定义需要复制到安装目录的插件核心文件,核心参数说明:
- Source: 源文件路径(此处为插件清单文件manifest.xml,需确保该文件与iss脚本同目录)
- DestDir: 目标目录({app} 等价于宏定义的DefaultInstallDir,即安装路径)
- Flags: ignoreversion: 忽略文件版本检查,强制复制(避免因版本号问题导致复制失败)
2.5 自定义代码区([Code])
作用
实现安装/卸载的核心业务逻辑、自定义界面控件、事件处理等,所有代码均基于Inno Setup原生Pascal脚本语法,无第三方扩展。
2.5.1 全局变量定义
var
UserChoice: Integer; // 用户选择:1=安装, 2=卸载
ProgressMemo: TMemo; // 日志显示控件
OperationCaption: String; // 当前操作名称(安装/卸载)
SelectPage: TWizardPage; // 自定义操作选择页
MarqueeProgressBar: TNewProgressBar; // 跑马灯进度条
ProgressLabel: TNewStaticText; // 进度条提示标签
2.5.2 核心函数说明
- LogStatus(Text: String):日志输出函数,同时将日志显示在界面Memo控件、写入本地install.log文件
- GetComputerNameStr():获取计算机名称,用于构建UNC共享路径(格式:\计算机名\共享名)
- RemoveNetworkShareIfExists(ShareNameParam: String):检查并删除已存在的同名网络共享,避免创建冲突
- CheckServerService():检查并确保LanmanServer(文件共享)服务正常运行,设置为自动启动
- EnableSMB1Protocol():启用SMB1.0协议,兼容老旧Windows系统的文件共享逻辑
- CreateNetworkShare(ShareNameParam, FolderPath: String):创建网络共享,包含目录创建、NTFS权限配置、共享发布
- GetOfficeMajorVersion():自动识别Office主版本(15.0/16.0/19.0),用于注册表路径匹配
- RegisterExcelSharedFolder(ShareNameParam: String):注册Excel可信目录,避免插件被Office安全策略拦截
- DeleteExcelRegistry():卸载时删除Excel可信目录对应的注册表项
- DeleteSharedFolder(FolderPath: String):卸载时删除安装目录及其中所有文件
- IsAdmin():检查当前用户是否拥有管理员权限,无权限则日志提示
2.5.3 核心事件说明
- InitializeWizard():初始化向导界面,创建安装/卸载选择页、跑马灯进度条、日志显示Memo控件
- CurPageChanged(CurPageID: Integer):页面切换事件,修改完成页的提示文本(安装完成/卸载完成)
- CurStepChanged(CurStep: TSetupStep):安装步骤变更事件,执行核心的安装/卸载逻辑
- CurUninstallStepChanged(CurUninstallStep: TUninstallStep):卸载步骤变更事件,清理共享、注册表、安装目录等残留
三、使用注意事项
- 文件替换:将manifest.xml替换为自身插件的清单文件,SetupIconFile/WizardImageFile等路径替换为实际文件路径
- GUID替换:通过Inno Setup的Tools→Generate GUID生成新GUID,替换脚本中的FixedGUID/AppId
- 权限要求:脚本必须以管理员身份运行,否则无法创建共享、修改注册表
- 环境适配:支持Windows 10/11、Office 2013+,SMB1.0协议可根据实际需求注释
- 日志排查:安装/卸载日志保存在{commonappdata}\VueOfficeAddin\install.log,可用于问题定位
- 编译方式:使用Inno Setup 6.6.1打开iss脚本,点击"Compile"按钮即可生成安装包
代码如下:
javascript
; ----------------------------
; 通用Vue Office插件安装脚本
; 适配 Inno Setup 6.6.1,仅使用官方原生属性/方法
; ----------------------------
; ======================== 宏定义区 ========================
; 应用名称(可自定义)
#define MyAppName "Vue Office 插件"
; 应用版本号
#define MyAppVersion "1.1"
; 发布者(可自定义)
#define MyAppPublisher "开发者"
; 官方URL(可自定义)
#define MyAppURL "https://example.com/"
; 网络共享名称(插件共享标识)
#define ShareName "VueOfficeAddin"
; 默认安装目录(公共应用数据目录)
#define DefaultInstallDir "{commonappdata}\VueOfficeAddin"
; 固定GUID(用于Excel注册表唯一标识,可自行生成新GUID替换)
#define FixedGUID "{01254BD0-0689-43DE-9274-8D3368DF7ADF}"
; ======================== Setup核心配置 ========================
[Setup]
; 应用唯一标识(需与上面的FixedGUID一致,或自行生成)
AppId={{01254BD0-0689-43DE-9274-8D3368DF7ADF}}
; 应用名称(引用宏定义)
AppName={#MyAppName}
; 应用版本
AppVersion={#MyAppVersion}
; 默认安装目录
DefaultDirName={#DefaultInstallDir}
; 禁用目录选择页面(固定安装路径)
DisableDirPage=yes
; 禁用程序组创建页面
DisableProgramGroupPage=yes
; 禁用准备安装备注页
DisableReadyMemo=yes
; 禁用准备安装确认页
DisableReadyPage=yes
; 禁用启动提示框
DisableStartupPrompt=yes
; 安装包输出文件名
OutputBaseFilename=VueOfficeAddin_Setup
; 压缩方式(LZMA高压缩)
Compression=lzma
; 固实压缩(提升压缩率)
SolidCompression=yes
; 要求管理员权限(创建共享/修改注册表需要)
PrivilegesRequired=admin
; 允许命令行覆盖权限要求
PrivilegesRequiredOverridesAllowed=commandline
; 允许卸载
Uninstallable=yes
; 向导样式(现代动态极地风格)
WizardStyle=modern dynamic polar
; 允许无快捷方式
AllowNoIcons=yes
; 发布者信息
AppPublisher={#MyAppPublisher}
; 发布者URL
AppPublisherURL={#MyAppURL}
; 支持URL
AppSupportURL={#MyAppURL}
; 更新URL
AppUpdatesURL={#MyAppURL}
; 默认程序组名称
DefaultGroupName=Vue Office 插件
; 许可证文件(如需添加,取消注释并替换路径)
; LicenseFile=.\license.txt
; 安装程序图标(需替换为自己的图标路径)
SetupIconFile=.\favicon.ico
; 向导大图(需替换为自己的图片路径)
WizardImageFile=.\images\WizardImage.bmp
; 向导小图(需替换为自己的图片路径)
WizardSmallImageFile=.\images\WizardSmallImage.bmp
; ======================== 语言配置 ========================
[Languages]
; 简体中文语言包(使用Inno Setup内置默认语言文件)
Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
; ======================== 文件复制配置 ========================
[Files]
; 复制插件清单文件到安装目录,忽略版本检查
Source: "manifest.xml"; DestDir: "{app}"; Flags: ignoreversion
; ======================== 自定义代码区 ========================
[Code]
//全局变量定义
var
UserChoice: Integer; // 用户选择:1=安装, 2=卸载
ProgressMemo: TMemo; // 日志显示控件
OperationCaption: String; // 当前操作名称(安装/卸载)
SelectPage: TWizardPage; // 自定义操作选择页
MarqueeProgressBar: TNewProgressBar; // 跑马灯进度条
ProgressLabel: TNewStaticText; // 进度条提示标签
// -----------------------------------------------------------------------------
// 功能:日志输出(同时显示在界面和本地日志文件)
// 参数:Text - 要输出的日志内容
//-----------------------------------------------------------------------------
procedure LogStatus(Text: String);
var
LogFile: String;
SL: TStringList;
begin
// 显示到界面Memo控件
if Assigned(ProgressMemo) then
begin
ProgressMemo.Lines.Add(Text);
ProgressMemo.SelStart := Length(ProgressMemo.Text); // 滚动到最后一行
ProgressMemo.Update; // 立即刷新界面
end;
// 写入本地日志文件
LogFile := ExpandConstant('{app}\install.log');
SL := TStringList.Create;
try
// 确保日志目录存在
if not DirExists(ExtractFileDir(LogFile)) then
CreateDir(ExtractFileDir(LogFile));
// 加载现有日志(追加模式)
if FileExists(LogFile) then
SL.LoadFromFile(LogFile);
SL.Add(Text); // 添加新日志行
SL.SaveToFile(LogFile); // 保存日志
finally
SL.Free; // 释放资源
end;
end;
//-----------------------------------------------------------------------------
//功能:获取计算机名称(用于构建UNC共享路径)
// 返回值:计算机名,默认localhost
//-----------------------------------------------------------------------------
function GetComputerNameStr(): String;
begin
Result := GetEnv('COMPUTERNAME');
if Result = '' then Result := 'localhost';
end;
//-----------------------------------------------------------------------------
//功能:检查并删除已存在的同名网络共享
// 参数:ShareNameParam - 共享名称
//-----------------------------------------------------------------------------
procedure RemoveNetworkShareIfExists(ShareNameParam: String);
var
ResultCode: Integer;
begin
LogStatus('🔍 检查并删除旧共享: ' + ShareNameParam);
// 方法1:使用net share命令删除
Exec(ExpandConstant('{cmd}'),
'/c net share ' + ShareNameParam + ' /DELETE /Y',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 方法2:使用PowerShell的SMB共享命令(兼容新版Windows)
Exec('powershell',
'-Command "if (Get-SmbShare -Name ''' + ShareNameParam + ''' -ErrorAction SilentlyContinue) { Remove-SmbShare -Name ''' + ShareNameParam + ''' -Force -ErrorAction SilentlyContinue }"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 日志输出结果
if ResultCode = 0 then
LogStatus('✅ 已删除旧共享: ' + ShareNameParam)
else
LogStatus('ℹ️ 无旧共享或删除失败(非错误): ' + ShareNameParam);
end;
//-----------------------------------------------------------------------------
// 功能:检查并确保LanmanServer(文件共享)服务正常运行
// 说明:文件共享依赖该服务,需确保启动且设为自动
//-----------------------------------------------------------------------------
procedure CheckServerService();
var
ResultCode: Integer;
begin
LogStatus('🔍 检查LanmanServer(文件共享)服务状态');
// 检查服务状态
Exec('powershell',
'-Command "$s=Get-Service -Name LanmanServer -ErrorAction SilentlyContinue; if($s){Write-Host $s.Status}else{Write-Host ''NotFound''}"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
if ResultCode = 0 then
begin
LogStatus('✅ LanmanServer 服务运行正常');
// 设置服务为自动启动
Exec('powershell',
'-Command "Set-Service -Name LanmanServer -StartupType Automatic"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end
else
begin
LogStatus('⚠️ LanmanServer 服务不可用,尝试启动...');
// 尝试启动服务
Exec('powershell',
'-Command "Start-Service -Name LanmanServer -ErrorAction SilentlyContinue"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
LogStatus('⚠️ 已尝试启动LanmanServer服务,返回码: ' + IntToStr(ResultCode));
end;
end;
//-----------------------------------------------------------------------------
//功能:启用SMB1.0协议(兼容老旧Windows系统)
// -----------------------------------------------------------------------------
procedure EnableSMB1Protocol();
var
ResultCode: Integer;
begin
LogStatus('🔍 检查并启用SMB1.0协议(兼容旧系统)');
Exec('powershell',
'-Command "Enable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -All -NoRestart -ErrorAction SilentlyContinue"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
LogStatus('ℹ️ SMB1.0协议启用操作完成,返回码: ' + IntToStr(ResultCode));
end;
//-----------------------------------------------------------------------------
// 功能:创建网络共享(含目录创建、权限配置、共享发布)
// 参数:
// ShareNameParam - 共享名称
// FolderPath - 要共享的文件夹路径
//-----------------------------------------------------------------------------
procedure CreateNetworkShare(ShareNameParam, FolderPath: String);
var
ResultCode: Integer;
Computer: String;
UNCPath: String;
begin
Computer := GetComputerNameStr();
UNCPath := '\\' + Computer + '\' + ShareNameParam;
// 确保安装目录存在
if not DirExists(FolderPath) then
begin
LogStatus('📁 创建安装目录: ' + FolderPath);
if not CreateDir(FolderPath) then
begin
LogStatus('❌ 创建目录失败: ' + FolderPath);
Exit;
end;
end;
// 设置文件夹NTFS权限(Everyone完全控制)
LogStatus('🔧 设置文件夹NTFS权限: ' + FolderPath);
Exec('powershell',
'-Command "$acl=Get-Acl ''' + FolderPath + '''; $rule=New-Object System.Security.AccessControl.FileSystemAccessRule(''Everyone'',''FullControl'',''ContainerInherit,ObjectInherit'',''None'',''Allow''); $acl.SetAccessRule($rule); Set-Acl ''' + FolderPath + ''' $acl"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 使用net share创建共享
LogStatus('🔧 使用net share创建共享: ' + ShareNameParam);
Exec(ExpandConstant('{cmd}'),
'/c net share ' + ShareNameParam + '=' + '"' + FolderPath + '" /GRANT:Everyone,FULL /Y',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 结果判断与日志输出
if ResultCode = 0 then
begin
LogStatus('✅ 共享创建成功: ' + UNCPath);
// 验证共享路径可访问
if FileExists(UNCPath + '\') then
LogStatus('✅ 共享路径可访问: ' + UNCPath)
else
LogStatus('⚠️ 共享创建成功,但访问验证失败(可能是网络延迟)');
end
else
begin
LogStatus('❌ net share创建共享失败,返回码: ' + IntToStr(ResultCode));
LogStatus('❌ 失败原因排查:');
LogStatus(' 1. 未以管理员身份运行安装程序');
LogStatus(' 2. 组策略/杀毒软件阻止了共享创建');
LogStatus(' 3. 文件夹路径含特殊字符或权限不足');
LogStatus(' 4. 手动执行命令排查:net share ' + ShareNameParam + '=' + '"' + FolderPath + '" /GRANT:Everyone,FULL');
end;
end;
//-----------------------------------------------------------------------------
// 功能:获取Office主版本号(用于注册表路径)
//返回值:Office版本号(16.0/19.0/15.0),默认16.0
// -----------------------------------------------------------------------------
function GetOfficeMajorVersion(): String;
begin
if RegKeyExists(HKCU, 'Software\Microsoft\Office\16.0\Excel') then
Result := '16.0'
else if RegKeyExists(HKCU, 'Software\Microsoft\Office\19.0\Excel') then
Result := '19.0'
else if RegKeyExists(HKCU, 'Software\Microsoft\Office\15.0\Excel') then
Result := '15.0'
else
Result := '16.0';
end;
// -----------------------------------------------------------------------------
//功能:注册Excel可信目录(避免插件被Excel拦截)
// 参数:ShareNameParam - 共享名称
// -----------------------------------------------------------------------------
procedure RegisterExcelSharedFolder(ShareNameParam: String);
var
UNCPath, RegPath: String;
ResultCode: Integer;
OfficeVersion: String;
begin
OfficeVersion := GetOfficeMajorVersion();
UNCPath := '\\' + GetComputerNameStr() + '\' + ShareNameParam;
RegPath := 'HKCU\Software\Microsoft\Office\' + OfficeVersion + '\WEF\TrustedCatalogs\{#FixedGUID}';
LogStatus('🔧 注册Excel可信目录: ' + UNCPath);
// 创建注册表项
Exec(ExpandConstant('{sys}\reg.exe'), 'add "' + RegPath + '" /f', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 设置注册表值(Flags:启用标识)
Exec(ExpandConstant('{sys}\reg.exe'),
'add "' + RegPath + '" /v "Flags" /t REG_DWORD /d 1 /f',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 设置ID(唯一标识)
Exec(ExpandConstant('{sys}\reg.exe'),
'add "' + RegPath + '" /v "Id" /t REG_SZ /d "{#FixedGUID}" /f',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 设置可信目录URL
Exec(ExpandConstant('{sys}\reg.exe'),
'add "' + RegPath + '" /v "Url" /t REG_SZ /d "' + UNCPath + '" /f',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 设置描述信息
Exec(ExpandConstant('{sys}\reg.exe'),
'add "' + RegPath + '" /v "Description" /t REG_SZ /d "VueOffice插件" /f',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
// 验证注册表项是否创建成功
if RegKeyExists(HKCU, RegPath) then
LogStatus('✅ Excel可信目录注册成功: ' + UNCPath)
else
LogStatus('❌ Excel可信目录注册失败(注册表项未创建)');
end;
//-----------------------------------------------------------------------------
// 功能:卸载时删除Excel可信目录注册表项
// -----------------------------------------------------------------------------
procedure DeleteExcelRegistry();
var
ResultCode: Integer;
RegPath: String;
OfficeVersion: String;
begin
OfficeVersion := GetOfficeMajorVersion();
RegPath := 'HKCU\Software\Microsoft\Office\' + OfficeVersion + '\WEF\TrustedCatalogs\{#FixedGUID}';
LogStatus('🔧 删除Excel可信目录注册表');
// 注册表项存在则删除
if RegKeyExists(HKCU, RegPath) then
begin
Exec(ExpandConstant('{sys}\reg.exe'), 'delete "' + RegPath + '" /f', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
if ResultCode = 0 then
LogStatus('✅ Excel注册表项删除成功')
else
LogStatus('⚠️ Excel注册表项删除失败(可能无相关项),返回码: ' + IntToStr(ResultCode));
end
else
begin
LogStatus('ℹ️ 无本插件注册表项,无需删除');
end;
end;
// -----------------------------------------------------------------------------
//功能:卸载时删除安装文件夹
//参数:FolderPath - 要删除的文件夹路径
//-----------------------------------------------------------------------------
procedure DeleteSharedFolder(FolderPath: String);
begin
LogStatus('🔧 删除安装文件夹: ' + FolderPath);
if DirExists(FolderPath) then
begin
// 删除文件夹内所有文件
DelTree(FolderPath + '\*.*', True, True, True);
// 删除空文件夹
if DelTree(FolderPath, True, True, True) then
LogStatus('✅ 安装文件夹删除成功: ' + FolderPath)
else
LogStatus('⚠️ 安装文件夹删除失败(可能被占用): ' + FolderPath);
end
else
begin
LogStatus('ℹ️ 安装文件夹不存在,无需删除');
end;
end;
//-----------------------------------------------------------------------------
//功能:更新操作选择页的下一步按钮文字
//-----------------------------------------------------------------------------
procedure UpdateFirstPageButtonText();
begin
if WizardForm.CurPageID = SelectPage.ID then
begin
WizardForm.NextButton.Visible := True;
if UserChoice = 1 then
WizardForm.NextButton.Caption := '开始安装'
else
WizardForm.NextButton.Caption := '开始卸载';
end;
end;
//-----------------------------------------------------------------------------
// 事件:安装单选按钮点击事件
// -----------------------------------------------------------------------------
procedure InstallRadioClick(Sender: TObject);
begin
UserChoice := 1;
OperationCaption := '安装';
UpdateFirstPageButtonText();
WizardForm.Caption := '{#MyAppName} 安装程序';
end;
// -----------------------------------------------------------------------------
//事件:卸载单选按钮点击事件
// -----------------------------------------------------------------------------
procedure UninstallRadioClick(Sender: TObject);
begin
UserChoice := 2;
OperationCaption := '卸载';
UpdateFirstPageButtonText();
WizardForm.Caption := '{#MyAppName} 卸载程序';
end;
//-----------------------------------------------------------------------------
//事件:初始化向导界面(创建自定义控件)
//-----------------------------------------------------------------------------
procedure InitializeWizard();
var
InstallRadio, UninstallRadio: TRadioButton;
begin
WizardForm.Caption := '{#MyAppName} 安装程序';
// 1. 创建操作选择页(安装/卸载选择)
SelectPage := CreateCustomPage(wpWelcome, '操作选择', '请选择要执行的操作');
// 创建安装单选按钮
InstallRadio := TRadioButton.Create(SelectPage);
InstallRadio.Parent := SelectPage.Surface;
InstallRadio.Top := 10;
InstallRadio.Left := 10;
InstallRadio.Width := 400;
InstallRadio.Caption := '安装 Vue Office 插件';
InstallRadio.Checked := True;
InstallRadio.OnClick := @InstallRadioClick;
// 创建卸载单选按钮
UninstallRadio := TRadioButton.Create(SelectPage);
UninstallRadio.Parent := SelectPage.Surface;
UninstallRadio.Top := 40;
UninstallRadio.Left := 10;
UninstallRadio.Width := 300;
UninstallRadio.Caption := '卸载 Vue Office 插件';
UninstallRadio.OnClick := @UninstallRadioClick;
// 默认选择安装
UserChoice := 1;
OperationCaption := '安装';
// 2. 创建跑马灯进度条(安装/卸载过程显示)
MarqueeProgressBar := TNewProgressBar.Create(WizardForm);
MarqueeProgressBar.Parent := WizardForm.InstallingPage;
MarqueeProgressBar.Left := 0;
MarqueeProgressBar.Top := ScaleY(20);
MarqueeProgressBar.Width := WizardForm.InstallingPage.ClientWidth;
MarqueeProgressBar.Height := ScaleY(20);
MarqueeProgressBar.Style := npbstMarquee; // 跑马灯样式
MarqueeProgressBar.Visible := True;
// 创建进度条提示标签
ProgressLabel := TNewStaticText.Create(WizardForm);
ProgressLabel.Parent := WizardForm.InstallingPage;
ProgressLabel.Left := ScaleX(0);
ProgressLabel.Top := MarqueeProgressBar.Top - ScaleY(20);
ProgressLabel.Caption := '处理中,请稍候...';
ProgressLabel.Visible := True;
// 3. 创建日志显示Memo控件(实时显示操作日志)
ProgressMemo := TMemo.Create(WizardForm);
ProgressMemo.Parent := WizardForm.InstallingPage;
ProgressMemo.Left := 0;
ProgressMemo.Top := MarqueeProgressBar.Top + MarqueeProgressBar.Height + ScaleY(5);
ProgressMemo.Width := WizardForm.InstallingPage.ClientWidth;
ProgressMemo.Height := WizardForm.InstallingPage.ClientHeight - ProgressMemo.Top;
ProgressMemo.ScrollBars := ssVertical; // 垂直滚动条
ProgressMemo.ReadOnly := True; // 只读
ProgressMemo.Font.Name := 'Consolas'; // 等宽字体,便于日志阅读
ProgressMemo.Font.Size := 9;
// 隐藏原有默认进度条
WizardForm.ProgressGauge.Visible := False;
// 初始化选择页按钮文字
UpdateFirstPageButtonText();
end;
//-----------------------------------------------------------------------------
//事件:页面切换事件(修改完成页文本)
//参数:CurPageID - 当前页面ID
//-----------------------------------------------------------------------------
procedure CurPageChanged(CurPageID: Integer);
begin
// 处理完成页
if CurPageID = wpFinished then
begin
// 根据用户选择修改完成页文本
if UserChoice = 1 then
begin
WizardForm.FinishedHeadingLabel.Caption := '安装完成';
WizardForm.FinishedLabel.Caption := '{#MyAppName} 已成功安装!';
end
else
begin
WizardForm.FinishedHeadingLabel.Caption := '卸载完成';
WizardForm.FinishedLabel.Caption := '{#MyAppName} 已成功卸载!';
end;
end
end;
//-----------------------------------------------------------------------------
// 功能:检查当前用户是否拥有管理员权限
//返回值:True=有管理员权限,False=无
//-----------------------------------------------------------------------------
function IsAdmin: Boolean;
var
ResultCode: Integer;
begin
Exec('powershell',
'-Command "([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)"',
'', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Result := (ResultCode = 0);
// 日志输出权限检查结果
if Result then
LogStatus('✅ 当前用户拥有管理员权限')
else
LogStatus('❌ 当前用户无管理员权限,' + OperationCaption + '可能失败!');
end;
//-----------------------------------------------------------------------------
// 事件:安装步骤变更事件(执行核心安装/卸载逻辑)
//参数:CurStep - 当前安装步骤
// -----------------------------------------------------------------------------
procedure CurStepChanged(CurStep: TSetupStep);
var
AppFolder: String;
LocalShareName: String;
begin
AppFolder := ExpandConstant('{app}');
LocalShareName := '{#ShareName}';
// 安装后步骤(执行核心逻辑)
if CurStep = ssPostInstall then
begin
LogStatus('=====================================');
LogStatus('开始执行' + OperationCaption + '操作: ');
LogStatus('=====================================');
// 检查管理员权限
IsAdmin();
// 安装逻辑
if UserChoice = 1 then
begin
LogStatus('🔹 开始环境检测...');
EnableSMB1Protocol(); // 启用SMB1.0
CheckServerService(); // 检查文件共享服务
LogStatus('🔹 环境检测完成');
LogStatus('🔹 开始安装流程...');
RemoveNetworkShareIfExists(LocalShareName); // 删除旧共享
CreateNetworkShare(LocalShareName, AppFolder); // 创建新共享
RegisterExcelSharedFolder(LocalShareName); // 注册Excel可信目录
LogStatus('=====================================');
LogStatus('✅ 安装流程执行完成');
LogStatus('=====================================');
end
// 卸载逻辑
else if UserChoice = 2 then
begin
LogStatus('🔹 开始卸载流程...');
RemoveNetworkShareIfExists(LocalShareName); // 删除共享
DeleteExcelRegistry(); // 删除注册表项
DeleteSharedFolder(AppFolder); // 删除安装目录
LogStatus('=====================================');
LogStatus('✅ 卸载流程执行完成');
LogStatus('=====================================');
end;
end;
end;
//-----------------------------------------------------------------------------
// 事件:卸载步骤变更事件(卸载后清理残留)
//参数:CurUninstallStep - 当前卸载步骤
//-----------------------------------------------------------------------------
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
LocalShareName: String;
begin
LocalShareName := '{#ShareName}';
// 卸载后步骤(清理残留)
if CurUninstallStep = usPostUninstall then
begin
LogStatus('🔹 卸载后清理残留...');
RemoveNetworkShareIfExists(LocalShareName); // 清理共享
DeleteExcelRegistry(); // 清理注册表
DeleteSharedFolder(ExpandConstant('{app}')); // 清理安装目录
end;
end;