Gapotchenko.FX.Diagnostics.Process 2024.1.3

Prefix Reserved
dotnet add package Gapotchenko.FX.Diagnostics.Process --version 2024.1.3                
NuGet\Install-Package Gapotchenko.FX.Diagnostics.Process -Version 2024.1.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Gapotchenko.FX.Diagnostics.Process" Version="2024.1.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Gapotchenko.FX.Diagnostics.Process --version 2024.1.3                
#r "nuget: Gapotchenko.FX.Diagnostics.Process, 2024.1.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Gapotchenko.FX.Diagnostics.Process as a Cake Addin
#addin nuget:?package=Gapotchenko.FX.Diagnostics.Process&version=2024.1.3

// Install Gapotchenko.FX.Diagnostics.Process as a Cake Tool
#tool nuget:?package=Gapotchenko.FX.Diagnostics.Process&version=2024.1.3                

Overview

The module provides extended functionality for process manipulation.

Process Extensions

GetParent()

GetParent() is an extension method provided by Gapotchenko.FX.Diagnostics.Process module for System.Diagnostics.Process class.

What it does is returns the parent process. Or null when parent process is absent or no longer running.

EnumerateParents()

Enumerates a chain of parent processes beginning with the closest parent.

ReadEnvironmentVariables()

Reads environment variables of a process.

The functionality is achieved by reading the process environment block (PEB) at the operating system level.

For example, this is how a PATH environment variable can be retrieved from all running instances of Microsoft Visual Studio:

using Gapotchenko.FX.Diagnostics;
using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        var processes = Process.GetProcessesByName("devenv");

        if (processes.Length == 0)
            Console.WriteLine("Process with a given name not found. Please modify the code and specify the existing process name.");

        foreach (var process in processes)
        {
            Console.WriteLine();
            Console.WriteLine("Process with ID {0} has the following value of PATH environment variable:", process.Id);

            var env = process.ReadEnvironmentVariables();

            string path = env["PATH"];
            Console.WriteLine(path);
        }
    }
}

ReadArguments()

The method retrieves a set of command-line arguments specified at the process start. The functionality is achieved by reading the process environment block at the operating system level.

using Gapotchenko.FX.Diagnostics;
using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        var processes = Process.GetProcessesByName("devenv");

        if (processes.Length == 0)
            Console.WriteLine("Process with a given name not found. Please modify the code and specify the existing process name.");

        foreach (var process in processes)
        {
            Console.WriteLine(
                "Process with ID {0} was started with the following command line: {1}",
                process.Id,
                process.ReadArguments());
        }
    }
}

ReadArgumentList()

The method retrieves a sequence of command-line arguments specified at the process start. It is similar to aforementioned ReadArguments() method but returns a sequence of command-line arguments instead of a single command line string.

This fundamental difference may be essential in multi-platform scenarios. For instance, Windows OS natively represents the command line of a process as a single string, while Unix operating systems natively represent the command line as a strongly-typed array of command-line arguments.

Whichever method is used the results are similar, but every method provides a higher degree of detalization for a particular operating system.

End()

Allows to end a process according to a specified mode of operation.

The End() method is interesting and a bit intricate. The stock Process class already provides a similar Kill() method which performs an immediate forceful termination of a process without giving it a chance to exit gracefully.

Depending on a kind of process being terminated, Kill() is not always suitable. For example, it may have devastating consequences if someone kills a Microsoft Visual Studio process without giving it a graceful shutdown. Lost files, potentially corrupted extensions and so on.

Meet the End() method provided by Gapotchenko.FX.Diagnostics.Process module. It allows to end a process according to a specified mode of operation. The default mode of operation is ProcessEndMode.Complete that follows a sequence presented below:

  1. Graceful techniques are tried first:
    1.1. End() method tries to close a main window of the process
    1.2. If that fails, it tries to send Ctrl+C (SIGTERM) signal to the process
  2. Forceful techniques:
    2.1. If graceful techniques fail, End() method tries to exit the process (suitable for the current process only)
    2.2. If that fails, it kills the process (SIGKILL)

The method returns a ProcessEndMode value on completion indicating how the process was actually ended.

Let's take a look on example that tries to end all running instances of Notepad:

using Gapotchenko.FX.Diagnostics;

foreach (var process in Process.GetProcessesByName("notepad"))
{
    var result = process.End();
    Console.WriteLine(result);
}

Once there is a running Notepad app, the sample produces the following output:

Close

signifying that a Notepad process was gracefully ended by closing its main window.

Now let's repeat the experiment by launching a Notepad app again and opening its "File Save" dialog via menu (File → Save As...). This time, let's keep the "File Save" dialog open, and launch the example code once again.

This time the result will be different:

Kill

The Notepad process was unable to shutdown gracefully and thus was forcefully killed. Graceful shutdown was not possible because the process had an active modal dialog.

<details> <summary>More examples</summary>

Let's modify sample a bit:

foreach (var process in Process.GetProcessesByName("notepad"))
{
    var result = process.End();

    Console.WriteLine("PID {0}", process.Id);
    Console.WriteLine("Graceful: {0}", (result & ProcessEndMode.Graceful) != 0);
    Console.WriteLine("Forceful: {0}", (result & ProcessEndMode.Forceful) != 0);
}

Now it shows the Id of a process that was ended together with a graceful/forceful classification of the result.

What if we want to limit the End() method to only perform a graceful process termination? Let's use the End(ProcessEndMode) method overload:

foreach (var process in Process.GetProcessesByName("notepad"))
{
    var result = process.End(ProcessEndMode.Graceful);

    Console.WriteLine("PID {0}", process.Id);
    Console.WriteLine(result);
}

Now the result will only be graceful, or have ProcessEndMode.None value if a process could not be gracefully ended.

But what if we want to limit the End() method to only perform a graceful process termination via Ctrl+C (SIGINT) signal and forceful kill? No problem:

foreach (var process in Process.GetProcessesByName("notepad"))
{
    var result = process.End(ProcessEndMode.Interrupt | ProcessEndMode.Kill);

    Console.WriteLine("PID {0}", process.Id);
    Console.WriteLine(result);
}

As you can see, despite a simple-looking signature, the End(…) method gives enormous possibilities for achieving a specific goal.

</details>

EndAsync()

The method is similar to End() but has an async implementation. It can be used to efficiently handle dozens of processes in a bulk.

For example, there is a need to shut down quite a few processes, say 20. One possible solution is to shut down the processes sequentially, one by one. But this may take quite a few moments to complete, up to several dozen seconds. On a positive side, it only takes one CPU thread. The second solution is to shut down the processes in parallel. This solution will reduce the overall time significantly, but that reduction will come at the expense of 20 wasted CPU threads. What if we could have the benefits of both solutions at the same time?

We can achieve that by using asynchronous code, like so:

static Task<ProcessEndMode[]> EndProcessesAsync(IEnumerable<Process> processesToEnd) =>
    Task.WhenAll(processesToEnd.Select(x => x.EndAsync()));

Even if you write a single-threaded app, you still get the benefits of asynchronous code here: the processes are shut down in parallel and no CPU threads are wasted.

Here is a full example that demonstrates that:

using Gapotchenko.FX.Diagnostics;
using Gapotchenko.FX.Threading.Tasks;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        var processEndModes = TaskBridge.Execute(
            EndProcessesAsync(
                Process.GetProcessesByName("notepad"));

        Console.WriteLine(
            "Notepads were ended with the following results: {0}.",
            string.Join(", ", processEndModes.Select(x => x.ToString())));
    }

    static Task<ProcessEndMode[]> EndProcessesAsync(IEnumerable<Process> processesToEnd) =>
        Task.WhenAll(processesToEnd.Select(x => x.EndAsync()));
}

(Reminder: the correct way to wait for the completion of an asynchronous task in synchronous code is to use TaskBridge)

Other Modules

Let's continue with a look at some other modules provided by Gapotchenko.FX:

Or look at the full list of modules.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net5.0-windows7.0 is compatible.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net6.0-windows7.0 is compatible.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net7.0-windows7.0 is compatible.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net8.0-windows7.0 is compatible.  net9.0 is compatible.  net9.0-windows7.0 is compatible. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 is compatible.  netcoreapp2.2 was computed.  netcoreapp3.0 is compatible.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 is compatible.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Gapotchenko.FX.Diagnostics.Process:

Package Downloads
IoFluently.NetStandard

Package Description

Gapotchenko.FX.Profiles.Core

Represents the Core profile of Gapotchenko.FX.

Gapotchenko.FX.Console

Provides virtual terminal functionality, console traits, and other useful primitives for .NET console apps.

BlossomiShymae.GrrrLCU

A simple wrapper for the LCU. Grrr. x3

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on Gapotchenko.FX.Diagnostics.Process:

Repository Stars
blish-hud/Blish-HUD
A Guild Wars 2 overlay with extreme extensibility through compiled modules.
Version Downloads Last updated
2024.1.3 1,080 11/10/2024
2022.2.7 40,237 5/1/2022
2022.2.5 1,526 5/1/2022
2022.1.4 1,551 4/6/2022
2021.2.21 1,741 1/21/2022
2021.2.20 1,521 1/17/2022
2021.1.5 8,207 7/6/2021
2020.2.2-beta 1,014 11/21/2020
2020.1.15 5,015 11/5/2020
2020.1.9-beta 1,055 7/14/2020
2020.1.8-beta 1,040 7/14/2020
2020.1.7-beta 1,113 7/14/2020
2020.1.1-beta 1,117 2/11/2020
2019.3.7 1,940 11/4/2019
2019.2.20 1,321 8/13/2019
2019.1.151 26,703 3/30/2019