news 2026/10/16 3:22:39

NSIS+Duilib 仿 QQ 安装包源码:自定义界面与安装逻辑实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NSIS+Duilib 仿 QQ 安装包源码:自定义界面与安装逻辑实战

简介:这是一份面向Windows桌面开发者的NSIS与Duilib整合实战工程,目标是复刻腾讯QQ安装程序的界面风格与交互流程。资源包共206个文件,约69.53MB,以h头文件、cpp源码、obj编译产物为主,辅以png界面素材、sln与vcxproj工程文件、nsi与nsh脚本,以及7z、rar等第三方依赖压缩包,完整覆盖从源码到构建的各个环节。工程按3party、samples、plugin、include等目录组织,分别承载第三方库、示例模板、NSIS扩展插件与头文件声明,便于理解NSIS脚本与Duilib界面库的协作方式。已有1866人学习下载,适合具备C++基础、希望掌握自定义安装包开发的程序员参考,可从中学习界面渲染、插件调用、脚本编写与安装流程优化的具体实现思路。

1. 从一次打包翻车说起:NSIS+Duilib 仿 QQ 安装包到底能解决什么

上周帮朋友处理一个桌面工具的发布,用某商业打包工具生成的安装包,界面是默认的灰底下一步下一步,用户装完就忘。更头疼的是,安装过程中想加一个自定义的协议注册和静默参数判断,翻遍了文档也没找到干净的入口。后来换回 NSIS 配合 Duilib 重写界面,才算把安装流程的控制权拿回来。这套 NSIS+Duilib 仿 QQ 安装包源码,核心就是用 NSIS 做安装逻辑引擎,用 Duilib 做界面渲染层,复刻出类似 QQ 安装包那种带自定义背景、动态按钮、协议勾选、路径选择的安装体验。它适合两类人:一是需要给桌面软件做品牌化安装界面的开发者,二是想学 NSIS 插件通信和 Duilib 窗口嵌入的 Windows 客户端工程师。如果你正在找一套能直接跑起来、又能拆开改的安装包模板,这份资源值得花时间拆一遍。

2. NSIS 与 Duilib 的协作机制:为什么不是单纯换皮

2.1 NSIS 负责什么,Duilib 负责什么

NSIS 本身是一个脚本驱动的安装制作工具,它的强项在于文件释放、注册表写入、快捷方式创建、卸载信息登记这些安装器该干的脏活累活。但它的原生界面能力停留在 Win32 控件级别,按钮样式、背景图、动画效果都很难做到现代安装包那种视觉水准。Duilib 则是一个轻量级 Windows 界面库,基于 DirectUI 思想,把控件绘制、消息路由、布局管理都抽象出来,适合做无边框、自绘风格的窗口。

这套源码的分工很明确:NSIS 脚本负责安装流程的调度,包括页面跳转、文件解压、注册表操作、静默参数解析;Duilib 编译出的 DLL 负责界面渲染和用户交互,包括背景绘制、按钮悬停效果、协议勾选框、路径编辑框。两者通过 NSIS 的插件接口通信,NSIS 在合适的时机调用 Duilib 插件,Duilib 把用户操作结果回传给 NSIS。

常见做法是,NSIS 脚本里用nsisDuilib::showPage这类自定义插件函数触发界面显示,Duilib 内部维护一个状态机,把安装路径、协议勾选状态、是否创建桌面快捷方式这些数据通过共享内存或窗口消息回传。这样 NSIS 不需要关心界面怎么画,Duilib 也不需要关心文件怎么释放。

2.2 为什么不用 NSIS 原生界面加皮肤

有人会问,NSIS 不是有 MUI2 吗,换套皮肤不就行了。实际用过就知道,MUI2 的皮肤机制本质还是 Win32 控件换位图,按钮的悬停、按下、禁用三态切换很生硬,背景图拉伸容易糊,高 DPI 下更是灾难。Duilib 的优势在于它自己接管了绘制,所有控件都是自绘的,你可以精确控制每个像素的渲染,还能做半透明、圆角、渐变这些效果。仿 QQ 安装包那种左侧大图、右侧选项、底部按钮栏的布局,用 Duilib 做起来比 MUI2 顺手得多。

另一个原因是交互逻辑的复杂度。QQ 安装包里有安装路径的实时校验、磁盘空间检测、协议勾选状态联动安装按钮的可用性,这些用 NSIS 原生控件写起来很别扭,而 Duilib 里就是几个控件属性和消息处理的事。

2.3 插件通信的基本约定

NSIS 调用 Duilib 插件,本质上是通过pluginapi.h里的导出函数。Duilib 编译成 DLL 后,需要导出NSISPluginInit、NSISPluginUnload以及具体的页面显示函数。NSIS 脚本里用nsisDuilib::函数名的方式调用,参数通过 NSIS 的栈传递。

一个典型的调用约定是这样的:

; NSIS 脚本中调用 Duilib 插件显示安装选项页 nsisDuilib::ShowInstallPage /NOUNLOAD "$INSTDIR" Pop $0 ; 返回值,0 表示用户点击了安装,1 表示取消

Duilib 侧接收参数后,把安装路径设置到界面控件上,然后进入消息循环。用户点击安装按钮时,Duilib 把当前界面上的路径、勾选状态打包,通过push指令压回 NSIS 栈,然后返回。NSIS 拿到返回值后决定下一步是执行安装还是回退页面。

注意:NSIS 插件调用是同步的,Duilib 的消息循环必须在插件函数返回前结束,否则 NSIS 主线程会卡死。常见做法是在 Duilib 里用模态对话框的方式跑消息循环,收到确定或取消后退出循环。

3. 把源码跑起来:编译环境、目录结构与关键配置

3.1 编译环境准备

这套源码涉及两个编译产物:Duilib 的 DLL 和 NSIS 的安装包。Duilib 部分用 Visual Studio 编译,建议 VS2015 及以上,平台工具集选 v140 或 v141,字符集用 Unicode。NSIS 部分需要安装 NSIS 3.x 版本,并且把 Duilib 编译出的 DLL 放到 NSIS 的插件目录或者安装包脚本同级目录。

依赖项方面,Duilib 本身依赖 Windows SDK 和 DirectX SDK 的一部分头文件,但源码包里通常已经包含了精简版的 Duilib,不需要额外装 DirectX SDK。如果编译时报d3d9.h找不到,检查 VS 安装时有没有勾选 Windows SDK 的图形组件。

3.2 目录结构说明

源码包的目录结构大致如下,不同版本可能略有差异:

目录/文件作用
Duilib/Duilib 库源码,包含核心绘制和控件实现
InstallerUI/安装包界面工程,编译出InstallerUI.dll
NSISScript/NSIS 脚本目录,含主脚本和页面逻辑
NSISScript/Plugins/存放编译好的 Duilib 插件 DLL
Resource/界面用到的图片、XML 布局文件
Build/编译输出目录,含最终安装包 exe

InstallerUI工程里会有一个stdafx.h和InstallerUI.cpp,入口函数是NSISPluginInit。界面布局用 Duilib 的 XML 描述,放在Resource目录下,运行时通过相对路径加载。

3.3 编译 Duilib 插件

打开InstallerUI.sln,选择 Release 配置,平台选 Win32 或 x64 要和 NSIS 脚本里的目标架构一致。编译前检查几个关键配置:

// InstallerUI.cpp 中插件导出函数示例 extern "C" __declspec(dllexport) void NSISPluginInit(HWND hParent, int code, char* szCmdLine) { // hParent 是 NSIS 传进来的父窗口句柄 // code 是调用码,区分不同页面 // szCmdLine 是命令行参数,通常传安装路径 if (code == 1) { // 显示安装选项页 CInstallWnd wnd; wnd.Create(hParent, _T("安装选项"), UI_WNDSTYLE_DIALOG, 0); wnd.CenterWindow(); wnd.ShowModal(); } }

编译成功后,把InstallerUI.dll复制到NSISScript/Plugins/目录下。如果 NSIS 脚本里用的是nsisDuilib::前缀,DLL 文件名需要和脚本里的插件名一致,或者用!addplugindir指定插件搜索路径。

3.4 NSIS 脚本关键段落实例

主脚本里控制页面跳转和安装逻辑的部分大概长这样:

; 定义安装目录变量 Var InstallPath Var ProtocolChecked ; 页面:欢迎页之后调用 Duilib 显示安装选项 Function ShowOptionsPage ; 从注册表或默认路径读取初始安装目录 StrCpy $InstallPath "$PROGRAMFILES\MyApp" ; 调用 Duilib 插件,传入初始路径 nsisDuilib::ShowInstallPage /NOUNLOAD "$InstallPath" Pop $0 ; 返回值:0=安装,1=取消 ; 从插件获取用户修改后的路径和协议勾选状态 nsisDuilib::GetInstallPath /NOUNLOAD Pop $InstallPath nsisDuilib::GetProtocolChecked /NOUNLOAD Pop $ProtocolChecked StrCmp $0 "0" +2 Abort ; 用户取消,终止安装 FunctionEnd

这段脚本的逻辑是:先给一个默认安装路径,调用 Duilib 界面让用户确认或修改,用户点击安装后从插件取回最终路径和协议勾选状态,如果用户点了取消就Abort终止安装流程。参数/NOUNLOAD表示插件调用后不卸载 DLL,因为后面还要继续调用GetInstallPath和GetProtocolChecked。

提示:如果 NSIS 脚本里调用插件时报「插件未找到」,检查 DLL 是否放到了NSISScript/Plugins/下,或者脚本开头有没有用!addplugindir指定正确路径。32 位 NSIS 只能加载 32 位 DLL,64 位同理。

4. 避坑与排查:五个让安装包翻车的细节

4.1 现象:安装界面显示空白或花屏

原因:Duilib 加载 XML 布局文件时路径不对,或者图片资源没有正确打包进安装包。NSIS 编译时如果没把Resource目录下的图片和 XML 一起释放到临时目录,Duilib 运行时找不到文件就会绘制异常。

解决:在 NSIS 脚本的Section里先把资源文件释放到$PLUGINSDIR或$TEMP下的一个子目录,然后通过命令行参数把资源路径传给 Duilib 插件。Duilib 侧用绝对路径加载 XML,不要用相对路径。

4.2 现象:点击安装按钮没反应,界面卡死

原因:Duilib 的消息循环没有正确退出,或者 NSIS 插件调用时父窗口句柄传错了。常见情况是 Duilib 窗口创建时用了HWND_DESKTOP而不是 NSIS 传进来的hParent,导致窗口不在 NSIS 的消息队列里。

解决:检查Create函数的第一个参数,确保用的是NSISPluginInit回调里拿到的hParent。另外确认ShowModal内部的消息循环在收到WM_CLOSE或自定义退出消息时能正确PostQuitMessage。

4.3 现象:高 DPI 显示器上界面模糊或错位

原因:Duilib 默认按 96 DPI 布局,系统缩放 125% 或 150% 时,窗口按物理像素创建,但控件坐标还是逻辑像素,导致错位。NSIS 本身对高 DPI 的支持也有限。

解决:在 Duilib 窗口创建前调用SetProcessDPIAware,然后根据GetDeviceCaps(LOGPIXELSX)获取实际 DPI,对布局坐标做等比缩放。或者直接在清单文件里声明 DPI 感知,让系统不做虚拟化。仿 QQ 安装包这类界面,建议按 96 DPI 设计,运行时统一缩放。

4.4 现象:静默安装参数失效

原因:NSIS 脚本里判断/S参数的逻辑放在了 Duilib 页面调用之后,导致静默模式下还是弹出了界面。或者 Duilib 插件在静默模式下没有正确跳过界面显示。

解决:在.onInit回调里最先判断IfSilent,静默模式下直接走安装逻辑,不调用任何界面插件。Duilib 侧也要加一个判断,如果收到静默标志就立即返回默认值,不创建窗口。

4.5 现象:卸载时残留文件或注册表项

原因:NSIS 的卸载段没有清理 Duilib 释放到临时目录的资源文件,或者注册表写入时用了WriteRegStr但卸载时忘了DeleteRegKey。

解决:在Section Uninstall里显式删除$PLUGINSDIR和$TEMP下的资源目录,注册表项用DeleteRegKey HKLM "Software\MyApp"整树删除。如果安装时注册了文件关联或协议,卸载时也要对应清理。

5. 进阶技巧:让安装包更像一个产品

5.1 用 XML 布局做多语言切换

Duilib 的界面布局完全由 XML 描述,这意味着你可以准备多套 XML 文件,运行时根据系统语言或命令行参数加载不同的 XML。比如install_zh.xml和install_en.xml,NSIS 脚本里通过$LANGUAGE变量判断当前语言,把对应文件名传给 Duilib 插件。这样不需要重新编译 DLL,只替换资源文件就能切换界面语言。

; 根据系统语言选择 XML 文件 ${If} $LANGUAGE == 2052 StrCpy $R0 "install_zh.xml" ${Else} StrCpy $R0 "install_en.xml" ${EndIf} nsisDuilib::ShowInstallPage /NOUNLOAD "$InstallPath" "$R0"

参数说明:$LANGUAGE是 NSIS 内置变量,2052 对应简体中文。$R0是临时寄存器,用来存 XML 文件名。调用插件时把文件名作为第二个参数传入,Duilib 侧根据文件名加载对应布局。

5.2 安装进度与后台解压的同步

仿 QQ 安装包通常有一个进度条页面,展示文件解压进度。NSIS 的File指令是同步的,解压大文件时界面会卡住。常见做法是把解压操作放到一个单独的线程里,Duilib 界面通过定时器查询进度并刷新进度条。NSIS 侧可以用nsisDuilib::SetProgress插件函数,在解压循环里不断更新进度值。

; 分块解压并更新进度 StrCpy $R1 0 ${For} $R2 1 10 ; 每次解压一部分文件 File "/oname=$INSTDIR\part$R2.dat" "data\part$R2.dat" IntOp $R1 $R1 + 10 nsisDuilib::SetProgress /NOUNLOAD "$R1" ${Next}

这段脚本把解压分成 10 个批次,每完成一批就调用插件更新进度条。实际项目中批次划分要按文件大小来,避免某个批次过大导致进度条跳跃。

5.3 验证安装包完整性的一个习惯

从那以后我每次编译完安装包,都会在虚拟机里跑一遍完整流程:全新安装、覆盖安装、静默安装、卸载、再次安装。重点看三个地方:安装目录下文件是否齐全、注册表项是否写入正确、控制面板里卸载信息是否正常显示。这套 NSIS+Duilib 仿 QQ 安装包源码的价值不在于界面多好看,而在于它把安装器该有的控制权都暴露出来了,你可以精确控制每一步的行为。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 3:21:04

国家中小学智慧教育平台电子课本下载工具:三步直接拿到PDF

国家中小学智慧教育平台电子课本下载工具:三步直接拿到PDF 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地获取课本内容。 项目…

作者头像 李华
网站建设 2026/10/16 3:20:16

Java扫描仪开发实战:基于TWAIN协议与JTwain库的JNI实现

简介:基于Java与JNI技术实现的扫描仪控制程序,面向需要将扫描功能集成到Java桌面应用的开发者。程序通过C封装底层扫描逻辑生成jtwain.dll,再由Java调用该动态库,支持中文环境且扫描过程不弹出系统对话框,相比常规TWAI…

作者头像 李华
网站建设 2026/10/16 3:19:18

词达人抓包解密全指南:Python3.9环境配置与AES解密实战

简介:本资源是一套面向英语学习技术爱好者与逆向分析初学者的词达人客户端抓包调试工具集,聚焦于理解词汇类App网络通信机制与本地交互逻辑。压缩包内含92个文件,以36个DLL动态库、13个EXE可执行程序(含词达人工具.exe、Fiddler.e…

作者头像 李华
网站建设 2026/10/16 3:18:35

Hive启动日志解析:Logging initialized不是报错,而是排查起点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 3:17:39

Windows SDK 7.1 离线安装与C++老项目编译环境配置指南

简介:面向C开发者的Microsoft Windows SDK 7.1安装包,专为Windows 7及Server 2008 R2平台的原生程序开发而设计。SDK集成了最新Windows API接口、支持C0x标准的编译器与链接器,并附带WinDbg等调试工具和性能分析组件,可帮助开发者…

作者头像 李华
网站建设 2026/10/16 3:17:18

Scribe 入门:把一次电脑操作自动记成步骤说明书

适合人群:经常要教同事“这个系统怎么填”“报销流程怎么走”,每次都要截图、标箭头、写说明,觉得很费时间的同学。Scribe 的做法是:你正常操作一遍,它自动把每次点击和输入记成带截图的步骤说明。按 Scribe 帮助中心的…

作者头像 李华