diff --git a/Daybreak.Core/Services/ApplicationLauncher/ApplicationLauncher.cs b/Daybreak.Core/Services/ApplicationLauncher/ApplicationLauncher.cs index de1bbdbb..801946bc 100644 --- a/Daybreak.Core/Services/ApplicationLauncher/ApplicationLauncher.cs +++ b/Daybreak.Core/Services/ApplicationLauncher/ApplicationLauncher.cs @@ -536,18 +536,7 @@ internal sealed class ApplicationLauncher( var scopedLogger = this.logger.CreateScopedLogger(); try { - var process = guildWarsApplicationLaunchContext.GuildWarsProcess; - if ( - process.MainModule?.FileName is not null - && process.MainModule.FileName.Contains( - "Gw.exe", - StringComparison.OrdinalIgnoreCase - ) - ) - { - process.Kill(true); - return; - } + this.guildWarsProcessFinder.KillProcess(guildWarsApplicationLaunchContext); } catch (Exception e) when (e.Message.Contains( diff --git a/Daybreak.Linux/Services/ApplicationLauncher/GuildWarsProcessFinder.cs b/Daybreak.Linux/Services/ApplicationLauncher/GuildWarsProcessFinder.cs index 6275f320..cac16c91 100644 --- a/Daybreak.Linux/Services/ApplicationLauncher/GuildWarsProcessFinder.cs +++ b/Daybreak.Linux/Services/ApplicationLauncher/GuildWarsProcessFinder.cs @@ -52,6 +52,19 @@ public sealed class GuildWarsProcessFinder( return this.FindProcesses(configuration).FirstOrDefault(); } + /// + /// Terminates the Wine-hosted Guild Wars process tree. + /// The process was already validated as a Gw.exe Wine process during discovery, + /// so we kill it directly. On Linux the process exposed by the context is the + /// Wine loader hosting Gw.exe; its therefore + /// points at the Wine loader rather than Gw.exe, which is why platform-agnostic + /// name checks cannot be used here. + /// + public void KillProcess(GuildWarsApplicationLaunchContext guildWarsApplicationLaunchContext) + { + guildWarsApplicationLaunchContext.GuildWarsProcess.Kill(entireProcessTree: true); + } + public IEnumerable FindProcesses( params LaunchConfigurationWithCredentials[] configurations ) diff --git a/Daybreak.Shared/Services/ApplicationLauncher/IGuildWarsProcessFinder.cs b/Daybreak.Shared/Services/ApplicationLauncher/IGuildWarsProcessFinder.cs index a32b1edd..4cb3fd59 100644 --- a/Daybreak.Shared/Services/ApplicationLauncher/IGuildWarsProcessFinder.cs +++ b/Daybreak.Shared/Services/ApplicationLauncher/IGuildWarsProcessFinder.cs @@ -30,4 +30,12 @@ public interface IGuildWarsProcessFinder IEnumerable FindProcesses( params LaunchConfigurationWithCredentials[] configurations ); + + /// + /// Terminates the Guild Wars process associated with the given launch context. + /// Implementations are responsible for handling platform-specific process layouts + /// (e.g. Wine-hosted processes on Linux). May throw, allowing the caller to handle + /// platform-specific failures such as insufficient privileges. + /// + void KillProcess(GuildWarsApplicationLaunchContext guildWarsApplicationLaunchContext); } diff --git a/Daybreak.Windows/Services/ApplicationLauncher/GuildWarsProcessFinder.cs b/Daybreak.Windows/Services/ApplicationLauncher/GuildWarsProcessFinder.cs index 8636ceaa..6676099a 100644 --- a/Daybreak.Windows/Services/ApplicationLauncher/GuildWarsProcessFinder.cs +++ b/Daybreak.Windows/Services/ApplicationLauncher/GuildWarsProcessFinder.cs @@ -28,6 +28,24 @@ public sealed class GuildWarsProcessFinder : IGuildWarsProcessFinder return this.FindProcesses(configuration).FirstOrDefault(); } + /// + /// Terminates the Guild Wars process tree. Guards against killing an unrelated + /// process by verifying the main module is Gw.exe. Reading the main module of an + /// elevated process throws a , which is propagated so + /// the caller can fall back to a direct kill or request elevation. + /// + public void KillProcess(GuildWarsApplicationLaunchContext guildWarsApplicationLaunchContext) + { + var process = guildWarsApplicationLaunchContext.GuildWarsProcess; + if ( + process.MainModule?.FileName is not null + && process.MainModule.FileName.Contains("Gw.exe", StringComparison.OrdinalIgnoreCase) + ) + { + process.Kill(true); + } + } + public IEnumerable FindProcesses( params LaunchConfigurationWithCredentials[] configurations )