Files
dinlo 436a9631fc Initial commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 18:44:04 +08:00

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