Eigener Installer mit Standard- und Portable-Modus, Updater fuer beide Varianten

This commit is contained in:
Davito committed 2026-10-14 10:34:48 +02:00
1 parent df88f8abaa
commit 92d02d0008
6 files changed
+508 -36

No files matched your search

+387
View File
@@ -0,0 +1,387 @@
; EuerChat-Installer
; Standard (Program Files, alle Benutzer) oder Portable (frei waehlbarer Ordner).
; Parameter, auch fuer stille Updates: /S /UPDATE /MODE=standard|portable /NS /D=<Ordner> (/D= immer zuletzt)
Unicode true
ManifestDPIAware true
SetCompressor /SOLID lzma
RequestExecutionLevel user
!ifndef VERSION
!define VERSION "0.0.0"
!endif
!ifndef EXE_SRC
!define EXE_SRC "..\src-tauri\target\release\euer-chat.exe"
!endif
!ifndef OUT_FILE
!define OUT_FILE "..\dist\EuerChat_${VERSION}_x64-setup.exe"
!endif
!define PRODUCT "EuerChat"
!define PUBLISHER "euer.tv"
!define MAINEXE "euer-chat.exe"
!define BUNDLEID "tv.euer.chat"
!define WEBVIEW2APPGUID "{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"
!define PLACEHOLDER_DIR "placeholder\${PRODUCT}"
; TEST_USER_CONTEXT: Testbuild ohne Administratorrechte (HKCU, eigener Registry-Schluessel).
!ifdef TEST_USER_CONTEXT
!define ROOT HKCU
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT}-Test"
!else
!define ROOT HKLM
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT}"
!endif
Name "${PRODUCT}"
OutFile "${OUT_FILE}"
BrandingText "${PRODUCT} ${VERSION}"
InstallDir "${PLACEHOLDER_DIR}"
VIProductVersion "${VERSION}.0"
VIAddVersionKey "ProductName" "${PRODUCT}"
VIAddVersionKey "FileDescription" "${PRODUCT} Installer"
VIAddVersionKey "FileVersion" "${VERSION}"
VIAddVersionKey "ProductVersion" "${VERSION}"
VIAddVersionKey "CompanyName" "${PUBLISHER}"
VIAddVersionKey "LegalCopyright" "${PUBLISHER}"
!include MUI2.nsh
!include FileFunc.nsh
!include LogicLib.nsh
!include nsDialogs.nsh
!include x64.nsh
!insertmacro GetParameters
!insertmacro GetOptions
!insertmacro un.GetParameters
!insertmacro un.GetOptions
Var Portable
Var UpdateMode
Var NoShortcut
Var Elevated
Var ModeGiven
Var RadioStandard
Var RadioPortable
!define MUI_ICON "..\src-tauri\icons\icon.ico"
!define MUI_UNICON "..\src-tauri\icons\icon.ico"
!define MUI_ABORTWARNING
; Seiten
!define MUI_PAGE_CUSTOMFUNCTION_PRE WelcomePre
!insertmacro MUI_PAGE_WELCOME
Page custom ModePageCreate ModePageLeave
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_TEXT "${PRODUCT} jetzt starten"
!define MUI_FINISHPAGE_RUN_FUNCTION RunMainBinary
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "German"
; ---------------------------------------------------------------------------
; Hilfsfunktionen
; ---------------------------------------------------------------------------
Function SetDefaultDir
${If} $Portable = 1
StrCpy $INSTDIR "$EXEDIR\${PRODUCT}"
${Else}
ReadRegStr $0 ${ROOT} "${UNINSTKEY}" "InstallLocation"
${If} $0 != ""
StrCpy $INSTDIR $0
${Else}
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCT}"
${EndIf}
${EndIf}
FunctionEnd
; Startet den Installer bei Bedarf mit Administratorrechten neu (nur fuer die Standard-Installation).
Function RequireAdmin
!ifndef TEST_USER_CONTEXT
System::Call 'shell32::IsUserAnAdmin()i.r0'
${If} $0 = 0
${If} $Elevated = 1
${IfNot} ${Silent}
MessageBox MB_ICONSTOP "Für die Standard-Installation sind Administratorrechte nötig."
${EndIf}
Abort
${EndIf}
StrCpy $1 "/MODE=standard /ELEVATED"
${If} ${Silent}
StrCpy $1 "$1 /S"
${EndIf}
${If} $UpdateMode = 1
StrCpy $1 "$1 /UPDATE"
${EndIf}
${If} $NoShortcut = 1
StrCpy $1 "$1 /NS"
${EndIf}
StrCpy $1 "$1 /D=$INSTDIR"
System::Call 'shell32::ShellExecute(i 0, t "runas", t "$EXEPATH", t "$1", t "", i 1) i .r2'
${If} $2 > 32
Quit
${EndIf}
${IfNot} ${Silent}
MessageBox MB_ICONEXCLAMATION "Ohne Administratorrechte kann die Standard-Installation nicht fortgesetzt werden. Wähle stattdessen „Portable“."
${EndIf}
Abort
${EndIf}
!endif
FunctionEnd
Function CloseRunningApp
nsExec::ExecToStack 'cmd /c tasklist /FI "IMAGENAME eq ${MAINEXE}" /NH | find /I "${MAINEXE}"'
Pop $0
Pop $1
${If} $0 = 0
${IfNot} ${Silent}
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "${PRODUCT} läuft noch und wird für die Installation beendet." IDOK +2
Abort
${EndIf}
nsExec::ExecToStack 'taskkill /F /IM "${MAINEXE}"'
Pop $0
Pop $1
Sleep 800
${EndIf}
FunctionEnd
Function RunMainBinary
; Ueber den Explorer starten, damit die App ohne Administratorrechte laeuft.
Exec '"$WINDIR\explorer.exe" "$INSTDIR\${MAINEXE}"'
FunctionEnd
; ---------------------------------------------------------------------------
; Installer
; ---------------------------------------------------------------------------
Function .onInit
${If} ${RunningX64}
SetRegView 64
${EndIf}
${GetParameters} $R9
${GetOptions} $R9 "/UPDATE" $0
${IfNot} ${Errors}
StrCpy $UpdateMode 1
${EndIf}
${GetOptions} $R9 "/NS" $0
${IfNot} ${Errors}
StrCpy $NoShortcut 1
${EndIf}
${GetOptions} $R9 "/ELEVATED" $0
${IfNot} ${Errors}
StrCpy $Elevated 1
${EndIf}
${GetOptions} $R9 "/MODE=" $0
${IfNot} ${Errors}
StrCpy $ModeGiven 1
${If} $0 == "portable"
StrCpy $Portable 1
${EndIf}
${EndIf}
; Stille Installation ohne Angabe: Standard
${If} ${Silent}
StrCpy $ModeGiven 1
${EndIf}
${If} $INSTDIR == "${PLACEHOLDER_DIR}"
Call SetDefaultDir
${EndIf}
${If} $ModeGiven = 1
${AndIf} $Portable <> 1
Call RequireAdmin
${EndIf}
FunctionEnd
Function WelcomePre
${If} $Elevated = 1
Abort
${EndIf}
FunctionEnd
Function ModePageCreate
${If} $ModeGiven = 1
Abort
${EndIf}
!insertmacro MUI_HEADER_TEXT "Installationsart" "Wie soll ${PRODUCT} installiert werden?"
nsDialogs::Create 1018
Pop $0
${NSD_CreateRadioButton} 0 6u 100% 12u "Standard-Installation (empfohlen)"
Pop $RadioStandard
${NSD_AddStyle} $RadioStandard ${WS_GROUP}
${NSD_CreateLabel} 12u 20u 94% 38u "Installiert ${PRODUCT} für alle Benutzer, standardmäßig nach C:\Program Files. Mit Startmenü- und Desktop-Verknüpfung; deinstallierbar über die Windows-Einstellungen. Erfordert Administratorrechte."
Pop $0
${NSD_CreateRadioButton} 0 66u 100% 12u "Portable (Ordner frei wählbar)"
Pop $RadioPortable
${NSD_CreateLabel} 12u 80u 94% 46u "Kopiert ${PRODUCT} nur in einen Ordner deiner Wahl, z. B. auf einen USB-Stick. Keine Administratorrechte und keine Einträge in der Registry. Die Einstellungen liegen im Unterordner „data“. Zum Entfernen genügt es, den Ordner zu löschen."
Pop $0
${If} $Portable = 1
${NSD_Check} $RadioPortable
${Else}
${NSD_Check} $RadioStandard
${EndIf}
nsDialogs::Show
FunctionEnd
Function ModePageLeave
${NSD_GetState} $RadioPortable $0
${If} $0 = ${BST_CHECKED}
StrCpy $Portable 1
${Else}
StrCpy $Portable 0
${EndIf}
Call SetDefaultDir
${If} $Portable = 0
Call RequireAdmin
${EndIf}
FunctionEnd
Section "WebView2"
${If} $UpdateMode <> 1
ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
${If} $4 == ""
ReadRegStr $4 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
${EndIf}
${If} $4 == ""
DetailPrint "Microsoft Edge WebView2 wird heruntergeladen ..."
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
NSISdl::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Pop $0
${If} $0 != "success"
Abort "WebView2 konnte nicht heruntergeladen werden. Bitte die Internetverbindung prüfen und erneut versuchen."
${EndIf}
DetailPrint "Microsoft Edge WebView2 wird installiert ..."
ExecWait '"$TEMP\MicrosoftEdgeWebview2Setup.exe" /silent /install' $1
${If} $1 <> 0
Abort "Die Installation von WebView2 ist fehlgeschlagen."
${EndIf}
${EndIf}
${EndIf}
SectionEnd
Section "Install"
SetOutPath $INSTDIR
Call CloseRunningApp
File "${EXE_SRC}"
${If} $Portable = 1
; Die Marker-Datei schaltet den portablen Datenordner der App frei.
FileOpen $0 "$INSTDIR\portable.flag" w
FileWrite $0 "${PRODUCT} portable: Einstellungen liegen im Ordner data.$\r$\n"
FileClose $0
CreateDirectory "$INSTDIR\data"
${Else}
Delete "$INSTDIR\portable.flag"
WriteUninstaller "$INSTDIR\uninstall.exe"
!ifdef TEST_USER_CONTEXT
SetShellVarContext current
!else
SetShellVarContext all
!endif
WriteRegStr ${ROOT} "${UNINSTKEY}" "DisplayName" "${PRODUCT}"
WriteRegStr ${ROOT} "${UNINSTKEY}" "DisplayVersion" "${VERSION}"
WriteRegStr ${ROOT} "${UNINSTKEY}" "Publisher" "${PUBLISHER}"
WriteRegStr ${ROOT} "${UNINSTKEY}" "InstallLocation" "$INSTDIR"
WriteRegStr ${ROOT} "${UNINSTKEY}" "DisplayIcon" "$INSTDIR\${MAINEXE}"
WriteRegStr ${ROOT} "${UNINSTKEY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD ${ROOT} "${UNINSTKEY}" "NoModify" 1
WriteRegDWORD ${ROOT} "${UNINSTKEY}" "NoRepair" 1
WriteRegDWORD ${ROOT} "${UNINSTKEY}" "EstimatedSize" 7600
CreateShortcut "$SMPROGRAMS\${PRODUCT}.lnk" "$INSTDIR\${MAINEXE}"
${If} $UpdateMode <> 1
${AndIf} $NoShortcut <> 1
CreateShortcut "$DESKTOP\${PRODUCT}.lnk" "$INSTDIR\${MAINEXE}"
${EndIf}
${EndIf}
SectionEnd
; ---------------------------------------------------------------------------
; Deinstallation (nur Standard-Installation)
; ---------------------------------------------------------------------------
Function un.onInit
${If} ${RunningX64}
SetRegView 64
${EndIf}
${GetParameters} $R9
${GetOptions} $R9 "/ELEVATED" $0
${IfNot} ${Errors}
StrCpy $Elevated 1
${EndIf}
${GetOptions} $R9 "/UPDATE" $0
${IfNot} ${Errors}
StrCpy $UpdateMode 1
${EndIf}
!ifndef TEST_USER_CONTEXT
System::Call 'shell32::IsUserAnAdmin()i.r0'
${If} $0 = 0
${If} $Elevated = 1
Abort
${EndIf}
StrCpy $1 "/ELEVATED"
${If} ${Silent}
StrCpy $1 "$1 /S"
${EndIf}
${If} $UpdateMode = 1
StrCpy $1 "$1 /UPDATE"
${EndIf}
StrCpy $1 "$1 _?=$INSTDIR"
System::Call 'shell32::ShellExecute(i 0, t "runas", t "$EXEPATH", t "$1", t "", i 1) i .r2'
${If} $2 > 32
Quit
${EndIf}
Abort
${EndIf}
!endif
FunctionEnd
Section "Uninstall"
!ifdef TEST_USER_CONTEXT
SetShellVarContext current
!else
SetShellVarContext all
!endif
nsExec::ExecToStack 'taskkill /F /IM "${MAINEXE}"'
Pop $0
Pop $1
Sleep 500
Delete "$INSTDIR\${MAINEXE}"
Delete "$INSTDIR\uninstall.exe"
RMDir "$INSTDIR"
Delete "$SMPROGRAMS\${PRODUCT}.lnk"
Delete "$DESKTOP\${PRODUCT}.lnk"
DeleteRegKey ${ROOT} "${UNINSTKEY}"
${If} $UpdateMode <> 1
${AndIfNot} ${Silent}
MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Sollen auch die Einstellungen von ${PRODUCT} (Konfiguration und gespeicherte Passwörter) gelöscht werden?" IDNO skip_data
SetShellVarContext current
RMDir /r "$APPDATA\${PRODUCT}"
RMDir /r "$LOCALAPPDATA\${BUNDLEID}"
skip_data:
${EndIf}
SectionEnd