制作 NSIS 安装包时,如何打包 .NET Framework 4.0?
进行 Windows 软件开发时,一般都会依赖某个版本的 .Net Framework。因此我们在制作安装包时,需要将所依赖版本的 .Net Framework 进行打包。
首先创建语言文件 lang_ch.nsi,内容如下:
Unicode true
!define sec_prod "置顶王"
!define sec_brand "图加软件(TujiaStudio)"
!define sec_run "运行 "
!define sec_update "更新完成!$\r$\n$\r$\n马上运行 ${sec_prod} 吗?"
Function InitStrings
Var /GLOBAL parent_dir
Var /GLOBAL publisher
Var /GLOBAL sec_shortcut
Var /GLOBAL sec_desktop
Var /GLOBAL sec_startmenu
Var /GLOBAL sec_prod_descrip
Var /GLOBAL sec_shortcut_descrip
Var /GLOBAL sec_desktop_descrip
Var /GLOBAL sec_startmenu_descrip
Var /GLOBAL instalreadyrun
Var /GLOBAL prodalreadyrun4inst
Var /GLOBAL msgDotNetNotInstalled
Var /GLOBAL msgDotNetDownloadBegin
Var /GLOBAL msgDotNetDownloadCompleted
Var /GLOBAL msgDotNetDownloadCanceled
Var /GLOBAL msgDotNetDownloadFailed
Var /GLOBAL msgPausingInstall
Var /GLOBAL msgFinishInstallNet
Var /GLOBAL msgRemoveNetInstaller
Var /GLOBAL msgCancelInstallNet
Var /GLOBAL msgProceedInstall
StrCpy $publisher "图加软件"
StrCpy $sec_shortcut "创建快捷方式"
StrCpy $sec_desktop "桌面"
StrCpy $sec_startmenu "启动菜单"
StrCpy $sec_prod_descrip "${sec_prod}主程序组件"
StrCpy $sec_shortcut_descrip "创建桌面或启动菜单快捷方式"
StrCpy $sec_desktop_descrip "创建桌面快捷方式"
StrCpy $sec_startmenu_descrip "创建启动菜单快捷方式"
StrCpy $instalreadyrun "安装程序已经运行!"
StrCpy $prodalreadyrun4inst "${sec_prod}正在运行,安装前请先退出程序!"
StrCpy $msgDotNetNotInstalled "未安装.NET Framework 4.0.$\n是否从 microsoft.com 下载 .NET Framework 4.0 并安装?"
StrCpy $msgDotNetDownloadBegin "开始下载 .NET Framework 4.0."
StrCpy $msgDotNetDownloadCompleted "完成下载。"
StrCpy $msgDotNetDownloadCanceled "下载取消。 是否继续安装?"
StrCpy $msgDotNetDownloadFailed "下载取消:$\n$0$\n$\n是否继续安装?"
StrCpy $msgPausingInstall "暂停安装${sec_prod},安装.NET Framework 4.0。"
StrCpy $msgFinishInstallNet ".NET Framework 4.0 成功安装。移除 .NET Framework 4.0 安装包。"
StrCpy $msgRemoveNetInstaller ".NET Framework 4.0 安装包成功移除。"
StrCpy $msgCancelInstallNet ".NET Framework 4.0 安装被用户中止。"
StrCpy $msgProceedInstall "继续安装${sec_prod}。"
FunctionEnd
Function un.InitStrings
Var /GLOBAL uninstalreadyrun
Var /GLOBAL prodalreadyrun4uninst
StrCpy $uninstalreadyrun "卸载程序已经运行!"
StrCpy $prodalreadyrun4uninst "${sec_prod}正在运行,卸载前请先退出程序!"
FunctionEnd
然后创建安装包源码文件 TMK_installer.nsi,内容如下(注意其中 IsDotNetInstalled 这个函数的定义及调用):
Unicode true
; Defines.
!define prod_name "TMK"
!define version "2.0.0"
!define size 432
!define out_file "${prod_name}_Setup_V${version}.exe"
!define exe_file "TopMostKing.exe"
!define mutex_id "${prod_name}_Inst_Mutex"
!define mutex_id_un "${prod_name}_Uninst_Mutex"
!define mutex_id_prod "TopMostKing"
!define lang_file "lang_ch.nsi"
!define root_key HKCU
!define request_lvl admin
!define DOT_MAJOR 4
!define DOT_MINOR 0
!define DOTNET40Full_URL "https://www.microsoft.com/downloads/info.aspx?na=41&srcfamilyid=0a391abd-25c1-4fc0-919f-b21f31ab88b7&srcdisplaylang=en&u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f9%2f5%2fA%2f95A9616B-7A37-4AF6-BC36-D6EA96C8DAAE%2fdotNetFx40_Full_x86_x64.exe"
;Includes
!include "MUI2.nsh"
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!include ${lang_file}
; Callbacks.
Function .onInit
Call InitStrings
; Check TMK running.
System::Call 'kernel32::CreateMutex(p 0, i 0, t "${mutex_id_prod}") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "$prodalreadyrun4inst"
Abort
; Check single instance of installer.
System::Call 'kernel32::CreateMutex(p 0, i 0, t "${mutex_id}") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "$instalreadyrun"
Abort
FunctionEnd
Function un.onInit
Call un.InitStrings
; Check TMK running.
System::Call 'kernel32::CreateMutex(p 0, i 0, t "${mutex_id_prod}") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "$prodalreadyrun4uninst"
Abort
; Check single instance of installer.
System::Call 'kernel32::CreateMutex(p 0, i 0, t "${mutex_id_un}") p .r1 ?e'
Pop $R0
StrCmp $R0 0 +3
MessageBox MB_OK|MB_ICONEXCLAMATION "$uninstalreadyrun"
Abort
FunctionEnd
; Custom calls.
Function IsDotNetInstalled
Push $0
Push $1
Push $2
Push $3
Push $4
Push $5
Push $6
Push $7
StrCpy $0 "0"
StrCpy $1 "SOFTWARE\Microsoft\.NETFramework" ; Registry entry to look in.
StrCpy $2 0
StartEnum:
; Enumerate the versions installed.
EnumRegKey $3 HKLM "$1\policy" $2
; If we don't find any versions installed, it's not here.
StrCmp $3 "" NoDotNET notEmpty
; We found something.
notEmpty:
; Find out if the RegKey starts with 'v'.
; If it doesn't, goto the next key.
StrCpy $4 $3 1 0
StrCmp $4 "v" +1 goNext
StrCpy $4 $3 1 1
; It starts with 'v'. Now check to see how the installed major version
; relates to our required major version.
; If it's equal check the minor version, if it's greater,
; we found a good RegKey.
IntCmp $4 ${DOT_MAJOR} +1 goNext yesDotNetReg
; Check the minor version. If it's equal or greater to our requested
; version then we're good.
StrCpy $4 $3 1 3
IntCmp $4 ${DOT_MINOR} yesDotNetReg goNext goNext
goNext:
; Go to the next RegKey.
IntOp $2 $2 + 1
goto StartEnum
yesDotNetReg:
; Now that we've found a good RegKey, let's make sure it's actually
; installed by getting the install path and checking to see if the
; mscorlib.dll exists.
EnumRegValue $2 HKLM "$1\policy\$3" 0
; $2 should equal whatever comes after the major and minor versions
; (ie, v1.1.4322)
StrCmp $2 "" NoDotNET
ReadRegStr $4 HKLM $1 "InstallRoot"
; Hopefully the install root isn't empty.
StrCmp $4 "" NoDotNET
; Build the actuall directory path to mscorlib.dll.
StrCpy $4 "$4$3.$2\mscorlib.dll"
IfFileExists $4 NewDotNET NoDotNET
NoDotNET:
MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION \
$msgDotNetNotInstalled \
/SD IDYES IDYES DownloadDotNET IDNO NewDotNET
goto GiveUpDotNET ;IDCANCEL
DownloadDotNET:
DetailPrint $msgDotNetDownloadBegin
NSISDL::download ${DOTNET40Full_URL} "$TEMP\dotnetfx40_full.exe"
DetailPrint $msgDotNetDownloadCompleted
Pop $0
${If} $0 == "cancel"
MessageBox MB_YESNO|MB_ICONEXCLAMATION \
$msgDotNetDownloadCanceled \
IDYES NewDotNET IDNO GiveUpDotNET
${ElseIf} $0 != "success"
MessageBox MB_YESNO|MB_ICONEXCLAMATION \
$msgDotNetDownloadFailed \
IDYES NewDotNET IDNO GiveUpDotNET
${EndIf}
DetailPrint $msgPausingInstall
ExecWait '$TEMP\dotnetfx40_full.exe /q /c:"install /q"'
DetailPrint $msgFinishInstallNet
Delete "$TEMP\dotnetfx40_full.exe"
DetailPrint $msgRemoveNetInstaller
goto NewDotNet
GiveUpDotNET:
Abort $msgCancelInstallNet
NewDotNET:
DetailPrint $msgProceedInstall
Pop $7
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Pop $0
FunctionEnd
; The name of the installer
Name "${sec_prod} ${version}"
; The branding text.
BrandingText ${sec_brand}
; The file to write
OutFile ${out_file}
; CRC check.
CRCCheck on
; The default installation directory
InstallDir "$PROGRAMFILES\TujiaStudio\${prod_name}"
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey ${root_key} "Software\TujiaStudio\${prod_name}" "InstallDir"
; Request application privileges for Windows
RequestExecutionLevel ${request_lvl}
Function "MyRun"
ShellExecAsUser::ShellExecAsUser "open" "$INSTDIR\${exe_file}"
FunctionEnd
;--------------------------------
;Pages
;--------------------------------
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\tmk.bmp"
!define MUI_HEADERIMAGE_RIGHT
!insertmacro MUI_PAGE_LICENSE "License.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_SHOWREADME "readme.txt"
;!define MUI_FINISHPAGE_RUN ${exe_file}
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "${sec_run}${sec_prod}"
!define MUI_FINISHPAGE_RUN_FUNCTION "MyRun"
!define MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\tmk.bmp"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
;Languages
;--------------------------------
!insertmacro MUI_LANGUAGE "SimpChinese"
;--------------------------------
; Installer
Section ${sec_prod} sec_prod_idx
SectionIn RO
; Set output path to the installation directory.
SetOutPath $INSTDIR
; Check .NET 4.0
Call IsDotNetInstalled
; Put file there
SetOutPath $INSTDIR
File ..\readme.txt
IfSilent block1 block2
block1:
ClearErrors
SetOverwrite try
File ..\${exe_file}
File ..\TujiaStudio.dll
IfErrors block1 done
block2:
File ..\${exe_file}
File ..\TujiaStudio.dll
Goto done
done:
SetOverwrite on
; Write info to registry
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "DisplayIcon" "$INSTDIR\${exe_file}"
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "DisplayName" "${sec_prod}"
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "DisplayVersion" "${version}"
WriteRegDWORD ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "EstimatedSize" ${size}
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "Publisher" "$publisher"
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegStr ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "URLInfoAbout" "http://www.tujiastudio.com"
WriteRegDWORD ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "NoModify" 1
WriteRegDWORD ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}" "NoRepair" 1
WriteRegStr ${root_key} "Software\TujiaStudio\${prod_name}" "InstallDir" "$INSTDIR"
WriteRegStr ${root_key} "Software\TujiaStudio\${prod_name}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteUninstaller "$INSTDIR\uninstall.exe"
IfSilent 0 false2
MessageBox MB_YESNO "${sec_update}" IDYES true2 IDNO false2
true2:
Call MyRun
false2:
SectionEnd
; Optional section (can be disabled by the user)
SectionGroup $sec_shortcut sec_shortcut_idx
Section $sec_desktop sec_desktop_idx
SetShellVarContext current
CreateShortCut "$DESKTOP\${prod_name}.lnk" "$INSTDIR\${exe_file}" \
"" "$INSTDIR\${exe_file}" 0 SW_SHOWNORMAL "" ${sec_prod}
SectionEnd
Section $sec_startmenu sec_startmenu_idx
CreateDirectory "$SMPROGRAMS\TujiaStudio"
CreateShortcut "$SMPROGRAMS\TujiaStudio\${prod_name}.lnk" "$INSTDIR\${exe_file}" \
"" "$INSTDIR\${exe_file}" 0 SW_SHOWNORMAL "" ${sec_prod}
SectionEnd
SectionGroupEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
DeleteRegKey ${root_key} "Software\Microsoft\Windows\CurrentVersion\Uninstall\${prod_name}"
DeleteRegKey ${root_key} "Software\TujiaStudio\${prod_name}"
DeleteRegKey /ifempty ${root_key} "Software\TujiaStudio"
; Remove files and uninstaller
Delete $INSTDIR\uninstall.exe
Delete $INSTDIR\${exe_file}
Delete $INSTDIR\TujiaStudio.dll
Delete $INSTDIR\readme.txt
; Remove shortcuts, if any
Delete "$SMPROGRAMS\TujiaStudio\${prod_name}.lnk"
Delete "$DESKTOP\${prod_name}.lnk"
; Remove directories used
RMDir "$INSTDIR"
${GetParent} "$INSTDIR" $parent_dir
RMDir "$parent_dir"
RMDir "$SMPROGRAMS\TujiaStudio"
SectionEnd
;--------------------------------
;Descriptions
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${sec_prod_idx} $sec_prod_descrip