feat: add safe command termination

This commit is contained in:
Aikawa Yataro 2025-02-21 08:29:58 +00:00
parent b2ab62825e
commit 34ae392251
No known key found for this signature in database
GPG key ID: 1C5D95FB10179404
5 changed files with 115 additions and 13 deletions

View file

@ -37,6 +37,12 @@ namespace SourceGit.Commands
public string Args { get; set; } = string.Empty; public string Args { get; set; } = string.Empty;
public bool RaiseError { get; set; } = true; public bool RaiseError { get; set; } = true;
public bool TraitErrorAsOutput { get; set; } = false; public bool TraitErrorAsOutput { get; set; } = false;
public bool Cancelable { get; } = false;
public Command(bool cancelable = false)
{
Cancelable = cancelable;
}
public bool Exec() public bool Exec()
{ {
@ -109,12 +115,22 @@ namespace SourceGit.Commands
return false; return false;
} }
_proc = proc;
int exitCode;
try
{
proc.BeginOutputReadLine(); proc.BeginOutputReadLine();
proc.BeginErrorReadLine(); proc.BeginErrorReadLine();
proc.WaitForExit(); proc.WaitForExit();
int exitCode = proc.ExitCode; exitCode = proc.ExitCode;
proc.Close(); proc.Close();
}
finally
{
_proc = null;
}
if (!isCancelled && exitCode != 0) if (!isCancelled && exitCode != 0)
{ {
@ -150,6 +166,10 @@ namespace SourceGit.Commands
}; };
} }
_proc = proc;
try
{
var rs = new ReadToEndResult() var rs = new ReadToEndResult()
{ {
StdOut = proc.StandardOutput.ReadToEnd(), StdOut = proc.StandardOutput.ReadToEnd(),
@ -162,6 +182,22 @@ namespace SourceGit.Commands
return rs; return rs;
} }
finally
{
_proc = null;
}
}
public void TryCancel()
{
if (!this.Cancelable)
throw new Exception("Command is not cancelable!");
if (_proc is null)
return;
Native.OS.TerminateSafely(_proc);
}
protected virtual void OnReadline(string line) protected virtual void OnReadline(string line)
{ {
@ -226,5 +262,7 @@ namespace SourceGit.Commands
[GeneratedRegex(@"\d+%")] [GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS(); private static partial Regex REG_PROGRESS();
private Process _proc = null;
} }
} }

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using Avalonia; using Avalonia;
@ -11,6 +12,14 @@ namespace SourceGit.Native
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]
internal class Linux : OS.IBackend internal class Linux : OS.IBackend
{ {
private enum SIGNAL : int
{
TERM = 15
}
[DllImport("c")]
private static extern int kill(int pid, int sig);
public void SetupApp(AppBuilder builder) public void SetupApp(AppBuilder builder)
{ {
builder.With(new X11PlatformOptions() { EnableIme = true }); builder.With(new X11PlatformOptions() { EnableIme = true });
@ -97,6 +106,12 @@ namespace SourceGit.Native
} }
} }
public void TerminateSafely(Process process)
{
if (kill(process.Id, (int)SIGNAL.TERM) == 0)
process.WaitForExit();
}
private string FindExecutable(string filename) private string FindExecutable(string filename)
{ {
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;

View file

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using Avalonia; using Avalonia;
@ -11,6 +12,14 @@ namespace SourceGit.Native
[SupportedOSPlatform("macOS")] [SupportedOSPlatform("macOS")]
internal class MacOS : OS.IBackend internal class MacOS : OS.IBackend
{ {
private enum SIGNAL : int
{
TERM = 15
}
[DllImport("System")]
private static extern int kill(int pid, int sig);
public void SetupApp(AppBuilder builder) public void SetupApp(AppBuilder builder)
{ {
builder.With(new MacOSPlatformOptions() builder.With(new MacOSPlatformOptions()
@ -88,5 +97,11 @@ namespace SourceGit.Native
{ {
Process.Start("open", $"\"{file}\""); Process.Start("open", $"\"{file}\"");
} }
public void TerminateSafely(Process process)
{
if (kill(process.Id, (int)SIGNAL.TERM) == 0)
process.WaitForExit();
}
} }
} }

View file

@ -23,6 +23,8 @@ namespace SourceGit.Native
void OpenInFileManager(string path, bool select); void OpenInFileManager(string path, bool select);
void OpenBrowser(string url); void OpenBrowser(string url);
void OpenWithDefaultEditor(string file); void OpenWithDefaultEditor(string file);
void TerminateSafely(Process process);
} }
public static string DataDir public static string DataDir
@ -168,6 +170,11 @@ namespace SourceGit.Native
_backend.OpenWithDefaultEditor(file); _backend.OpenWithDefaultEditor(file);
} }
public static void TerminateSafely(Process process)
{
_backend.TerminateSafely(process);
}
private static void UpdateGitVersion() private static void UpdateGitVersion()
{ {
if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable)) if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable))

View file

@ -35,6 +35,11 @@ namespace SourceGit.Native
public int cyBottomHeight; public int cyBottomHeight;
} }
private enum CTRL_EVENT : int
{
CTRL_C = 0
}
[DllImport("ntdll.dll")] [DllImport("ntdll.dll")]
private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation); private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
@ -53,6 +58,12 @@ namespace SourceGit.Native
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)] [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags); private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, int cild, IntPtr apidl, int dwFlags);
[DllImport("kernel32.dll")]
public static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);
[DllImport("kernel32.dll")]
private static extern bool GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);
public void SetupApp(AppBuilder builder) public void SetupApp(AppBuilder builder)
{ {
// Fix drop shadow issue on Windows 10 // Fix drop shadow issue on Windows 10
@ -212,6 +223,22 @@ namespace SourceGit.Native
Process.Start(start); Process.Start(start);
} }
public void TerminateSafely(Process process)
{
if (SetConsoleCtrlHandler(IntPtr.Zero, true))
{
try
{
if (GenerateConsoleCtrlEvent((int)CTRL_EVENT.CTRL_C, process.Id))
process.WaitForExit();
}
finally
{
SetConsoleCtrlHandler(IntPtr.Zero, false);
}
}
}
private void FixWindowFrameOnWin10(Window w) private void FixWindowFrameOnWin10(Window w)
{ {
var platformHandle = w.TryGetPlatformHandle(); var platformHandle = w.TryGetPlatformHandle();