ProcessExtensions

This commit is contained in:
Timerix22 2022-12-16 10:00:54 +06:00
parent decc2cecc4
commit 253b1010cf
6 changed files with 205 additions and 22 deletions

View File

@ -1,21 +0,0 @@
namespace DTLib.Extensions;
public static class IfMethod
{
public static T If<T>(this T input, bool condition, Func<T, T> if_true, Func<T, T> if_false) =>
condition ? if_true(input) : if_false(input);
public static void If<T>(this T input, bool condition, Action<T> if_true, Action<T> if_false)
{
if (condition) if_true(input);
else if_false(input);
}
public static T If<T>(this T input, bool condition, Func<T, T> if_true) =>
condition ? if_true(input) : input;
public static void If<T>(this T input, bool condition, Action<T> if_true)
{
if (condition) if_true(input);
}
}

View File

@ -0,0 +1,11 @@
using System.Diagnostics;
namespace DTLib.Extensions;
internal interface IProcessSuspenderImpl
{
void Suspend(Process p);
void Suspend(int pid);
void Resume(Process p);
void Resume(int pid);
}

View File

@ -0,0 +1,38 @@

// ReSharper disable UnusedMember.Local
using System.Diagnostics;
namespace DTLib.Extensions;
public static class ProcessExtensions
{
private static IProcessSuspenderImpl processSuspender;
static ProcessExtensions()
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
processSuspender = new ProcessSuspenderImplWindows();
else
processSuspender = new ProcessSuspenderImplUnix();
}
public static void Suspend(this Process p) => processSuspender.Suspend(p);
public static void Suspend(int pid)
{
if (pid <= 0)
throw new Exception($"invalid pid: {pid}");
processSuspender.Suspend(pid);
}
public static void Resume(this Process p) => processSuspender.Resume(p);
public static void Resume(int pid)
{
if (pid <= 0)
throw new Exception($"invalid pid: {pid}");
processSuspender.Resume(pid);
}
}

View File

@ -0,0 +1,92 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace DTLib.Extensions;
// https://stackoverflow.com/questions/41041730/net-core-app-how-to-send-sigterm-to-child-processes
internal class ProcessSuspenderImplUnix : IProcessSuspenderImpl
{
[SuppressMessage("ReSharper", "UnusedMember.Local")]
enum Signum
{
/// Hangup (POSIX).
SIGHUP = 1,
/// Interrupt (ANSI).
SIGINT = 2,
/// Quit (POSIX).
SIGQUIT = 3,
/// Illegal instruction (ANSI).
SIGILL = 4,
/// Trace trap (POSIX).
SIGTRAP = 5,
/// Abort (ANSI).
SIGABRT = 6,
/// IOT trap (4.2 BSD).
SIGIOT = 6,
/// BUS error (4.2 BSD).
SIGBUS = 7,
/// Floating-point exception (ANSI).
SIGFPE = 8,
/// Kill, unblockable (POSIX).
SIGKILL = 9,
/// User-defined signal 1 (POSIX).
SIGUSR1 = 10,
/// Segmentation violation (ANSI).
SIGSEGV = 11,
/// User-defined signal 2 (POSIX).
SIGUSR2 = 12,
/// Broken pipe (POSIX).
SIGPIPE = 13,
/// Alarm clock (POSIX).
SIGALRM = 14,
/// Termination (ANSI).
SIGTERM = 15,
/// Stack fault.
SIGSTKFLT = 16,
/// Same as SIGCHLD (System V).
SIGCLD = SIGCHLD,
/// Child status has changed (POSIX).
SIGCHLD = 17,
/// Continue (POSIX).
SIGCONT = 18,
/// Stop, unblockable (POSIX).
SIGSTOP = 19,
/// Keyboard stop (POSIX).
SIGTSTP = 20,
/// Background read from tty (POSIX).
SIGTTIN = 21,
/// Background write to tty (POSIX).
SIGTTOU = 22,
/// Urgent condition on socket (4.2 BSD).
SIGURG = 23,
/// CPU limit exceeded (4.2 BSD).
SIGXCPU = 24,
/// File size limit exceeded (4.2 BSD).
SIGXFSZ = 25,
/// Virtual alarm clock (4.2 BSD).
SIGVTALRM = 26,
/// Profiling alarm clock (4.2 BSD).
SIGPROF = 27,
/// Window size change (4.3 BSD, Sun).
SIGWINCH = 28,
/// Pollable event occurred (System V).
SIGPOLL = SIGIO,
/// I/O now possible (4.2 BSD).
SIGIO = 29,
/// Power failure restart (System V).
SIGPWR = 30,
/// Bad system call.
SIGSYS = 31,
SIGUNUSED = 31
}
[DllImport ("libc", SetLastError=true, EntryPoint="kill")]
private static extern int sys_kill (int pid, int sig);
public void Suspend(Process p) => Suspend(p.Id);
public void Suspend(int pid) => sys_kill(pid, (int)Signum.SIGSTOP);
public void Resume(Process p) => Resume(p.Id);
public void Resume(int pid) => sys_kill(pid, (int)Signum.SIGCONT);
}

View File

@ -0,0 +1,63 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace DTLib.Extensions;
// https://github.com/SarathR/ProcessUtil
internal class ProcessSuspenderImplWindows : IProcessSuspenderImpl
{
[SuppressMessage("ReSharper", "UnusedMember.Local")]
enum ThreadAccess
{
TERMINATE = 0x0001,
SUSPEND_RESUME = 0x0002,
GET_CONTEXT = 0x0008,
SET_CONTEXT = 0x0010,
SET_INFORMATION = 0x0020,
QUERY_INFORMATION = 0x0040,
SET_THREAD_TOKEN = 0x0080,
IMPERSONATE = 0x0100,
DIRECT_IMPERSONATION = 0x0200
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int ResumeThread(IntPtr hThread);
public void Suspend(Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
int errCode=Marshal.GetLastWin32Error();
throw new Exception($"unmanaged function exited with error: {errCode}");
}
SuspendThread(pOpenThread);
}
}
public void Suspend(int pid) => Suspend(Process.GetProcessById(pid));
public void Resume(Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
int errCode=Marshal.GetLastWin32Error();
throw new Exception($"unmanaged function exited with error: {errCode}");
}
ResumeThread(pOpenThread);
}
}
public void Resume(int pid) => Resume(Process.GetProcessById(pid));
}

View File

@ -2,7 +2,7 @@ using System.IO;
namespace DTLib.Extensions; namespace DTLib.Extensions;
public static class OtherStuff public static class StreamExtensions
{ {
public static void Write(this Stream stream, byte[] buff) public static void Write(this Stream stream, byte[] buff)
=> stream.Write(buff, 0, buff.Length); => stream.Write(buff, 0, buff.Length);