# Windows Toolbox Bootstrapper # Wird unter der Wurzel-URL ausgeliefert und per irm https:// | iex gestartet. # Laedt das komplette Bundle (WinSetup.ps1 + modules + sqlite3.exe) herunter und startet # das Menue. So gibt der Einzeiler den vollen Funktionsumfang, obwohl irm|iex selbst nur # eine Datei holt. # # Neu gegenueber frueher (zwei Aenderungen): # # 1) FESTER Installationsordner statt Zufalls-Temp. # Frueher wurde in %TEMP%\WinSetup_ entpackt - der Pfad war jedes Mal anders, # darum liess sich in Bitdefender keine dauerhafte Ausnahme setzen. Jetzt wird immer nach # %LOCALAPPDATA%\WindowsToolbox # installiert (per-User, kein Admin noetig, stabiler Pfad). Der Ordner wird ganz am Anfang # sichtbar ausgegeben, damit der Nutzer genau weiss, was er in Bitdefender ausnehmen muss. # # 2) Download-Verifikation statt still halb-kaputt. # Frueher lief es bei halbem Download oder wenn der Virenscanner eine Datei wegschnappte # stumm weiter, und man musste "2x ausfuehren". Jetzt wird jede Datei nach dem Download # geprueft (vorhanden UND groesser 0 Byte), einmal automatisch neu geladen, und bei # endgueltigem Fehlschlag klar gemeldet: fehlt das Kernskript, wird hart abgebrochen mit # AV-Ausnahme-Hinweis; fehlen nur Module/sqlite3.exe, wird gewarnt und trotzdem gestartet. $ErrorActionPreference = 'Stop' $BaseUrl = 'https://w.nils.my' # <-- wird vom Server zur Laufzeit ersetzt # Ausfuehrung fuer diesen Prozess freigeben (die geladenen .ps1 tragen sonst die # "Mark-of-the-Web" und werden unter RemoteSigned/AllSigned blockiert). try { Set-ExecutionPolicy -Scope Process Bypass -Force -ErrorAction SilentlyContinue } catch { } # TLS 1.2 erzwingen, sonst schlaegt Invoke-WebRequest gegen HTTPS auf aelteren Systemen fehl. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # FESTER Installationsordner (per-User, stabiler Pfad fuer die AV-Ausnahme). $dir = Join-Path $env:LOCALAPPDATA 'WindowsToolbox' New-Item -ItemType Directory -Path $dir -Force | Out-Null New-Item -ItemType Directory -Path (Join-Path $dir 'modules') -Force | Out-Null Write-Host "" Write-Host " Installationsordner: $dir" -ForegroundColor Cyan Write-Host " (diesen Ordner bei Bedarf in Bitdefender als Ausnahme eintragen)" -ForegroundColor DarkGray Write-Host "" # Download-Helfer: laedt eine Datei und prueft danach, ob sie wirklich da und nicht leer ist. # Bei Fehlschlag EINMAL kurz warten und neu versuchen. Ist die Datei danach immer noch # fehlend oder leer, wird $false zurueckgegeben (Aufrufer sammelt den Namen), NICHT abgebrochen. function Get-BundleFile { param( [string] $RelPath, # z.B. 'WinSetup.ps1' oder 'modules/Crypto.ps1' [string] $TargetDir ) $url = "$BaseUrl/$RelPath" $dst = Join-Path $TargetDir ($RelPath -replace '/', '\') for ($try = 1; $try -le 2; $try++) { try { Invoke-WebRequest -Uri $url -OutFile $dst -UseBasicParsing } catch { # Download-Fehler (Netz, 404, vom Scanner abgeschossen) - nicht werfen, unten pruefen. } if ((Test-Path -LiteralPath $dst) -and ((Get-Item -LiteralPath $dst).Length -gt 0)) { return $true } # Fehlgeschlagen oder leer: kurz warten, dann zweiter Versuch. Start-Sleep -Milliseconds 500 } return $false } # Textdateien und die Binaerdatei. Get-BundleFile behandelt beide gleich (Invoke-WebRequest # schreibt in beiden Faellen den Rohinhalt), sqlite3.exe steht getrennt fuer die Uebersicht. $textFiles = @( 'WinSetup.ps1', 'modules/Setup.ps1', 'modules/Wartung.ps1', 'modules/Migration.ps1', 'modules/Crypto.ps1', 'modules/UserData.ps1', 'modules/Taskbar.ps1' ) $binFiles = @( 'sqlite3.exe' ) $missing = @() foreach ($f in $textFiles) { if (-not (Get-BundleFile -RelPath $f -TargetDir $dir)) { $missing += $f } } foreach ($f in $binFiles) { if (-not (Get-BundleFile -RelPath $f -TargetDir $dir)) { $missing += $f } } # Mark-of-the-Web von allen geladenen Dateien entfernen (Guertel und Hosentraeger). try { Get-ChildItem -LiteralPath $dir -Recurse -File | Unblock-File -ErrorAction SilentlyContinue } catch { } # Auswertung: fehlt das Kernskript -> hart abbrechen. Fehlen nur Module/sqlite3 -> warnen. $winSetup = Join-Path $dir 'WinSetup.ps1' if ($missing -contains 'WinSetup.ps1') { Write-Host "" Write-Host " FEHLER: WinSetup.ps1 konnte nicht geladen werden - vermutlich hat der" -ForegroundColor Red Write-Host " Virenscanner (Bitdefender) die Ausfuehrung blockiert." -ForegroundColor Red Write-Host "" Write-Host " Bitte eine Ausnahme fuer den Ordner" -ForegroundColor Yellow Write-Host " $dir" -ForegroundColor Yellow Write-Host " in Bitdefender einrichten (Antivirus-Ausnahmen UND Advanced Threat Defense)," -ForegroundColor Yellow Write-Host " dann erneut starten." -ForegroundColor Yellow Write-Host "" return } if ($missing.Count -gt 0) { $liste = ($missing | ForEach-Object { $_ -replace '/', '\' }) -join ', ' Write-Host "" Write-Host " Hinweis: $liste fehlt/blockiert - die Passwort-/Migrationsfunktionen" -ForegroundColor Yellow Write-Host " (Profil exportieren/importieren) sind ohne AV-Ausnahme fuer" -ForegroundColor Yellow Write-Host " $dir" -ForegroundColor Yellow Write-Host " nicht verfuegbar. Menuepunkte 1/2 und die Wartungsfunktionen laufen normal." -ForegroundColor Yellow Write-Host "" } else { Write-Host " OK - starte Windows Toolbox..." -ForegroundColor Green Write-Host "" } # Menue starten (interaktiv). $PSScriptRoot des Skripts = $dir, dadurch findet WinSetup.ps1 # seine Module und sqlite3.exe. WINSETUP_HOME wird fuer die Selbst-Elevation (Admin) gesetzt, # damit das elevierte Fenster das Bundle im selben festen Ordner wiederfindet. $env:WINSETUP_HOME = $dir & $winSetup