NSIS实现安装前检测是否安装程序,程序是否运行,安装后关联程序默认打开方式,刷新文件图标

本文介绍了如何使用nullsoft NSIS来创建C#程序的安装包,包括安装前检查程序是否运行,检测并卸载旧版本,以及安装后关联程序默认打开方式和刷新文件图标。作者分享了遇到的困难和解决方法,并提供了相关脚本资源。

这段时间在公司研究C#程序的安装包尝试了vs自带的setup项目,效果不尽如意跟直接压缩没太大区别。老大让我试试用nullsoft做着试试…在这期间踩了好多好多坑…主要是网上找到的nullsoft教程都不太适合初学者。整理发布下我拼凑的这篇脚本。本人研究有限,仅为了项目开发而研究。高手勿喷。

正文开始,首先是nullsoft的官网,可以在下载页下载nsis。nsis仅是一个编译器在这里插入图片描述
点击compile nsi scripts后把.nsi脚本文件扔进去就可以编译了。
这里是第一个坑…尼玛我才懒得学脚本语言。那好,再下载一个HM NIS Edit,其实嘛脚本文件拿什么文件编辑器打开都可以,但是这个HM NIS Edit自带向导,这就很友好了。这里提醒跟我一样c#的初学者一下打包的文件除了一些.txt文件和.pdb文件外几乎都是需要的…然后可以把需要打包的文件夹(release)先复制一份丢到其他文件夹中像这样在这里插入图片描述
后面跟着向导弄完基本你的安装包就能正常工作了…
然后头疼的地方来了,定制化安装包,实现检测安装时是否在运行本软件,若运行则弹窗终止进程。检测是否安装旧版本,若安装则卸载旧版本。安装后自动关联文件默认打开方式,刷新文件图标。
还是那句话懒得学脚本语言。就白嫖呗。东拼西凑的可以用,有什么需要修改的自己看着办吧。上代码。

; Script generated by the HM NIS Edit Script Wizard.
!include "WordFunc.nsh"
!include "FileAssociation.nsh"
!include "FileFunc.nsh"
!include "nsProcess.nsh"
!include LogicLib.nsh
; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "你的产品名字"
!define PRODUCT_VERSION "版本号"
!define PRODUCT_PUBLISHER "公司名"
!define PRODUCT_WEB_SITE "公司网址"
;定义注册表方便验证是否已安装,以及安装的版本
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\你的软件名.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
;你产品的唯一标识号
!define UninstId "{BDB7DC98-77FE-4859-8B49-C66132185719}"

;定义打包方式,lzma追求最小体积
SetCompressor lzma

; MUI 1.67 compatible ------
!include "MUI.nsh"

; MUI Settings
!define MUI_ABORTWARNING
;定义安装程序图标
!define MUI_ICON "Install2.ico"
;定义卸载程序图标
!define MUI_UNICON "uninstall.ico"

; Language Selection Dialog Settings
!define MUI_LANGDLL_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_LANGDLL_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_LANGDLL_REGISTRY_VALUENAME "NSIS:Language"

; Welcome page
!insertmacro MUI_PAGE_WELCOME
; License page
!define MUI_LICENSEPAGE_CHECKBOX
;读取授权文件
!insertmacro MUI_PAGE_LICENSE "D:\repos\你的软件名\bin\x64\打包发布\授权文件.txt"
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\你的软件名.exe"
!insertmacro MUI_PAGE_FINISH

; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES

; Language files
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "SimpChinese"

; Reserve files
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS

; MUI end ------

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "你的软件名.exe"
;不知道咋回事nsis一直默认写在x86下就干脆写死
InstallDir "C:\Program Files\你的产品名"
InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show

;这个必须在调用前(.onit前)定义
!macro UninstallExisting exitcode uninstcommand
Push `${uninstcommand}`
Call UninstallExisting
Pop ${exitcode}
!macroend




Function .onInit
/*开始查找同名进程*/
  nsProcess::_FindProcess "你的软件名.exe"
  Pop $R0
  IntCmp $R0 0 running no_running no_running
  running:
  MessageBox MB_ICONQUESTION|MB_YESNO "安装程序检测到 ${PRODUCT_NAME} 正在运行,是否强行关闭并继续安装?" IDYES dokill IDNO stopit
  no_running:
  GoTo endding
  dokill:
  nsProcess::_CloseProcess "你的软件名.exe"
  Pop $R0
  GoTo endding
  stopit:
  Abort
  endding:
  nsProcess::_Unload
  /*查找同名进程结束*/
  /*开始查找注册表看是否有装旧版本*/
  ReadRegStr $0 HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString"
		${If} $0 != ""
		${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION "检测到有安装旧版本,需要卸载之前的版本吗?" /SD IDYES IDYES`
		!insertmacro UninstallExisting $0 $0
		${If} $0 <> 0
			MessageBox MB_YESNO|MB_ICONSTOP "卸载失败, 继续安装吗?" /SD IDYES IDYES +2
				Abort
		${EndIf}
	${EndIf}
	/*结束查找注册表*/
	;开始安装
  !insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd

;你软件中的文件
Section "MainSection" SEC01
  SetOutPath "$INSTDIR"
  File "D:\repos\你的软件名\bin\x64\打包发布\cef.pak"
SectionEnd

Section -AdditionalIcons
  CreateShortCut "$SMPROGRAMS\你的产品名\Uninstall.lnk" "$INSTDIR\uninst.exe"
SectionEnd

Section -Post
  WriteUninstaller "$INSTDIR\uninst.exe"
  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\你的软件名.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\你的软件名.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
SectionEnd


Function un.onUninstSuccess
  HideWindow
  /*清理注册表文件关联*/
  ${unregisterExtension} ".srp" "你的软件名"
  ${unregisterExtension} ".sdpc" "你的软件名"
  /*清理注册表结束*/
  ;刷新文件图标
  ${RefreshShellIcons}
  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) 已成功地从你的计算机移除。"
FunctionEnd

Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
 MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "你确实要完全移除 $(^Name) ,其及所有的组件?" IDYES +2
  Abort
  ;检测程序是否运行
  nsProcess::_FindProcess "你的软件名.exe"
  Pop $R0
  IntCmp $R0 0 running no_running no_running
  running:
  MessageBox MB_ICONQUESTION|MB_YESNO "安装程序检测到 ${PRODUCT_NAME} 正在运行,是否强行关闭并继续卸载?" IDYES dokill IDNO stopit
  no_running:
  GoTo endding
  dokill:
  nsProcess::_CloseProcess "你的软件名.exe"
  Pop $R0
  GoTo endding
  stopit:
  Abort
  endding:
  nsProcess::_Unload
FunctionEnd


Section Uninstall
  Delete "$INSTDIR\uninst.exe"
  SetAutoClose true
SectionEnd


;添加文件关联
Section -AssociateToSdpcSrp
	${registerExtension} "$INSTDIR\你的软件名.exe" ".srp" "你的软件名"
	${registerExtension} "$INSTDIR\你的软件名.exe" ".sdpc" "你的软件名"
	${RefreshShellIcons}
SectionEnd


;安装之前自动卸载旧版本
Function UninstallExisting
Exch $1 ; uninstcommand
Push $2 ; Uninstaller
Push $3 ; Len
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
	StrCpy $2 $1 1 $3
	IntOp $3 $3 + 1
	StrCmp $2 "" +2
	StrCmp $2 ' ' 0 sloop
	IntOp $3 $3 - 1
	Goto run
qloop:
	StrCmp $3 "" 0 +2
	StrCpy $1 $1 "" 1 ; Remove initial quote
	IntOp $3 $3 + 1
	StrCpy $2 $1 1 $3
	StrCmp $2 "" +2
	StrCmp $2 '"' 0 qloop
run:
	StrCpy $2 $1 $3 ; Path to uninstaller
	StrCpy $1 161 ; ERROR_BAD_PATHNAME
	GetFullPathName $3 "$2\.." ; $InstDir
	IfFileExists "$2" 0 +4
	ExecWait '"$2" /S _?=$3' $1 ; This assumes the existing uninstaller is a NSIS uninstaller, other uninstallers don't support /S nor _?=
	IntCmp $1 0 "" +2 +2 ; Don't delete the installer if it was aborted
	Delete "$2" ; Delete the uninstaller
	RMDir "$3" ; Try to delete $InstDir
	RMDir "$3\.." ; (Optional) Try to delete the parent of $InstDir
Pop $3
Pop $2
Exch $1 ; exitcode
FunctionEnd
Section
SetOutPath $InstDir
WriteUninstaller "$InstDir\uninst.exe"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "DisplayName" "你的软件名"
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "UninstallString" '"$InstDir\uninst.exe"'
WriteRegStr HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}" "QuietUninstallString" '"$InstDir\uninst.exe" /S'
SectionEnd
 
Section Uninstall
Delete "$InstDir\你的软件名.exe"
DeleteRegKey HKCU "Software\Software\Microsoft\Windows\CurrentVersion\Uninstall\${UninstId}"
Delete "$InstDir\uninst.exe"
RMDir "$InstDir"
SectionEnd

一定要用nsprocess才能控制进程,网上说的啥findprocess,killprocess都过时了。nsprocess是需要在官网上下载插件安装的。把头文件丢入include文件夹中,dll丢plugin文件夹。我也不知道放哪个,就每个文件夹扔了一份反正能用就完事了。
然后脚本文件同一目录下扔个nsProcess.nsh和FileAssociation.nsh。
FileAssociation.nsh中代码如下
代码源自nullsoft官网

/*
_____________________________________________________________________________
 
                       File Association
_____________________________________________________________________________
 
 Based on code taken from http://nsis.sourceforge.net/File_Association 
 
 Usage in script:
 1. !include "FileAssociation.nsh"
 2. [Section|Function]
      ${FileAssociationFunction} "Param1" "Param2" "..." $var
    [SectionEnd|FunctionEnd]
 
 FileAssociationFunction=[RegisterExtension|UnRegisterExtension]
 
_____________________________________________________________________________
 
 ${RegisterExtension} "[executable]" "[extension]" "[description]"
 
"[executable]"     ; executable which opens the file format
                   ;
"[extension]"      ; extension, which represents the file format to open
                   ;
"[description]"    ; description for the extension. This will be display in Windows Explorer.
                   ;
 
 
 ${UnRegisterExtension} "[extension]" "[description]"
 
"[extension]"      ; extension, which represents the file format to open
                   ;
"[description]"    ; description for the extension. This will be display in Windows Explorer.
                   ;
 
_____________________________________________________________________________
 
                         Macros
_____________________________________________________________________________
 
 Change log window verbosity (default: 3=no script)
 
 Example:
 !include "FileAssociation.nsh"
 !insertmacro RegisterExtension
 ${FileAssociation_VERBOSE} 4   # all verbosity
 !insertmacro UnRegisterExtension
 ${FileAssociation_VERBOSE} 3   # no script
*/
 
 
!ifndef FileAssociation_INCLUDED
!define FileAssociation_INCLUDED
 
!include Util.nsh
 
!verbose push
!verbose 3
!ifndef _FileAssociation_VERBOSE
  !define _FileAssociation_VERBOSE 3
!endif
!verbose ${_FileAssociation_VERBOSE}
!define FileAssociation_VERBOSE `!insertmacro FileAssociation_VERBOSE`
!verbose pop
 
!macro FileAssociation_VERBOSE _VERBOSE
  !verbose push
  !verbose 3
  !undef _FileAssociation_VERBOSE
  !define _FileAssociation_VERBOSE ${_VERBOSE}
  !verbose pop
!macroend
 
 
 
!macro RegisterExtensionCall _EXECUTABLE _EXTENSION _DESCRIPTION
  !verbose push
  !verbose ${_FileAssociation_VERBOSE}
  Push `${_DESCRIPTION}`
  Push `${_EXTENSION}`
  Push `${_EXECUTABLE}`
  ${CallArtificialFunction} RegisterExtension_
  !verbose pop
!macroend
 
!macro UnRegisterExtensionCall _EXTENSION _DESCRIPTION
  !verbose push
  !verbose ${_FileAssociation_VERBOSE}
  Push `${_EXTENSION}`
  Push `${_DESCRIPTION}`
  ${CallArtificialFunction} UnRegisterExtension_
  !verbose pop
!macroend
 
 
 
!define RegisterExtension `!insertmacro RegisterExtensionCall`
!define un.RegisterExtension `!insertmacro RegisterExtensionCall`
 
!macro RegisterExtension
!macroend
 
!macro un.RegisterExtension
!macroend
 
!macro RegisterExtension_
  !verbose push
  !verbose ${_FileAssociation_VERBOSE}
 
  Exch $R2 ;exe
  Exch
  Exch $R1 ;ext
  Exch
  Exch 2
  Exch $R0 ;desc
  Exch 2
  Push $0
  Push $1
 
  ReadRegStr $1 HKCR $R1 ""  ; read current file association
  StrCmp "$1" "" NoBackup  ; is it empty
  StrCmp "$1" "$R0" NoBackup  ; is it our own
    WriteRegStr HKCR $R1 "backup_val" "$1"  ; backup current value
NoBackup:
  WriteRegStr HKCR $R1 "" "$R0"  ; set our file association
 
  ReadRegStr $0 HKCR $R0 ""
  StrCmp $0 "" 0 Skip
    WriteRegStr HKCR "$R0" "" "$R0"
    WriteRegStr HKCR "$R0\shell" "" "open"
    WriteRegStr HKCR "$R0\DefaultIcon" "" "$R2,0"
Skip:
  WriteRegStr HKCR "$R0\shell\open\command" "" '"$R2" "%1"'
  WriteRegStr HKCR "$R0\shell\edit" "" "Edit $R0"
  WriteRegStr HKCR "$R0\shell\edit\command" "" '"$R2" "%1"'
 
  Pop $1
  Pop $0
  Pop $R2
  Pop $R1
  Pop $R0
 
  !verbose pop
!macroend
 
 
 
!define UnRegisterExtension `!insertmacro UnRegisterExtensionCall`
!define un.UnRegisterExtension `!insertmacro UnRegisterExtensionCall`
 
!macro UnRegisterExtension
!macroend
 
!macro un.UnRegisterExtension
!macroend
 
!macro UnRegisterExtension_
  !verbose push
  !verbose ${_FileAssociation_VERBOSE}
 
  Exch $R1 ;desc
  Exch
  Exch $R0 ;ext
  Exch
  Push $0
  Push $1
 
  ReadRegStr $1 HKCR $R0 ""
  StrCmp $1 $R1 0 NoOwn ; only do this if we own it
  ReadRegStr $1 HKCR $R0 "backup_val"
  StrCmp $1 "" 0 Restore ; if backup="" then delete the whole key
  DeleteRegKey HKCR $R0
  Goto NoOwn
 
Restore:
  WriteRegStr HKCR $R0 "" $1
  DeleteRegValue HKCR $R0 "backup_val"
  DeleteRegKey HKCR $R1 ;Delete key with association name settings
 
NoOwn:
 
  Pop $1
  Pop $0
  Pop $R1
  Pop $R0
 
  !verbose pop
!macroend
 
!endif # !FileAssociation_INCLUDED

感谢nullsoft和网上各位大神,没有大神的辛苦努力哪有我这样小白的幸福生活!是他们让我的工作变得简单。
本人小白一名如果有误欢迎指正。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值