Qt程序打包指南:用Inno Setup配置安装包与exe文件简介信息
接了个Qt的小工具项目编译倒是顺利双击exe也能跑可一到交付环节就有点尴尬用户拿到的就是一个光秃秃的exe加一堆DLL文件夹右键看属性文件说明、版本、版权全是空的安装体验也谈不上。后来我把交付规范改成Inno Setup打包顺便把“简介”这件事彻底梳理了一遍——这里说的简介不只是安装包属性里那一两行字还涉及安装界面上的说明文字、装完后程序exe自带的文件描述。这篇文章就把这套操作完整写出来从Inno Setup脚本里怎么配置到Qt那边怎么提前埋好字段再到实际编译打包中会踩的坑一次讲清楚。1. 先把“简介”拆清楚三层信息分别写在哪1.1 用户能看到的“简介”其实有三个位置很多人以为“给软件加简介”就是在安装包上写一段话实际操作后会发现不够。站在用户视角信息会出现在三个地方安装包exe本身、安装过程中的界面、安装完成后程序exe的属性页。安装包exe本身就是用户下载下来的Setup.exe右键查看属性在“详细信息”标签页里能看到文件说明、产品名称、版权、原始文件名等内容。这部分由Inno Setup的VersionInfo系列字段控制。安装过程中的界面指的是Inno Setup向导里的欢迎页、许可协议页、信息介绍页。用户可以在开始安装前看到一段关于软件的介绍文字这部分可以通过InfoBeforeFile或者自定义页面来实现。安装完成后的程序exe属性是用户去安装目录里右键主程序exe看到的那个“文件描述”。很多人在Inno Setup里折腾半天发现安装包属性很好看了但装出来的exe属性还是空的——因为那部分信息不是Inno Setup管而是Qt编译时写在exe资源里的。这层最容易漏后面会专门讲。1.2 为什么是Inno SetupQt生态里官方有Qt Installer Framework很多项目也用NSIS或者Advanced Installer但我最终固定用Inno Setup是有原因的。Qt Installer Framework走的是组件化管理适合大型软件、多组件选择的发布场景但对一个几十MB的小工具来说重量级偏大脚本学习的曲线也比较陡。NSIS虽然灵活但需要搓一堆底层代码出错排查看不太直观。Inno Setup处于一个中间位置脚本是类Pascal语法结构清晰对中文本地化支持很好而且它默认编译出来的安装包就是Unicode中文简介不会出现乱码这一类基础问题。还有一个实际考量很多Qt程序发布时会带一堆运行库和插件目录Inno Setup处理这种目录复制比较简单一条recursesubdirs就能把DLL和子文件夹原封不动搬进去不太容易出现漏文件。1.3 一个典型的Qt交付场景下面所有操作都基于这样一个假设场景我用Qt编写了一个图像处理Demo工具使用MSVC编译器开发机是64位Windows环境用windeployqt部署过运行依赖编译输出目录里除了exe以外还有platforms、styles、translations、imageformats等Qt插件目录。如果要发布我需要把这些全部打进去并让安装包看起来信息完整。如果你用的不是这个组合比如MinGW编译或者Qt版本不同原理不变只是依赖目录会略有差异。2. 脚本里配置简介从[Setup]段到VersionInfo字段2.1 [Setup]段的文本字段怎么填Inno Setup的脚本文件以[Setup]段开始。和简介直接相关的字段是AppName、AppVerName、AppVersion、AppPublisher、AppComments这几个。AppName和AppVerName的区别很多人搞不明白AppName是纯名称AppVerName是带版本号的完整显示名如果没写AppVerName默认就用AppName加上AppVersion拼出来。控制面板的“程序和功能”列表里显示的就是AppVerName。AppComments这个字段会出现在控制面板里是程序管理列表中的“备注”列Windows 10以上的“应用和功能”界面虽然不直接显示这一列但系统内部会读它。所以我一般会在这里写一句完整的功能定位比如“这是一款用于批量图像处理的桌面工具”。[Setup]段里还有一组VersionInfo系列字段这是真正控制安装包exe“详细信息”属性页的内容[Setup] AppName某图像处理Demo AppVersion1.2.0 AppVerName某图像处理Demo V1.2.0 AppPublisher某实验室 AppComments这是一款用于图像批量处理的桌面工具 VersionInfoVersion1.2.0.0 VersionInfoDescription某图像处理Demo安装程序 VersionInfoCopyrightCopyright (C) 2025 某实验室 VersionInfoProductName某图像处理Demo VersionInfoProductVersion1.2.0.0VersionInfoDescription对应属性页里的“文件说明”VersionInfoProductName对应“产品名称”VersionInfoVersion对应“文件版本”VersionInfoCopyright对应“版权”。这一组字段写完重新编译安装包右键属性立刻能看到完整信息。2.2 中文字段正常显示的前提条件Inno Setup 6版本的编译器默认生成Unicode安装包所以上面这些中文字段保存正常就不会乱码。有一点容易忽略.iss脚本文件的编码。如果在别处编辑脚本再粘贴中文进去最好在Inno Setup编译器里重新保存一次或者在编辑器里选择带BOM的UTF-8编码。Inno Setup编译器对UTF-8的识别在无BOM场景下偶尔会有兼容问题老规矩直接存成带签名的UTF-8最稳。另外一个跟中文强相关的是[Languages]段官方中文语言文件和编译器一起安装时路径通常是C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl。这个文件缺失或者路径填错编译时会直接报错“Reading file ... ChineseSimplified.isl 失败”那就是语言文件路径没对准。2.3 装出来的主程序exe为什么没有文件描述这是打包流程里最容易遗漏的一层。Inno Setup的VersionInfo字段只作用于安装包exe不会替你修改被安装的Qt主程序exe的资源信息。用户装完后去安装目录看某图像处理Demo.exe的属性如果没有事先处理文件说明、产品名称全是空。正确做法是在Qt工程里提前写好。Qt的qmake工程中.pro文件里只需要加几行VERSION 1.2.0.0 QMAKE_TARGET_PRODUCT 某图像处理Demo QMAKE_TARGET_DESCRIPTION 这是一款用于图像批量处理的桌面工具 QMAKE_TARGET_COPYRIGHT Copyright (C) 2025 某实验室 QMAKE_TARGET_COMPANY 某实验室重新qmake并编译后生成的exe就自带这些资源字段。如果你用的是CMake也可以设置VERSION属性和WIN32_EXECUTABLE相关的资源信息。这一步最好在Inno Setup打包前完成因为Inno Setup脚本里复制的就是这个带完整信息的exe。还有一种应急方案如果工程已经编完、线上版本不能重编也可以用资源编辑器直接改exe的资源信息。但这种方式一是要额外装工具二是每次发布都手动改、流程容易漏不如在工程源头解决干净。3. 把简介写进安装界面从InfoBeforeFile到自定义页面3.1 用InfoBeforeFile做安装过程中的功能简介页InfoBeforeFile是Inno Setup里很实用的一个字段它指向一个文本或RTF文件在用户选择安装目录之前显示。很多人把它当许可协议用其实它很适合做“软件简介页”。我习惯把这个文件做成INTRO.txt内容就是软件的定位和核心功能说明某图像处理Demo 版本 1.2.0 本软件是面向图像批量处理的桌面工具支持以下功能 1. 批量导入图片并进行格式转换 2. 统一调整分辨率、压缩质量 3. 移除图片元数据、自定义文件命名规则 4. 基于Qt平台开发支持Windows 10及以上系统 安装前请确认系统已安装可用的图形驱动。文件名INTRO.txt注意保存为UTF-8编码Inno Setup读取时按Unicode处理中文不会乱。如果希望简介页的排版更丰富一点也可以用RTF文件支持简单的字体颜色和列表效果。3.2 自定义页面再加一层专属简介展示有些项目连InfoBeforeFile都嫌不够正式想在安装界面加一个独立的“功能介绍”页这时就需要写一点Pascal脚本。比如在欢迎页之后插入一个页面显示标题和几段描述文字[Code] var IntroPage: TOutputMsgPage; procedure InitializeWizard; begin IntroPage : CreateOutputMsgPage(wpWelcome, 软件简介, 了解本软件的主要功能, 某图像处理Demo 是一款面向图像批量处理的桌面工具。 支持图片格式转换、批量压缩、元数据清理和自定义命名。 本软件基于Qt开发运行环境为Windows 10及以上版本。); end;CreateOutputMsgPage的第一个参数指定页面插入位置我用wpWelcome表示插在欢迎页之后。第二个参数是页面标题第三个是页面副标题第四个是正文。这里用字符串拼接的好处是长文本不会写在一行里可读性好。这样改完用户点下一步时就能看到一个完整的软件简介页比默认的信息页更像正规商业软件的安装流程。3.3 安装包的入口视觉也不能拖后腿简介文字做得再完整安装包图标还是默认图标整体观感会很业余。建议准备一个installer.ico在[Setup]段里用SetupIconFileinstaller.ico指定如果主程序exe有版权图标编译好的exe图标会自动带到快捷方式上不需要额外配置。还可以考虑把WizardStyle设为modernInno Setup 6的现代向导风格会让安装界面视觉上更干净、更像原生Windows应用。这些细节配合简介信息整体交付质感会明显提升。4. Qt依赖怎么带理想简介之外的实际打包难点4.1 windeployqt部署后的关键目录很多Qt程序打包后打不开九成是依赖没复制全。windeployqt会把Qt相关的DLL和插件部署到程序目录这些目录在Inno Setup里必须原样打进去。至少这几个目录要覆盖platforms至少包含qwindows.dll这是Qt在Windows上跑起来的平台插件没有它程序直接报“could not find or load the Qt platform plugin windows”。styles包含窗口样式相关的插件缺了可能导致界面渲染异常。translationsQt内置的翻译文件如果程序里加载了Qt基础控件的翻译这个目录不能漏。imageformats如果程序需要加载非默认格式的图片比如WebP这个目录要带上。tls新版Qt带OpenSSL相关的目录程序有网络请求时必须带否则TLS握手可能出错。这些目录还有一个特点名字固定、里面文件可能随Qt版本变化。在Inno Setup脚本里不能用只列单文件的方式要按目录递归复制。4.2 [Files]段的两种复制方式最简单的写法是显式列出windeployqt输出的每个目录[Files] Source: D:\build\release\某图像处理Demo.exe; DestDir: {app}; Flags: ignoreversion Source: D:\build\release\platforms\*; DestDir: {app}\platforms; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\styles\*; DestDir: {app}\styles; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\translations\*; DestDir: {app}\translations; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\imageformats\*; DestDir: {app}\imageformats; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\*.dll; DestDir: {app}; Flags: recursesubdirs ignoreversion这里注意根目录的DLL用Source: ...\*.dllDestDir直接落到{app}根目录插件目录要用DestDir: {app}\platforms这种带相对路径的目标目录加recursesubdirs createallsubdirs保证子目录结构不丢失。如果工程还引入了第三方运行库比如Qt里常见的QScintilla组件、Halcon图像库之类的DLL建议在release目录下再确认一遍有没有遗漏的非Qt依赖一并放进打包源码列表里。4.3 快捷方式的工作目录Qt程序运行时要加载相对路径的资源文件时很可能按“当前工作目录”来找。直接从文件管理器双击exe时当前目录就是exe所在目录没问题但从开始菜单快捷方式启动时如果不指定工作目录系统会把它设成其他值导致程序找不到config.ini或者images/之类的资源文件。Inno Setup的[Icons]段里有一个专门字段解决这个问题[Icons] Name: {group}\某图像处理Demo; Filename: {app}\某图像处理Demo.exe; WorkingDir: {app} Name: {autodesktop}\某图像处理Demo; Filename: {app}\某图像处理Demo.exe; WorkingDir: {app}; Tasks: desktopicon把WorkingDir设为{app}程序无论从开始菜单还是桌面快捷方式启动当前工作目录都能对准安装目录。这个问题很多人排查半天其实根源就一行字段。5. 完整脚本示例与编译实操5.1 一份可直接改用的脚本把前面几个部分的内容拼起来一份可用的Inno Setup脚本大致长这样#define MyAppName 某图像处理Demo #define MyAppVersion 1.2.0 #define MyAppPublisher 某实验室 #define MyAppExeName 某图像处理Demo.exe [Setup] AppId{{8F7E0A11-2E35-4C6B-9B0C-FA5C12345678} AppName{#MyAppName} AppVersion{#MyAppVersion} AppVerName{#MyAppName} V{#MyAppVersion} AppPublisher{#MyAppPublisher} AppComments这是一款用于图像批量处理的桌面工具 VersionInfoVersion{#MyAppVersion}.0 VersionInfoDescription{#MyAppName}安装程序 VersionInfoCopyrightCopyright (C) 2025 {#MyAppPublisher} VersionInfoProductName{#MyAppName} VersionInfoProductVersion{#MyAppVersion}.0 DefaultDirName{autopf}\{#MyAppName} DefaultGroupName{#MyAppName} OutputBaseFilename{#MyAppName}_Setup_V{#MyAppVersion} Compressionlzma2/ultra64 SolidCompressionyes WizardStylemodern SetupIconFileinstaller.ico UninstallDisplayIcon{app}\{#MyAppExeName} ArchitecturesAllowedx64compatible ArchitecturesInstallIn64BitModex64compatible LicenseFileLICENSE.txt InfoBeforeFileINTRO.txt [Languages] Name: chinesesimplified; MessagesFile: C:\Program Files (x86)\Inno Setup 6\Languages\ChineseSimplified.isl [Tasks] Name: desktopicon; Description: 创建桌面快捷方式; Flags: unchecked [Files] Source: D:\build\release\{#MyAppExeName}; DestDir: {app}; Flags: ignoreversion Source: D:\build\release\*.dll; DestDir: {app}; Flags: recursesubdirs ignoreversion Source: D:\build\release\platforms\*; DestDir: {app}\platforms; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\styles\*; DestDir: {app}\styles; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\translations\*; DestDir: {app}\translations; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\imageformats\*; DestDir: {app}\imageformats; Flags: recursesubdirs createallsubdirs ignoreversion Source: D:\build\release\config.ini; DestDir: {app}; Flags: ignoreversion [Icons] Name: {group}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; WorkingDir: {app} Name: {autodesktop}\{#MyAppName}; Filename: {app}\{#MyAppExeName}; WorkingDir: {app}; Tasks: desktopicon [Run] Filename: {app}\{#MyAppExeName}; Description: 立即运行{#MyAppName}; Flags: nowait postinstall skipifsilentAppId的GUID不要照抄在Inno Setup编译器里通过菜单生成一个新的否则每台机器上生成的安装包会共用同一个识别ID升级覆盖时会互相干扰。5.2 编译和验证流程在Inno Setup编译器里打开脚本确认编辑器里中文显示正常直接按F9编译即可。编译后建议养成一套固定验证流程先在安装包exe上右键属性能看到“文件说明”“产品名称”“版权”等内容。再跑一遍安装流程重点检查安装界面是否出现INTRO.txt里的内容、完成页能否正常启动程序。最后去安装目录右键主程序exe看“详细信息”确认里面也有文件描述和产品名称。这几处都对上说明三层简介都到位了。5.3 字段与系统显示位置的对照为了方便以后快速定位问题我把常用字段和系统显示位置整理成下面这个对应关系脚本字段系统显示位置说明AppVerName控制面板 / 应用和功能安装后显示主名称AppComments控制面板备注 / 程序属性软件功能备注VersionInfoDescription安装包右键 - 详细信息 - 文件说明安装包自身的简介VersionInfoProductName安装包右键 - 详细信息 - 产品名称安装包产品名VersionInfoCopyright安装包右键 - 详细信息 - 版权版权信息QMAKE_TARGET_DESCRIPTION安装后exe右键 - 详细信息 - 文件说明主程序的简介QMAKE_TARGET_PRODUCT安装后exe右键 - 详细信息 - 产品名称主程序产品名有了这个表哪一层信息缺失就能直接定位到对应的配置位置。6. 常见问题与排查实录6.1 语言文件报错编译时报类似Reading file: ... ChineseSimplified.isl失败的错误基本就是[Languages]段指错了路径。检查编译器版本是否安装了官方中文语言包或者直接用完整路径指到编译器安装目录下的Languages文件夹。如果确实没有中文语言包文件可以在Inno Setup安装时勾选简体中文语言或者从另一台装有同版本编译器的机器拷贝.isl文件过来。6.2 安装后exe没有文件描述这是最典型的问题安装包属性里简介齐全装完看主程序exe却是空的。原因前面已经说过Inno Setup写的VersionInfo只管安装包本身Qt主程序的资源信息要在.pro文件里设置VERSION和QMAKE_TARGET_DESCRIPTION后重新编译。如果已经上了线上版本只能临时用资源编辑器补但下个版本一定要在工程里写死。6.3 安装包中文乱码现在Inno Setup 6的安装包默认Unicode通常不会有乱码。出现乱码多半是.iss脚本文件编码问题或者在别处编辑脚本时用了非UTF-8编码。处理方式是在Inno Setup编译器里全选脚本内容剪切后重新粘贴让编译器按默认方式保存一次然后再编译。InfoBeforeFile指定的INTRO.txt如果出现乱码则检查该文件本身的编码保存为UTF-8即可。6.4 安装完成后程序启动报“无法定位程序输入点”或直接闪退这种情况大多和Qt运行库缺失或版本不一致有关。检查打包输出目录里是否完整包含了windeployqt生成的所有DLL和插件目录尤其是platforms\qwindows.dll。我习惯在[Files]段里保留根目录DLL的递归复制同时在新装好的系统上做一次干净环境验证确保不是依赖了开发机上才有的第三方库。6.5 Installation信息页内容显示不完整InfoBeforeFile的文件内容如果是一大段文字Inno Setup会在页面里自动加滚动条但换行和格式依赖文件本身的排版。TXT文件建议手动换行不要依赖自动换行RTF文件可以在RTF编辑器里做好宽度设置效果更可控。我个人在实际操作中最大的感触是打包这件事技术难度不高但细节非常琐碎。第一次做的时候以为只要把exe和DLL拖进去、脚本里写几个名字就完事了结果安装包倒是很体面装完一看主程序exe属性全空又重新回Qt工程补字段、重新编译、重新打包费了不少时间。所以建议后面接手打包的人在动手前先按本文开头那三层框架确认需求每一层都验收到位再交付这样一次通过的把握会大很多。