简介:面向Inno Setup开发者的自定义安装界面资源包,主要解决默认安装向导外观单一、难以整合产品品牌的问题,涵盖欢迎页、许可协议、目录选择、进度与完成页等界面模块,适合需要为Windows客户端快速制作专业安装程序的开发者。压缩包共136个文件,以64个isl脚本、23个png界面图、19个psd设计源文件、11个dll运行库及exe辅助工具为主,整体约105.4MB,目录结构清晰。资源内置Inno Setup增强版封装,已包含对.NET Framework 4的封装支持,可直接复用;Code.iss为界面代码,version.h配置文件中可方便修改应用名称、公司、版本号等品牌信息。同时提供PSD源文件与PNG图片,temp目录下的界面图可自由替换,App目录下的程序文件按需覆盖后修改脚本即可完成整套安装界面定制。已有1744人学习下载,适合中高级Windows安装包开发者参考并二次开发。
1. inno setup自定义安装界面:先想清楚你要动的是配置还是脚本
交付 Windows 桌面软件的时候,安装包界面往往是最容易被低估的细节。inno setup自定义安装界面 这个需求,说穿了就是一句话:默认的 Inno Setup 向导能装完软件,但界面长得很“通用”,换成自家品牌和交互逻辑之后,客户的第一印象会立刻不一样。这个方向能解决三件事:品牌统一、选项引导、把“是否创建快捷方式”“选择安装模式”这类判断放进可视化的页面里。适合谁:有桌面产品要交付的开发者、要给客户做定制安装包的实施工程师、以及被采购方要求“安装包看起来正规些”的小团队。别急着找现成皮肤,先搞清楚 Inno Setup 提供的两层修改入口,后面就能按需改到位。
2. 零代码入口与脚本入口:两条修改路径怎么选
2.1 [Setup]与[Messages]段:换品牌名、按钮文字与欢迎页的最短路径
Inno Setup 的安装包简介文件(.iss)本身就是一大块配置。先看一段最常见的定制配置,这段工作不需要写一行 Pascal 脚本,只需要在打包前改配置项:
[Setup] ; 这些是安装包整体层面的定制项 AppName=HarborClient AppVersion=2.1.0 AppPublisher=示例科技 DefaultDirName={autopf}\HarborClient DisableWelcomePage=no ; modern 风格更扁平,classic 是传统分组样式 WizardStyle=modern SetupIconFile=assets\setup.ico WizardImageFile=assets\banner.bmp WizardSmallImageFile=assets\small.bmp [Messages] WelcomeLabel1=欢迎安装 HarborClient WelcomeLabel2=这个向导会帮你在本机部署 HarborClient 客户端。%n%n如果安装过旧版本,请先卸载再继续。 ButtonNext=下一步(&N) ButtonBack=上一步(&B) ButtonInstall=开始安装(&I) ButtonFinish=完成(&F)这段配置主要解决“想换品牌又不想碰代码”的场景。[Setup]段里,AppName与AppPublisher会显示在安装向导的标题栏和欢迎页上;WizardStyle决定整体风格,modern 偏扁平、classic 偏传统;SetupIconFile换的是安装包 exe 的文件图标;WizardImageFile与WizardSmallImageFile分别对应向导左侧的大图和左上角的小图,这两个文件走的是 BMP/JPG 位图路线,后面避坑章会专门讲透明图片在这里翻车的问题。
[Messages]段则负责把按钮文字全部换成自己的语言习惯,比如ButtonInstall是从“安装”页面点下去的按钮,ButtonFinish是完成后右下角的“完成”。注意行尾的(&N)是键盘快捷键写法,按 Alt+N 就能触发下一步,这个细节在客户现场演示时很加分。你能在这里做的定制,本质上是给 Inno Setup 的默认控件换文字和图片,不改控件结构。
2.2 InitializeWizard事件与WizardForm:接管向导窗体的第一行脚本
当需求超过“换名字换图”之后,就要进入脚本入口了。Inno Setup 的脚本语言是对象 Pascal 的一个子集,入口点就是事件函数。在向导窗体创建完成之后、显示给用户之前,InitializeWizard会被调用。这是修改窗体属性的最佳时机,因为此时所有页面控件都已经创建完毕,你改什么它都能立刻生效。
[Code] procedure InitializeWizard; begin // WizardForm 是整个安装向导的主窗体,所有子页面都在它上面 WizardForm.Color := clWindow; WizardForm.Caption := 'HarborClient 安装向导'; // 欢迎页顶部大标题,调整字号和颜色 WizardForm.WelcomeLabel1.Font.Size := 11; WizardForm.WelcomeLabel1.Font.Color := clNavy; // 下一步/上一步按钮宽度拉宽,避免文字被截断 WizardForm.NextButton.Width := ScaleX(120); WizardForm.BackButton.Width := ScaleX(120); end;这里最核心的东西是WizardForm这个全局变量。它代表整个安装向导窗体,下面挂着WelcomeLabel1、NextButton、BackButton、BitmapImage等一批现成控件。你在 [Setup] 段看到的所有文案,本质上都是给这些控件的默认值;脚本修改则可以做到更细:字体颜色、控件宽度、按钮间距、甚至是某个页面在向导中的显示顺序。
ScaleX和ScaleY是坐标换算函数。Inno Setup 内部以 96 DPI 为基准设计界面,当目标机器开了 125% 或 150% 缩放时,直接写固定像素会错位,套一层ScaleX才能保证在不同分辨率下表现一致。这条是新手最容易忽略的细节,也是后面避坑章要展开讲的主题。
2.3 选型判断:什么时候只在Messages改,什么时候必须上[Code]
把两条路摆在一起,判断标准其实很清晰。只换品牌信息、按钮文字、欢迎语、默认图标,[Setup]+[Messages]就够,半小时能交付。要插入新的输入页面、勾选框、单选按钮,或者在点击下一步时做环境检测,就必须上[Code],用InitializeWizard创建页面和控件,用事件函数处理逻辑。
还有一种情况要提前给读者打预防针:如果你期待的“自定义安装界面”是那种彻底重绘窗体、左侧导航栏、全自定义动画的现代安装器外观,Inno Setup 默认做不到。它能改的是窗体属性、控件状态与页面流程,但窗体骨架是固定的。做完全换肤需要借助 VCL 样式(EnableVclStyles)或干脆评估其他专用安装工具,这个边界提前知道能省下不少折腾时间。对这个项目来说,绝大多数“看起来更正规”的需求,脚本入口已经能覆盖。
3. 用TInputOptionWizardPage做一个带选项的自定义安装界面:完整脚本与参数说明
3.1 页面挂载点与控件布局:把自建页面插到安装步骤的哪里
先拆一个最常见的真实需求:安装时让用户选择“标准模式”还是“兼容模式”,同时决定要不要在桌面创建快捷方式。这需要 Inno Setup 的预设组件TInputOptionWizardPage来做一个单选页面,再用TNewCheckBox在页面下方追加一个复选框。
页面插在哪个位置,由CreateInputOptionPage的第一个参数决定。Inno Setup 内置页面有固定的 ID,比如wpWelcome(欢迎页)、wpSelectTasks(选择任务页)、wpReady(准备安装页)。把自建页面挂在wpSelectTasks之后,意味着用户先选任务、再选安装模式、最后看到准备安装页,这个顺序符合大多数商业软件的引导习惯。
布局上注意一个关键概念:TInputOptionWizardPage内部有一个Surface属性,它是页面上用来承载控件的容器。所有往自定义页面里追加的控件,Parent必须指向ModePage.Surface,而不是直接指向ModePage或WizardForm,否则控件会跑到窗口外或者干脆不显示。
3.2 完整可编译脚本:创建页面、读取勾选、应用结果
下面这段脚本是完整可编译的,建议直接抄到项目里再按需调整注释里的业务逻辑:
[Setup] AppName=HarborClient AppVersion=2.1.0 DefaultDirName={autopf}\HarborClient WizardStyle=modern [Code] var ModePage: TInputOptionWizardPage; DesktopIconBox: TNewCheckBox; InstallMode: Integer; procedure InitializeWizard; begin // 1. 创建安装模式选择页,插在“选择任务”页之后 ModePage := CreateInputOptionPage( wpSelectTasks, '选择安装模式', '根据目标机器情况决定安装行为', '标准模式适合绝大多数机器,兼容模式会关闭高开销特性。', True); ModePage.Add('标准模式(推荐)'); ModePage.Add('兼容模式(降低占用)'); ModePage.Values[0] := True; // 2. 在模式选择页下方追加一个复选框 DesktopIconBox := TNewCheckBox.Create(ModePage); DesktopIconBox.Parent := ModePage.Surface; DesktopIconBox.Left := ScaleX(8); DesktopIconBox.Top := ModePage.Values[0].Top + ScaleY(30); DesktopIconBox.Width := ScaleX(260); DesktopIconBox.Height := ScaleY(21); DesktopIconBox.Caption := '同时在桌面创建快捷方式'; DesktopIconBox.Checked := True; end; function NextButtonClick(CurPageID: Integer): Boolean; var FreeSpace: Int64; begin Result := True; if CurPageID = ModePage.ID then begin InstallMode := ModePage.SelectedValueIndex; // 标准模式下做一次磁盘空间检测,失败则阻止用户继续 if InstallMode = 0 then begin if GetSpaceOnDisk(ExpandConstant('{sd}'), FreeSpace) and (FreeSpace < 500 * 1024 * 1024) then begin MsgBox('磁盘剩余空间不足 500MB,请先清理。', mbError, MB_OK); Result := False; end; end; end; end; procedure CurStepChanged(CurStep: TSetupStep); begin // 文件复制完成之后,再创建桌面快捷方式 if CurStep = ssPostInstall then begin if DesktopIconBox.Checked then CreateShellShortcut( 'desktop', ExpandConstant('{autopf}\HarborClient\harbor.exe'), '', ExpandConstant('{autopf}\HarborClient'), 'HarborClient 客户端', ExpandConstant('{autopf}\HarborClient\harbor.exe'), 0); end; end;逐段说明一下脚本干了什么。CreateInputOptionPage的五个参数分别是:插入位置、页面标题、页面主描述、页面副描述、是否可选。最后一个参数传True时,页面上会有一个“取消勾选此页”之类的退出入口?实际含义是此页面是否允许用户通过取消勾选跳过,传True后用户仍可以正常进入下一步,传False则强制必须停留;这里有版本差异,常见做法是先传True,再在NextButtonClick里按业务校验拦截,可控性更强。
ModePage.Add每调用一次就增加一个单选按钮,返回值会被放入Values数组,运行时通过SelectedValueIndex拿到用户选中的索引。DesktopIconBox挂到Surface上之后,用Left、Top、Width、Height手动定位,坐标全部经过ScaleX/ScaleY处理,这是保证高分屏不翻车的前提。NextButtonClick是点击“下一步”时触发的事件函数,返回False会阻止页面前进,适合做环境检查;CurStepChanged监听安装步骤变化,ssPostInstall在文件复制完成后触发,此时创建快捷方式,目标文件一定已经落盘。
3.3 NextButtonClick与CurStepChanged:逻辑必须在正确的事件里执行
| 事件函数 | 触发时机 | 典型用途 |
|---|---|---|
| InitializeWizard | 向导窗体创建后、显示前 | 创建自定义页面、改控件属性、加载图片 |
| ShouldSkipPage | 每页显示前 | 按条件跳过某一步,比如兼容模式下跳过附加功能页 |
| NextButtonClick | 点击下一步时(还没真正切换页面) | 校验用户输入、环境检测,返回 False 中止流程 |
| CurStepChanged | 安装步骤状态变化时 | 文件复制后创建快捷方式、写注册表、启动服务 |
| InstallMode 读取点 | 点击下一步后 | 把用户选择存入全局变量,供后续步骤消费 |
这里最常见的误用,是把校验逻辑写进InitializeWizard。那个阶段用户还没做任何选择,页面数据是默认值,校验没有意义。正确做法是把“读取用户选择”放在NextButtonClick里,把“应用选择结果”放在CurStepChanged或安装后的Run条目里,两个事件各管一段,出问题也容易定位。
另一个容易踩的点是CreateShellShortcut的版本依赖。这个内置函数是较新版本 Inno Setup 才有的,如果你的环境还在用旧版编译器,编译会直接报Undefined identifier。备选方案是用[Tasks]段内置的桌面图标任务:在[Tasks]里定义desktopicon,用WizardForm.TasksList.Checked[索引]同步状态,这样旧版本也能跑。不过既然已经在新环境上推进,建议直接用内置函数,代码更短。
4. 自定义安装界面避坑:布局失效、图片变色与脚本报错怎么排查
4.1 背景色改完,页面上的文字像隐身了一样
现象:在InitializeWizard里把WizardForm.Color改成深色(比如clBlack)之后,欢迎页标题、正文文字全部看不见了。原因:Inno Setup 默认控件文字颜色是接近黑色的clWindowText,深色背景下黑字压黑底,不是控件消失,是文字和背景融在一起了。解决:改背景的同时,要把所有会出现在这个背景上的文字控件颜色一起改掉,典型的是WizardForm.WelcomeLabel1、WizardForm.WelcomeLabel2、WizardForm.PageNameLabel和WizardForm.PageDescriptionLabel。参考写法:
WizardForm.WelcomeLabel1.Font.Color := clWhite; WizardForm.WelcomeLabel2.Font.Color := clWhite;如果是自建页面上的TNewStaticText或TNewCheckBox,同样要设置Font.Color。记住一个原则:背景色变更不是单点操作,它牵动页面上所有控件的前景色,改之前先列出页面上会出现的全部控件清单。
4.2 高分屏下控件叠在一起:DPI缩放不是玄学
现象:在开发机(100% 缩放)上排版正常,到客户现场的一台 150% 缩放的笔记本上,自定义页面的复选框和单选按钮叠成一片。原因:Inno Setup 支持 DPI 感知,但前提是你在脚本里正确使用了ScaleX/ScaleY。直接写死Left := 8的控件,在高分屏下坐标不跟随缩放,视觉上必然错位。解决:所有手工坐标都走ScaleX/ScaleY转换,同时用现成控件的Top做相对定位。比如第 3 章里DesktopIconBox.Top := ModePage.Values[0].Top + ScaleY(30),就是拿单选按钮的实际位置做基准,再往下偏移 30 像素,而不是写死Top := 108。另外,Font.Size不需要缩放,Pascal 脚本里字号本来就按点阵计算,系统会自己处理。
4.3 PNG透明区域在安装包里变成黑色块
现象:给[Setup]段的WizardImageFile配了一张漂亮的透明 PNG,编译时直接报错,或者编译过了但安装界面里透明区域变成黑块。原因:WizardImageFile和WizardSmallImageFile这两个配置项从老版本开始就只认 BMP 与 JPG,不认 PNG。PNG 的 Alpha 通道在这里没有对应实现,强行使用会在编译阶段被拒绝。解决:走 [Setup] 段就用 BMP 或者 JPG,透明需求用 BMP 指定透明色处理;实在要用透明 PNG,改在InitializeWizard里用TBitmapImage加载,并由代码设置BackColor与显示位置。多数团队最终图省事,直接把左侧大图换成不透明 JPG,这是最稳妥的。
4.4 编译报Undefined identifier:先查声明顺序再查ISPP
现象:脚本写好后按编译,报Undefined identifier: 'ModePage'或者Unknown.原因分两类。一类是变量声明顺序问题:Pascal 脚本要求先声明后使用,var声明必须放在所有procedure之前,如果InitializeWizard里用的变量声明写在函数后面,编译器就会报未定义。另一类是版本问题:CreateShellShortcut这类新函数在老版本 Inno Setup 里不存在,编译通不过。解决:先把所有var声明集中放到[Code]顶部;再确认编译用的 iscc 是较新版本;如果你用了 ISPP 预处理(脚本里出现#include、{#emit}这类东西),检查预处理代码的变量作用域,ISPP 的变量和 Pascal 脚本变量是两套体系,不能混用。
4.5 卸载程序把你的自定义界面丢了
现象:安装界面改得很有品牌感,客户卸载时弹出来的却是默认蓝条向导,像安装包做了一半。原因:安装程序和卸载程序是两份独立的程序,安装时的InitializeWizard只作用于安装向导。卸载程序也有自己的WizardForm实例,它的事件函数是InitializeUninstallWizard,默认不修改的话自然回到原始状态。解决:在同一个 .iss 里补一个卸载事件入口:
procedure InitializeUninstallWizard; begin UninstallWizardForm.Color := clWindow; UninstallWizardForm.Caption := 'HarborClient 卸载向导'; end;UninstallWizardForm是卸载向导的全局窗体变量,Inno Setup 对它的操作方式和安装向导基本一致。如果你的自定义逻辑比较多,把这个入口单独抽成一个文件,安装和卸载两边各自调用,保证一致体验。
5. 把自定义界面沉淀成一套可复用的品牌模板
做到这里,你已经能按需求改文字、加页面、调布局、补事件了。接下来值得做的一件事:把第 2 章和第 3 章的代码抽成一个公共的branding.inc文件,在项目里用#include引入。这样每个新项目打包时只需要写业务逻辑,界面风格统一由一份文件维护。常见的目录结构是这样:
branding/ branding.inc // 所有界面定制脚本 assets/ setup.ico banner.bmp small.bmp project/ harbor.iss // 业务脚本,顶部 #include ..\branding\branding.inc在 CI 环境里,命令行编译的参数也可以把品牌变量传进去,比如用 iscc.exe 编译时指定/DBrandName=HarborClient,脚本里用{#emit}读取预处理变量,同一套模板就能产出不同客户版本的安装包。打包完成后我一般会做三件验证:在干净的虚拟机里跑一遍完整安装流程,检查每个页面在 100% 和 150% 缩放下的截图;跑一遍卸载流程,确认卸载事件里的自定义设置生效;再看一眼任务管理器,确认安装过程中没有残留进程。这套验证动作是我之前吃过亏换来的教训——UI 改动不影响安装逻辑,但安装逻辑出问题往往要从 UI 操作开始排查,界面和流程在安装器里本来就是一体。希望帮到你。
本文还有配套的精品资源,点击获取