-
Notifications
You must be signed in to change notification settings - Fork 60
/
EasyProcess.cs
138 lines (117 loc) · 4.03 KB
/
EasyProcess.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
namespace Easy.Common;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Provides an abstraction to simplify executing a process asynchronously.
/// </summary>
public sealed class EasyProcess : IDisposable
{
private readonly Process _process;
private EasyProcess(ProcessStartInfo startInfo)
{
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
_process = new()
{
EnableRaisingEvents = true,
StartInfo = startInfo
};
_process.OutputDataReceived += OnOutputData;
_process.ErrorDataReceived += OnErrorData;
}
/// <summary>
/// Creates an instance of the <see cref="EasyProcess"/>.
/// </summary>
public EasyProcess(string processPath, string args) : this(new ProcessStartInfo(processPath, args)) =>
Ensure.NotNullOrEmptyOrWhiteSpace(processPath);
/// <summary>
/// Creates an instance of the <see cref="EasyProcess"/>.
/// </summary>
public EasyProcess(FileInfo processPath, string args) : this(new ProcessStartInfo(processPath.FullName, args)) =>
Ensure.NotNull(processPath, nameof(processPath));
/// <summary>
/// Creates an instance of the <see cref="EasyProcess"/>.
/// </summary>
public EasyProcess(FileInfo processPath, DirectoryInfo workingDirectory, string args) :
this(new ProcessStartInfo(processPath.FullName, args) { WorkingDirectory = workingDirectory.FullName })
{
ArgumentNullException.ThrowIfNull(processPath, nameof(processPath));
ArgumentNullException.ThrowIfNull(workingDirectory, nameof(workingDirectory));
}
/// <summary>
/// Gets the process Id.
/// </summary>
public int Id => _process.Id;
/// <summary>
/// Gets the value that the associated process specified when it terminated.
/// </summary>
public int ExitCode => _process.ExitCode;
/// <summary>
/// Gets the time that the associated process exited.
/// </summary>
public DateTime ExitTime => _process.ExitTime;
/// <summary>
/// Gets the time that the associated process was started.
/// </summary>
public DateTime StartTime => _process.StartTime;
/// <summary>
/// Gets a value indicating whether the associated process has been terminated.
/// </summary>
public bool HasExited => _process.HasExited;
/// <summary>
/// Gets the execution time of the process.
/// </summary>
public TimeSpan ExecutionTime => _process.ExitTime - _process.StartTime;
/// <summary>
/// Raised on every process output line.
/// </summary>
public event EventHandler<string>? OnOutput;
/// <summary>
/// Raised on every process error line.
/// </summary>
public event EventHandler<string>? OnError;
/// <summary>
/// Starts the process and publishes output and error lines if any as events.
/// </summary>
public async Task Start(CancellationToken cToken = default)
{
_process.Start();
_process.BeginOutputReadLine();
_process.BeginErrorReadLine();
try
{
await _process.WaitForExitAsync(cToken).ConfigureAwait(false);
} catch (TaskCanceledException)
{
_process.Kill(true);
}
}
/// <summary>
/// Releases all the resources used by this instance.
/// </summary>
public void Dispose()
{
_process.OutputDataReceived -= OnOutputData;
_process.ErrorDataReceived -= OnErrorData;
_process.Dispose();
}
private void OnOutputData(object sender, DataReceivedEventArgs e)
{
if (e.Data is not null)
{
OnOutput?.Invoke(this, e.Data);
}
}
private void OnErrorData(object sender, DataReceivedEventArgs e)
{
if (e.Data is not null)
{
OnError?.Invoke(this, e.Data);
}
}
}