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 bool RaiseError { get; set; } = true;
public bool TraitErrorAsOutput { get; set; } = false;
public bool Cancelable { get; } = false;
public Command(bool cancelable = false)
{
Cancelable = cancelable;
}
public bool Exec()
{
@ -109,12 +115,22 @@ namespace SourceGit.Commands
return false;
}
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
_proc = proc;
int exitCode = proc.ExitCode;
proc.Close();
int exitCode;
try
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
exitCode = proc.ExitCode;
proc.Close();
}
finally
{
_proc = null;
}
if (!isCancelled && exitCode != 0)
{
@ -150,17 +166,37 @@ namespace SourceGit.Commands
};
}
var rs = new ReadToEndResult()
_proc = proc;
try
{
StdOut = proc.StandardOutput.ReadToEnd(),
StdErr = proc.StandardError.ReadToEnd(),
};
var rs = new ReadToEndResult()
{
StdOut = proc.StandardOutput.ReadToEnd(),
StdErr = proc.StandardError.ReadToEnd(),
};
proc.WaitForExit();
rs.IsSuccess = proc.ExitCode == 0;
proc.Close();
proc.WaitForExit();
rs.IsSuccess = proc.ExitCode == 0;
proc.Close();
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)
@ -226,5 +262,7 @@ namespace SourceGit.Commands
[GeneratedRegex(@"\d+%")]
private static partial Regex REG_PROGRESS();
private Process _proc = null;
}
}

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia;
@ -11,6 +12,14 @@ namespace SourceGit.Native
[SupportedOSPlatform("linux")]
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)
{
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)
{
var pathVariable = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;

View file

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Avalonia;
@ -11,6 +12,14 @@ namespace SourceGit.Native
[SupportedOSPlatform("macOS")]
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)
{
builder.With(new MacOSPlatformOptions()
@ -88,5 +97,11 @@ namespace SourceGit.Native
{
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 OpenBrowser(string url);
void OpenWithDefaultEditor(string file);
void TerminateSafely(Process process);
}
public static string DataDir
@ -168,6 +170,11 @@ namespace SourceGit.Native
_backend.OpenWithDefaultEditor(file);
}
public static void TerminateSafely(Process process)
{
_backend.TerminateSafely(process);
}
private static void UpdateGitVersion()
{
if (string.IsNullOrEmpty(_gitExecutable) || !File.Exists(_gitExecutable))

View file

@ -35,6 +35,11 @@ namespace SourceGit.Native
public int cyBottomHeight;
}
private enum CTRL_EVENT : int
{
CTRL_C = 0
}
[DllImport("ntdll.dll")]
private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);
@ -53,6 +58,12 @@ namespace SourceGit.Native
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
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)
{
// Fix drop shadow issue on Windows 10
@ -212,6 +223,22 @@ namespace SourceGit.Native
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)
{
var platformHandle = w.TryGetPlatformHandle();