NSIS 自定義界面,下載并安裝Net.Framework4.8
以 ScreenToGif 這款軟件為例,詳細講解如何在安裝的過程中檢測并下載net包進行安裝。
前言
1、ScreenToGif 是一款開源的截屏軟件,依賴于Net.Framework環境
2、本文講解的NSIS安裝過程為自定義界面,而非傳統界面(需要傳統界面的留言區留言)。
3、Win10系統好像是自動集成了Net.Framework4.8的環境
4、安裝Net.Framework4.0以上的版本,需要先安裝微軟證書,再安裝Net.Framework安裝,否則可能安裝不成功,如下圖:
相關資源
Net.Framework4.8離線包下載鏈接:https://download.visualstudio.microsoft.com/download/pr/014120d7-d689-4305-befd-3cb711108212/0fd66638cde16859462a6243a4629a50/ndp48-x86-x64-allos-enu.exe
本文中的安裝示例包:
鏈接: 鏈接: https://pan.baidu.com/s/1aMUEs_F74whkGEzubInkfw
提取碼: 2g6y
NSIS使用到的插件
1、nsNiuniuSkin:基于Duilib的界面庫(自定義界面的界面庫)
2、nsis7zU:壓縮及解壓
3、inetc:下載文件(增加下載回調,當前進度,下載包大小,已下載大小,下載速度 ,剩余時間等信息)
4、KillProcDLL:結束進程(增加向結束進程發送主線程消息,進而實現被結束的進程安全退出,默認的結束方式為強制結束進程)
系統相關的問題
1、Win10系統使用NSIS創建任務欄圖標會失敗,Win10以下的系統無問題。
2、如果你的應用程序啟動需要管理員身份啟動,哪么添加開機啟動將會失敗。
制作好的安裝包安裝過程:
安裝過程的邏輯
1、驗證系統當前的net版本是否低于 4.8.03761,如果低于 4.8.03761 則做如下邏輯:
- 下載微軟證書
- 安裝微軟證書
- 下載net安裝包
- 安裝net安裝包
NSIS功能代碼分享
代碼段相關的宏定義
# ====================== 自定義宏 產品信息==============================
!define PRODUCT_NAME "ScreenToGif"
#安裝卸載項用到的KEY
!define PRODUCT_PATHNAME "ScreenToGif"
#安裝路徑追加的名稱
!define INSTALL_APPEND_PATH "ScreenToGif"
#默認生成的安裝路徑
!define INSTALL_DEFALT_SETUPPATH ""
#執行文件名稱
!define EXE_NAME "ScreenToGif.exe"
#版本號
!define PRODUCT_VERSION "1.0.0.0"
#主頁地址
!define HOME_URL "https://www.screentogif.com/"
#用戶條款
!define TERMS_URL ""
#產品發布商
!define PRODUCT_PUBLISHER "Nicke Manarin"
#產品法律
!define PRODUCT_LEGAL "Nicke Manarin Copyright(c)2020"
#打包出來的文件名稱
!define INSTALL_OUTPUT_NAME "ScreenToGif_${PRODUCT_VERSION}.exe"
#應用程序的數據目錄
!define LOCAL_APPDATA_DIR "$LOCALAPPDATA\ScreenToGif"
#打包文件目錄
!define APP_FILE_DIR "D:\myCode\app\app-qt-client\PackageDirectory\ScreenToGif"
#文件數量
!define APP_FILE_COUNT 9
#完整安裝包下載地址
!define ALL_SETUP_DL_URL ""
#Net包名稱
!define NET_PACK_NAME "ndp48-x86-x64-allos-enu.exe"
#Net包下載地址
!define NET_PACK_DL_URL "https://download.visualstudio.microsoft.com/download/pr/014120d7-d689-4305-befd-3cb711108212/0fd66638cde16859462a6243a4629a50/ndp48-x86-x64-allos-enu.exe"
#微軟證書名稱(win7安裝net4.6以上版本需下載微軟證書并安裝,否則net安裝會失敗)
#net4.0不需要安裝微軟證書
!define MS_ROOT_CERT_NAME "MicrosoftRootCertificateAuthority2011.cer"
#微軟證書下載地址
!define MS_ROOT_CERT_DL_URL "https://download.microsoft.com/download/2/4/8/248D8A62-FCCD-475C-85E7-6ED59520FC0F/MicrosoftRootCertificateAuthority2011.cer"
獲取net版本
;獲取.Net Framework版本支持
Function GetNetFrameworkVersion
Push $1
Push $0
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "InstallSuccess"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.0\Setup" "Version"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "2.0.50727.832"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.1.4322.573"
StrCmp $0 1 KnowNetFrameworkVersion +1
ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Install"
ReadRegDWORD $1 HKLM "SOFTWARE\Microsoft\.NETFramework\policy\v1.0" "Version"
StrCmp $1 "" +1 +2
StrCpy $1 "1.0.3705.0"
StrCmp $0 1 KnowNetFrameworkVersion +1
StrCpy $1 "not .NetFramework"
KnowNetFrameworkVersion:
Pop $0
Exch $1
FunctionEnd
下載微軟證書
; 微軟證書下載回調
Function MicrosoftCertificatePackDownLoadCallBack
; 0-當前進度(百分比)
Pop $0
; 1-累計大小
Pop $1
; 2-已下載大小
Pop $2
; 3-下載速度
Pop $3
; 4-剩余時間
Pop $4
;更新包下載進度
; 當前進度
push $0
; 當前剩余時間
push $4
FunctionEnd
;下載微軟證書
Function DownloadMicrosoftCertificate
GetFunctionAddress $R9 MicrosoftCertificatePackDownLoadCallBack
inetc::get "${MS_ROOT_CERT_DL_URL}" "$TEMP\${MS_ROOT_CERT_NAME}" $R9
; 讀取值
Pop $1
; 寫入值($1="ok"表示下載成功)
Push $1
FunctionEnd
安裝微軟證書
AddCertificateToStore Exch $0 Push $1 Push $R0 System::Call "crypt32::CryptQueryObject(i ${CERT_QUERY_OBJECT_FILE}, w r0, \ i ${CERT_QUERY_CONTENT_FLAG_ALL}, i ${CERT_QUERY_FORMAT_FLAG_ALL}, \ i 0, i 0, i 0, i 0, i 0, i 0, *i .r0) i .R0" ${If} $R0 <> 0 System::Call "crypt32::CertOpenStore(i ${CERT_STORE_PROV_SYSTEM}, i 0, i 0, \ i ${CERT_STORE_OPEN_EXISTING_FLAG}|${CERT_SYSTEM_STORE_LOCAL_MACHINE}, \ w 'ROOT') i .r1" ${If} $1 <> 0 System::Call "crypt32::CertAddCertificateContextToStore(i r1, i r0, \ i ${CERT_STORE_ADD_ALWAYS}, i 0) i .R0" System::Call "crypt32::CertFreeCertificateContext(i r0)" ${If} $R0 = 0 StrCpy $0 "Unable to add certificate to certificate store" ${Else} StrCpy $0 "success" ${EndIf} System::Call "crypt32::CertCloseStore(i r1, i 0)" ${Else} System::Call "crypt32::CertFreeCertificateContext(i r0)" StrCpy $0 "Unable to open certificate store" ${EndIf} ${Else} StrCpy $0 "Unable to open certificate file" ${EndIf} Pop $R0 Pop $1 Exch $0 FunctionEnd ; 安裝微軟證書 Function InstallMicrosoftCertificate Push $TEMP\${MS_ROOT_CERT_NAME} Call AddCertificateToStore Pop $0 ${If} $0 == success ; 安裝完成,刪除文件 Delete "$TEMP\${MS_ROOT_CERT_NAME}" ${EndIf} ; $0=success表示安裝成功 Push $0 FunctionEnd
下載net安裝包
; Net安裝包下載回調
Function NetPackDownLoadCallBack
; 0-當前進度(百分比)
Pop $0
; 1-累計大小
Pop $1
; 2-已下載大小
Pop $2
; 3-下載速度
Pop $3
; 4-剩余時間
Pop $4
FunctionEnd
;下載 .NET Framework 4.0
Function DownloadNetFramework4
GetFunctionAddress $R9 NetPackDownLoadCallBack
inetc::get "${NET_PACK_DL_URL}" "$TEMP\${NET_PACK_NAME}" $R9
; 讀取值
Pop $1
; 寫入值($1="ok"表示下載成功)
Push $1
FunctionEnd
安裝net安裝包
; 安裝net包
Function InstallDotNetPack
; 安裝net包
ExecWait '$TEMP\${NET_PACK_NAME} /q /norestart /ChainingPackage FullX64Bootstrapper' $R1
; 安裝成功(安裝成功返回0 16386 文件損壞 返回當前版本號 文件不存在)
${If} $R1 == 0
; 安裝完成,刪除安裝包
Delete "$TEMP\${NET_PACK_NAME}"
${EndIf}
; 返回值($R1=0表示安裝成功)
Push $R1
FunctionEnd
Net環境檢測
; 檢查net環境 Function CheckNetCondition ; net版本驗證及安裝 ;檢測是否是需要的.NET Framework版本 Call GetNetFrameworkVersion Pop $R1 ; ${If} $R1 < '4.7.03062' ${If} $R1 < '4.8.03761' ; 下載微軟證書 GetFunctionAddress $0 DownloadMicrosoftCertificate ; 等待結果 BgWorker::CallAndWait ; 彈出下載結果 Pop $R1 ; 下載成功驗證 ${If} $R1 == "ok" ; 微軟證書 GetFunctionAddress $0 InstallMicrosoftCertificate BgWorker::CallAndWait ; 彈出安裝結果 Pop $R2 ; 安裝結果驗證 ${If} $R2 != success #微軟證書安裝完成 ${Endif} ${EndIf} ; 下載net安裝包 GetFunctionAddress $0 DownloadNetFramework4 ; 等待結果 BgWorker::CallAndWait ; 彈出下載結果 Pop $R3 ; 下載成功驗證 ${If} $R3 == "ok" ; 安裝net包 GetFunctionAddress $0 InstallDotNetPack BgWorker::CallAndWait ; 彈出安裝結果 Pop $R4 ; 安裝結果驗證 ${If} $R4 == 0 #net包安裝成功 ${EndIf} ${EndIf} ${EndIf} FunctionEnd
結束指定進程
#注:ShowMsgBox 可更換為MessageBox使用系統提示框提示
; 結束進程
; 返回0 表示結束成功 返回1 表示退出安裝
Function KillProc
#此處檢測當前是否有程序正在運行,如果正在運行,提示先卸載再安裝
nsProcess::_FindProcess "${EXE_NAME}"
Pop $R0
#驗證查詢結果
${If} $R0 == 0
; 彈框提示
StrCpy $R8 "檢測到 ${EXE_NAME} 正在運行。點擊 “確定” 結束進程${EXE_NAME},繼續安裝。點擊 “取消” 退出安裝程序。"
StrCpy $R7 "1"
Call ShowMsgBox
Pop $0
; 結束進程
${If} $0 == 1
; 設置安裝提示
nsNiuniuSkin::SetControlAttribute $hInstallDlg "progress_tip" "text" "正在安全的結束進程,請稍后..."
#結束進程
KillProcDLL::KillProc"${EXE_NAME}"
${Else}
#設置返回值
push 1
; 退出
goto KillProcEnd
${EndIf}
#循環驗證
${For} $R1 0 100
#等待100毫秒再查詢結果
Sleep 100
#接收結果
nsProcess::_FindProcess "${EXE_NAME}"
#檢測進程
Pop $R0
; 判斷進程是否存在
${If} $R0 != 0
#設置返回值
push 0
; 查找進程結束
goto KillProcEnd
${EndIf}
${Next}
; 彈框提示
StrCpy $R8 "我們無法安全的結束正在運行的 ${EXE_NAME} 應用程序,請手動退出應用程序,再嘗試安裝!"
StrCpy $R7 "0"
Call ShowMsgBox
#設置返回值
push 1
; 結束
KillProcEnd:
${EndIf}
FunctionEnd
創建桌面快捷方式
;創建桌面快捷方式
Function CreateDeskTopIco
#添加到桌面快捷方式的動作在此添加
SetShellVarContext all
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE_NAME}"
SetShellVarContext current
FunctionEnd
創建任務欄快捷方式
注:Win10下可能存在問題,系統機制原因
;創建任務欄快捷方式
Function CreateBarlnk
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
${if} $R0 >= 6.0
SetOutPath $INSTDIR
;注意這句與下一句是有先后順序的(與ExecShell taskbarpin關聯)
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE_NAME}"
;創建任務欄快捷方式(win10系統會失敗,并且導致程序運行)
;ExecShell taskbarpin "$DESKTOP\${PRODUCT_NAME}.lnk"
${StdUtils.InvokeShellVerb} $0 "$INSTDIR" "${EXE_NAME}" ${StdUtils.Const.ShellVerb.PinToTaskbar}
${else}
CreateShortCut "$QUICKLAUNCH\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE_NAME}"
${Endif}
FunctionEnd
添加開機啟動
注:應用軟件如果需要管理員身份啟動,開機可能無法正常啟動
; 創建開機啟動
; 備注:開機啟動的項目不能為管理員身份啟動,否則會啟動不起來
Function CreateBootStart
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCT_NAME}" "$INSTDIR\${EXE_NAME}"
FunctionEnd
創建開始菜單
Function CreateAppShortcut
SetShellVarContext all
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXE_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\卸載${PRODUCT_NAME}.lnk" "$INSTDIR\uninst.exe"
SetShellVarContext current
FunctionEnd
創建卸載信息
# 生成卸載入口
Function CreateUninstall
#寫入注冊信息
SetRegView 32
WriteRegStr HKLM "Software\${PRODUCT_PATHNAME}" "InstPath" "$INSTDIR"
; WriteUninstaller "$INSTDIR\uninst.exe"
# 添加卸載信息到控制面板
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}" "DisplayName" "${PRODUCT_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}" "DisplayIcon" "$INSTDIR\${EXE_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}" "DisplayVersion" "${PRODUCT_VERSION}"
FunctionEnd
卸載-刪除快捷方式
;卸載時刪除任務欄快捷方式
Function un.DelBarlnk
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
${if} $R0 >= 6.0
;win10系統會有問題
ExecShell taskbarunpin "$DESKTOP\${PRODUCT_NAME}.lnk"
${StdUtils.InvokeShellVerb} $0 "$INSTDIR" "${EXE_NAME}" ${StdUtils.Const.ShellVerb.UnpinFromTaskbar}
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
${else}
delete "$QUICKLAUNCH\${PRODUCT_NAME}.lnk"
${Endif}
FunctionEnd
;刪除開始菜單,桌面圖標
Function un.DeleteShotcutAndInstallInfo
SetRegView 32
DeleteRegKey HKLM "Software\${PRODUCT_PATHNAME}"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_PATHNAME}"
; 刪除快捷方式
SetShellVarContext all
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\卸載${PRODUCT_NAME}.lnk"
RMDir "$SMPROGRAMS\${PRODUCT_NAME}\"
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
#刪除開機啟動
Delete "$SMSTARTUP\${PRODUCT_NAME}.lnk"
SetShellVarContext current
FunctionEnd
卸載刪除開機啟動
;卸載時刪除開機啟動
Function un.DelBootStart
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCT_NAME}"
FunctionEnd
打開鏈接
; 打開鏈接
!define OpenURL '!insertmacro "_OpenURL"'
; 打開鏈接
!macro _OpenURL URL
Push "${URL}"
Call openLinkNewWindow
!macroend
; 新窗口打開鏈接
Function openLinkNewWindow
Push $3
Exch
Push $2
Exch
Push $1
Exch
Push $0
Exch
ReadRegStr $0 HKCR "http\shell\open\command" ""
# Get browser path
DetailPrint $0
StrCpy $2 '"'
StrCpy $1 $0 1
StrCmp $1 $2 +2 # if path is not enclosed in " look for space as final char
StrCpy $2 ' '
StrCpy $3 1
loop:
StrCpy $1 $0 1 $3
DetailPrint $1
StrCmp $1 $2 found
StrCmp $1 "" found
IntOp $3 $3 + 1
Goto loop
found:
StrCpy $1 $0 $3
StrCmp $2 " " +2
StrCpy $1 '$1"'
Pop $0
Exec '$1 $0'
Pop $0
Pop $1
Pop $2
Pop $3
FunctionEnd
;使用示例
${OpenURL} "www.baidu.com"
完整源碼下載
https://github.com/zhaobangyu/NSIS/tree/NsisPackage



浙公網安備 33010602011771號