diff --git a/PSLauncher-0.6.0.exe b/PSLauncher-0.6.0.exe new file mode 100644 index 0000000..cbdd4b7 Binary files /dev/null and b/PSLauncher-0.6.0.exe differ diff --git a/installer/PSLauncher.iss b/installer/PSLauncher.iss index c537ceb..5c6cb28 100644 --- a/installer/PSLauncher.iss +++ b/installer/PSLauncher.iss @@ -28,7 +28,9 @@ AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} -DefaultDirName={autopf}\{#MyAppPublisher}\{#MyAppShortName} +; Installation user-mode (pas d'admin requis) : le launcher pourra se mettre à jour +; tout seul via PSLauncher.Updater.exe sans déclencher de UAC. +DefaultDirName={localappdata}\Programs\{#MyAppPublisher}\{#MyAppShortName} DefaultGroupName={#MyAppName} DisableProgramGroupPage=yes OutputDir=output @@ -37,8 +39,9 @@ SetupIconFile= Compression=lzma2/ultra SolidCompression=yes WizardStyle=modern -PrivilegesRequired=admin +PrivilegesRequired=lowest PrivilegesRequiredOverridesAllowed=dialog +UsedUserAreasWarning=no ArchitecturesAllowed=x64 ArchitecturesInstallIn64BitMode=x64 UninstallDisplayName={#MyAppName} diff --git a/installer/output/PSLauncher-Setup-0.5.0.exe b/installer/output/PSLauncher-Setup-0.5.0.exe new file mode 100644 index 0000000..a6ab447 Binary files /dev/null and b/installer/output/PSLauncher-Setup-0.5.0.exe differ diff --git a/src/PSLauncher.Core/Updates/LauncherSelfUpdater.cs b/src/PSLauncher.Core/Updates/LauncherSelfUpdater.cs index 81b9beb..51dd13c 100644 --- a/src/PSLauncher.Core/Updates/LauncherSelfUpdater.cs +++ b/src/PSLauncher.Core/Updates/LauncherSelfUpdater.cs @@ -84,23 +84,45 @@ public sealed class LauncherSelfUpdater : ILauncherSelfUpdater File.Move(downloadedPath, newPath); } - // 5. Lance l'updater détaché + // 5. Lance l'updater détaché. Si le target est dans un dossier protégé + // (Program Files, etc.), on ne pourra pas le remplacer sans admin : + // on demande l'élévation via ShellExecute + Verb=runas (UAC popup). var pid = Environment.ProcessId; + var needsElevation = !CanWriteTo(targetExe); + var psi = new ProcessStartInfo { FileName = updaterPath, - UseShellExecute = false, - CreateNoWindow = true, + UseShellExecute = needsElevation, // requis pour Verb=runas + CreateNoWindow = !needsElevation, + Verb = needsElevation ? "runas" : "", + Arguments = string.Join(' ', new[] + { + $"--target=\"{targetExe}\"", + $"--source=\"{newPath}\"", + $"--pid={pid}", + "--launch", + }), }; - psi.ArgumentList.Add($"--target={targetExe}"); - psi.ArgumentList.Add($"--source={newPath}"); - psi.ArgumentList.Add($"--pid={pid}"); - psi.ArgumentList.Add("--launch"); - _logger.LogInformation("Spawning updater {Updater} (target={Target}, source={Source}, pid={Pid})", - updaterPath, targetExe, newPath, pid); + _logger.LogInformation("Spawning updater {Updater} (target={Target}, source={Source}, pid={Pid}, elevate={Elevate})", + updaterPath, targetExe, newPath, pid, needsElevation); SysProcess.Start(psi); return true; // l'appelant doit Application.Current.Shutdown() après } + + /// Test rapide : peut-on écrire à côté du fichier cible (= dans son dossier) ? + private static bool CanWriteTo(string filePath) + { + try + { + var dir = Path.GetDirectoryName(filePath)!; + var probe = Path.Combine(dir, ".pslauncher-write-test-" + Guid.NewGuid().ToString("N").Substring(0, 8)); + File.WriteAllText(probe, ""); + File.Delete(probe); + return true; + } + catch { return false; } + } } diff --git a/src/PSLauncher.Updater/Program.cs b/src/PSLauncher.Updater/Program.cs index bcb75ae..d285f99 100644 --- a/src/PSLauncher.Updater/Program.cs +++ b/src/PSLauncher.Updater/Program.cs @@ -82,16 +82,21 @@ internal static class Program return 4; } - // 5. Relance + // 5. Relance. + // Si on a été lancé en admin (Verb=runas via UAC), un Process.Start direct + // propagerait l'élévation au launcher et donc à tous ses sous-processus + // (PROSERVE_UE_5_5.exe), ce qu'on ne veut pas. On passe par explorer.exe + // qui est non-élévé : il ouvre l'exe dans la session utilisateur normale. if (opts.Launch) { try { Process.Start(new ProcessStartInfo { - FileName = opts.Target, - UseShellExecute = true, - WorkingDirectory = Path.GetDirectoryName(opts.Target)!, + FileName = "explorer.exe", + Arguments = "\"" + opts.Target + "\"", + UseShellExecute = false, + CreateNoWindow = true, }); } catch (Exception ex)