NSIS学习笔记(以Qt4程序打包为例)

晨曦之光 发布于 2012/05/08 10:16
阅读 1K+
收藏 0

一个Qt4程序安装(发布)后它应该有如下的结构(可参考 Qt 程序在 windows 下的发布 ):

|-- sample.exe
|-- QtCore4.dll
|-- QtGui4.dll
|-- imageformats/
|       |-- qjpeg4.dll

接下来我们使用nsis,来制作一个实现这个功能的安装程序(并稍作完善)

如果你还没有安装nsis,不妨马上去下载一个。

第一个脚本

要使用nsis,只需要编写一个简单的 xxx.nsi 文件:

;A simple nsis script file: sample.nsi
!define QTDIR "D:/Qt/4.7.0"

outfile "sample-installer.exe"
installDir "$PROGRAMFILES/sample" 
RequestExecutionLevel admin

section
setOutPath $INSTDIR
file sample.exe
file ${QTDIR}/bin/QtCore4.dll
file ${QTDIR}/bin/QtGui4.dll

setOutPath $INSTDIR/imageformats
file ${QTDIR}/plugins/imageformats/qjpeg4.dll
sectionEnd

将该文件和我们的sample.exe 放到同一个文件夹,然后点右键,选择"编译NSIS脚本"。稍等片刻,一个安装程序 sample-installer.exe 就生成了。

知识点

  • 注释
    • 以分号";"或井号"#"开头的行都是注释,也可以使用C语言中的"/* */"
  • 编译期命令(Compile Time Commands)
    • 以叹号"!"开始的命令叫做编译期命令(名字比较怪,但我们将其理解成C、C++中的

    • 一定要理清这一点,它只是一种扩展。
  • outfile
    • 给安装程序指定个名字
  • installDir
    • 默认将文件安装到何处
  • RequestExecutionLevel

    • 对Vista及Win7用户,安装程序工作时需要申请执行权限
  • section
    • 每一个nsis脚本至少要有一个section段。段内执行我们需要的安装操作
    • setOutPath
      • 指定文件输出到何处
    • file
      • 安装哪些文件

有没有疑问?我们一开始设置了 installDir,后面使用的却是INSTDIR。installDir 和 INSTDIR 什么关系?!

INSTDIR 的值获得方式:

  • 如果脚本编译时通过 /D 选项指定了路径,将使用该路径。否则,
  • 如果从注册表读取路径到 InstallDirRegKey,将使用该路径。否则,

  • 才使用installDir 指定的路径

注意:用户可以通过界面选择安装路径,也是影响的INSTDIR的值

第二个脚本

前面的脚本功能太弱了:

  • 用户不能选择安装目录
  • 只有安装,没有卸载功能
  • 不能创建快捷方式

改进一下

安装路径

安装程序相当于是一个wizard多页面程序,我们只要添加相应的页面即可。直接在第一个section的上面添加两行:

Page directory
Page instfiles

这些都是nsis提供的标准页面。

添加卸载功能

添加卸载功能也就是要生成一个卸载程序,这个程序时安装程序安装时自动生成的。要实现这个,我们只需要在前面的section中添加一句

writeUninstaller $INSTDIR/uninstaller.exe

那么该卸载程序执行什么动作呢?这需要我们添加一个新的段:

section "Uninstall"
rmDir /r "$INSTDIR"
sectionEnd

快捷方式

常见的两类:

一个是在桌面上

createShortCut "$DESKTOP/sample.lnk" "$INSTDIR/sample.exe"

一个是在开始菜单的"程序"中。

createShortCut "$SMPROGRAMS/sample.lnk" "$INSTDIR/sample.exe"

很直观,直接放到section段中即可。程序卸载时需要删除快捷方式,故在uninstall段中需要添加形影的删除指令。

关于createShortCut的详细信息看Manual:

link.lnk target.file [parameters [icon.file [icon_index_number [start_options [keyboard_shortcut [description]]]]]]

可以设置图标、命令行参数、快捷键、描述等等。

完整代码

 

!define QTDIR "D:/Qt/4.7.0"

outfile "sample-installer.exe"
installDir "$PROGRAMFILES/sample" 
RequestExecutionLevel admin

Page directory
Page instfiles

section
setOutPath $INSTDIR
file sample.exe
file ${QTDIR}/bin/QtCore4.dll
file ${QTDIR}/bin/QtGui4.dll

setOutPath $INSTDIR/imageformats
file ${QTDIR}/plugins/imageformats/qjpeg4.dll

createShortCut "$DESKTOP/sample.lnk" "$INSTDIR/sample.exe"
createShortCut "$SMPROGRAMS/sample.lnk" "$INSTDIR/sample.exe"

writeUninstaller $INSTDIR/uninstaller.exe
sectionEnd

section "Uninstall"
rmDir /r "$INSTDIR"
delete "$DESKTOP/sample.lnk"
delete "$SMPROGRAMS/sample.lnk"
sectionEnd

知识点

page

  • 用来添加页面
  • 还有一个灵活一些的 pageEx 命令
  • 卸载程序的页面可以使用uninstPage 命令指定

section

  • 每一个NSIS安装脚本包含至少一个section
  • 每一个section包含0条或多条指令
  • 各个section按顺序执行(运行中可以禁用启用某些section)
  • 名字为"Uninstall"或者以"un."为前缀的section,属于卸载程序

第三个脚本

第二个例子可以工作了,但是有很不爽的一点,我们还一直没提:界面好丑,好老土啊!

尽管nsis 原始自带的界面不好看,但是它也提供了新式的安装界面 (它是通过宏扩展来实现的)。使用起来也是很方便,我们看看如何做:

包含新式的头文件(我们一开始就说了,叹号开头的可以类比C、C++的宏)

!include "MUI2.nsh"

插入新式页面

!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES

卸载页面类似(PAGE和UNPAGE的区别)

!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES

由于新式页面提供了友好的多语言支持,我们选择简体中文

!insertmacro MUI_LANGUAGE "SimpChinese"

完整代码

 

!include "mui2.nsh"

!define QTDIR "D:/Qt/4.7.0"

outfile "sample-installer.exe"
installDir "$PROGRAMFILES/sample" 
RequestExecutionLevel admin

;Page directory
;Page instfiles
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "SimpChinese"

section
setOutPath $INSTDIR
file sample.exe
file ${QTDIR}/bin/QtCore4.dll
file ${QTDIR}/bin/QtGui4.dll

setOutPath $INSTDIR/imageformats
file ${QTDIR}/plugins/imageformats/qjpeg4.dll

createShortCut "$DESKTOP/sample.lnk" "$INSTDIR/sample.exe"
createShortCut "$SMPROGRAMS/sample.lnk" "$INSTDIR/sample.exe"

writeUninstaller $INSTDIR/uninstaller.exe
sectionEnd

section "Uninstall"
rmDir /r "$INSTDIR"
delete "$DESKTOP/sample.lnk"
delete "$SMPROGRAMS/sample.lnk"
sectionEnd

函数

前一个已经工作的很好了,但是我们可能有其他的要求。接下来我们只看代码片段和重要的概念:

前面一直没涉及nsis的另一个重要的概念:函数(functions)。

  • 函数和我们前面的段比较相似,都是包含需要执行的指令,不同之处在于section会被直接执行。而函数则需要调用。
  • 按调用方式它可分两类,一是被直接调用的函数,另一类则称为回调函数。

直接调用

很简单,我们看下面的代码片段即可。

Function func
  ;some commands
FunctionEnd

Section
  Call func
SectionEnd

回调

回调函数在特定的时候被安装程序调用,比如 .onInit 在初始化时会被调用。

这时我们可以弹出一个对话框

 Function .onInit
   MessageBox MB_YESNO "This will install. Continue?" IDYES NoAbort
     Abort ;
   NoAbort:
 FunctionEnd

提示用户是否继续。

更有用的一点可能是,我们希望同一时刻只有一个安装程序在运行

Function .onInit
 System::Call 'kernel32::CreateMutexA(i 0, i 0, t "SAMPLE_MUTEX") i .r1 ?e'
 Pop $R0
 
 StrCmp $R0 0 +3
   MessageBox MB_OK|MB_ICONEXCLAMATION "Another Installer is Running!"
   Abort
FunctionEnd

这儿用到了一个system::call,这是什么东西?

插件

在前面,我们调用了系统的api来创建一个Windows的内核对象(互斥量)。调用系统api使用的是nsis的System插件(由于不需要额外的操作,你可能没意识到它是插件)。

我们看一下Call如何使用的:

Call PROC [( PARAMS ) [RETURN [? OPTIONS]]]

对照一下!

i 0, i 0, t "SAMPLE_MUTEX"

PARAMS

函数参数(类型和值)

i .r1

RETURN

返回值

?e

? OPTIONS

选项e,可执行程序的路径

注意,参数和返回值,其实都是3个值一组(类型、(source)值、(destination)值)。返回值中的.代表忽略source值。

除了system插件,nsis自带的还有进行数学运算的math,自定义界面的nsDialog插件等等。

LogicLib

我们前面涉及到了 StrCmp $R0 0 +3  ,这是神马用法??

流程控制真的让人头疼,用标签和相对位置进行跳转,给人一种汇编语言的感觉。看看下面的例子:

StrCmp $0 'some value' 0 +3
  MessageBox MB_OK '$$0 is some value'
  Goto done
StrCmp $0 'some other value' 0 +3
  MessageBox MB_OK '$$0 is some other value'
  Goto done
# else
  MessageBox MB_OK '$$0 is "$0"'
done:

一不小心数错行,或者增减语句,可都不是小事情了。

幸好,nsis可以通过类似宏的机制扩展,而且也提供了一个 logiclib.nsh 文件。这样一来,我们处理分支或循环就简单多了(尽管语法少多有点别扭,没办法,毕竟不是内置支持的东西)

 

${If} $0 == 'some value'
  MessageBox MB_OK '$$0 is some value'
${ElseIf} $0 == 'some other value'
  MessageBox MB_OK '$$0 is some other value'
${Else}
  MessageBox MB_OK '$$0 is "$0"'
${EndIf}

其他

文本只是一个学习笔记,很多东西都没有涉及(manual中很详细哈,遇到问题应该首先去查manual)。


原文链接:http://blog.csdn.net/dbzhang800/article/details/6411870
加载中
返回顶部
顶部