165 lines
5.1 KiB
NSIS
165 lines
5.1 KiB
NSIS
|
|
!ifndef nsProcess::FindProcess
|
||
|
|
!include "nsProcess.nsh"
|
||
|
|
!endif
|
||
|
|
|
||
|
|
!ifmacrondef customCheckAppRunning
|
||
|
|
!include "getProcessInfo.nsh"
|
||
|
|
Var pid
|
||
|
|
!endif
|
||
|
|
|
||
|
|
# http://nsis.sourceforge.net/Allow_only_one_installer_instance
|
||
|
|
!macro ALLOW_ONLY_ONE_INSTALLER_INSTANCE
|
||
|
|
BringToFront
|
||
|
|
!define /ifndef SYSTYPE_PTR p ; NSIS v3.0+
|
||
|
|
System::Call 'kernel32::CreateMutex(${SYSTYPE_PTR}0, i1, t"${APP_GUID}")?e'
|
||
|
|
Pop $0
|
||
|
|
IntCmpU $0 183 0 launch launch ; ERROR_ALREADY_EXISTS
|
||
|
|
StrLen $0 "$(^SetupCaption)"
|
||
|
|
IntOp $0 $0 + 1 ; GetWindowText count includes \0
|
||
|
|
StrCpy $1 "" ; Start FindWindow with NULL
|
||
|
|
loop:
|
||
|
|
FindWindow $1 "#32770" "" "" $1
|
||
|
|
StrCmp 0 $1 notfound
|
||
|
|
System::Call 'user32::GetWindowText(${SYSTYPE_PTR}r1, t.r2, ir0)'
|
||
|
|
StrCmp $2 "$(^SetupCaption)" 0 loop
|
||
|
|
SendMessage $1 0x112 0xF120 0 /TIMEOUT=2000 ; WM_SYSCOMMAND:SC_RESTORE to restore the window if it is minimized
|
||
|
|
System::Call "user32::SetForegroundWindow(${SYSTYPE_PTR}r1)"
|
||
|
|
notfound:
|
||
|
|
Abort
|
||
|
|
launch:
|
||
|
|
!macroend
|
||
|
|
|
||
|
|
!macro CHECK_APP_RUNNING
|
||
|
|
Var /GLOBAL CmdPath
|
||
|
|
Var /GLOBAL FindPath
|
||
|
|
Var /GLOBAL PowerShellPath
|
||
|
|
StrCpy $CmdPath "$SYSDIR\cmd.exe"
|
||
|
|
StrCpy $FindPath "$SYSDIR\find.exe"
|
||
|
|
StrCpy $PowerShellPath "$SYSDIR\WindowsPowerShell\v1.0\powershell.exe"
|
||
|
|
!ifmacrodef customCheckAppRunning
|
||
|
|
!insertmacro customCheckAppRunning
|
||
|
|
!else
|
||
|
|
!insertmacro IS_POWERSHELL_AVAILABLE
|
||
|
|
!insertmacro _CHECK_APP_RUNNING
|
||
|
|
!endif
|
||
|
|
!macroend
|
||
|
|
|
||
|
|
!macro IS_POWERSHELL_AVAILABLE
|
||
|
|
Var /GLOBAL IsPowerShellAvailable ; 0 = available, 1 = not available
|
||
|
|
# Try running PowerShell with a simple command to check if it's available
|
||
|
|
nsExec::Exec `"$PowerShellPath" -C "if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }"`
|
||
|
|
Pop $0 # Return code (0 = success, other = error)
|
||
|
|
|
||
|
|
${if} $0 == 0
|
||
|
|
# PowerShell is available, check if it's not blocked by policies
|
||
|
|
nsExec::Exec `"$PowerShellPath" -C "if ((Get-ExecutionPolicy -Scope Process) -eq 'Restricted') { exit 1 } else { exit 0 }"`
|
||
|
|
Pop $0
|
||
|
|
${endIf}
|
||
|
|
|
||
|
|
${if} $0 != 0
|
||
|
|
StrCpy $0 1
|
||
|
|
${endIf}
|
||
|
|
|
||
|
|
StrCpy $IsPowerShellAvailable $0
|
||
|
|
!macroend
|
||
|
|
|
||
|
|
!macro FIND_PROCESS _FILE _RETURN
|
||
|
|
${if} $IsPowerShellAvailable == 0
|
||
|
|
nsExec::Exec `"$PowerShellPath" -C "if ((Get-CimInstance -ClassName Win32_Process | ? {$$_.Path -and $$_.Path.StartsWith('$INSTDIR', 'CurrentCultureIgnoreCase')}).Count -gt 0) { exit 0 } else { exit 1 }"`
|
||
|
|
Pop ${_RETURN}
|
||
|
|
${else}
|
||
|
|
!ifdef INSTALL_MODE_PER_ALL_USERS
|
||
|
|
${nsProcess::FindProcess} "${_FILE}" ${_RETURN}
|
||
|
|
!else
|
||
|
|
# find process owned by current user
|
||
|
|
nsExec::Exec `"$CmdPath" /C tasklist /FI "USERNAME eq %USERNAME%" /FI "IMAGENAME eq ${_FILE}" /FO CSV | "$FindPath" "${_FILE}"`
|
||
|
|
Pop ${_RETURN}
|
||
|
|
!endif
|
||
|
|
${endIf}
|
||
|
|
!macroend
|
||
|
|
|
||
|
|
!macro KILL_PROCESS _FILE _FORCE
|
||
|
|
Push $0
|
||
|
|
${if} ${_FORCE} == 1
|
||
|
|
${if} $IsPowerShellAvailable == 0
|
||
|
|
StrCpy $0 "-Force"
|
||
|
|
${else}
|
||
|
|
StrCpy $0 "/F"
|
||
|
|
${endIf}
|
||
|
|
${else}
|
||
|
|
StrCpy $0 ""
|
||
|
|
${endIf}
|
||
|
|
|
||
|
|
${if} $IsPowerShellAvailable == 0
|
||
|
|
nsExec::Exec `"$PowerShellPath" -C "Get-CimInstance -ClassName Win32_Process | ? {$$_.Path -and $$_.Path.StartsWith('$INSTDIR', 'CurrentCultureIgnoreCase')} | % { Stop-Process -Id $$_.ProcessId $0 }"`
|
||
|
|
${else}
|
||
|
|
!ifdef INSTALL_MODE_PER_ALL_USERS
|
||
|
|
nsExec::Exec `taskkill /IM "${_FILE}" /FI "PID ne $pid"`
|
||
|
|
!else
|
||
|
|
nsExec::Exec `"$CmdPath" /C taskkill $0 /IM "${_FILE}" /FI "PID ne $pid" /FI "USERNAME eq %USERNAME%"`
|
||
|
|
!endif
|
||
|
|
${endIf}
|
||
|
|
Pop $0
|
||
|
|
!macroend
|
||
|
|
|
||
|
|
!macro _CHECK_APP_RUNNING
|
||
|
|
${GetProcessInfo} 0 $pid $1 $2 $3 $4
|
||
|
|
${if} $3 != "${APP_EXECUTABLE_FILENAME}"
|
||
|
|
${if} ${isUpdated}
|
||
|
|
# allow app to exit without explicit kill
|
||
|
|
Sleep 300
|
||
|
|
${endIf}
|
||
|
|
|
||
|
|
!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
|
||
|
|
${if} $R0 == 0
|
||
|
|
${if} ${isUpdated}
|
||
|
|
# allow app to exit without explicit kill
|
||
|
|
Sleep 1000
|
||
|
|
Goto doStopProcess
|
||
|
|
${endIf}
|
||
|
|
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "$(appRunning)" /SD IDOK IDOK doStopProcess
|
||
|
|
Quit
|
||
|
|
|
||
|
|
doStopProcess:
|
||
|
|
|
||
|
|
DetailPrint "$(appClosing)"
|
||
|
|
|
||
|
|
!insertmacro KILL_PROCESS "${APP_EXECUTABLE_FILENAME}" 0
|
||
|
|
# to ensure that files are not "in-use"
|
||
|
|
Sleep 300
|
||
|
|
|
||
|
|
# Retry counter
|
||
|
|
StrCpy $R1 0
|
||
|
|
|
||
|
|
loop:
|
||
|
|
IntOp $R1 $R1 + 1
|
||
|
|
|
||
|
|
!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
|
||
|
|
${if} $R0 == 0
|
||
|
|
# wait to give a chance to exit gracefully
|
||
|
|
Sleep 1000
|
||
|
|
!insertmacro KILL_PROCESS "${APP_EXECUTABLE_FILENAME}" 1 # 1 = force kill
|
||
|
|
!insertmacro FIND_PROCESS "${APP_EXECUTABLE_FILENAME}" $R0
|
||
|
|
${if} $R0 == 0
|
||
|
|
DetailPrint `Waiting for "${PRODUCT_NAME}" to close.`
|
||
|
|
Sleep 2000
|
||
|
|
${else}
|
||
|
|
Goto not_running
|
||
|
|
${endIf}
|
||
|
|
${else}
|
||
|
|
Goto not_running
|
||
|
|
${endIf}
|
||
|
|
|
||
|
|
# App likely running with elevated permissions.
|
||
|
|
# Ask user to close it manually
|
||
|
|
${if} $R1 > 1
|
||
|
|
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "$(appCannotBeClosed)" /SD IDCANCEL IDRETRY loop
|
||
|
|
Quit
|
||
|
|
${else}
|
||
|
|
Goto loop
|
||
|
|
${endIf}
|
||
|
|
not_running:
|
||
|
|
${endIf}
|
||
|
|
${endIf}
|
||
|
|
!macroend
|