Skip to content

Commit d9a4114

Browse files
committed
Asyny methods now support the use of cancelation tokens
1 parent 782e029 commit d9a4114

9 files changed

Lines changed: 716 additions & 213 deletions

File tree

src/WGet.NET/Components/Internal/ProcessManager.cs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//--------------------------------------------------//
55
using System.IO;
66
using System.Text;
7+
using System.Threading;
78
using System.Diagnostics;
89
using System.Threading.Tasks;
910
using WGetNET.Models;
@@ -58,13 +59,16 @@ public ProcessResult ExecuteWingetProcess(string cmd)
5859
/// <param name="cmd">
5960
/// A <see cref="string"/> representing the command that winget should be executed with.
6061
/// </param>
62+
/// <param name="cancellationToken">
63+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
64+
/// </param>
6165
/// <returns>
6266
/// A <see cref="ProcessResult"/> object,
6367
/// containing the output an exit id of the process.
6468
/// </returns>
65-
public async Task<ProcessResult> ExecuteWingetProcessAsync(string cmd)
69+
public async Task<ProcessResult> ExecuteWingetProcessAsync(string cmd, CancellationToken cancellationToken = default)
6670
{
67-
return await RunProcessAsync(GetStartInfo(cmd));
71+
return await RunProcessAsync(GetStartInfo(cmd), cancellationToken);
6872
}
6973

7074
/// <summary>
@@ -93,8 +97,11 @@ private ProcessStartInfo GetStartInfo(string cmd)
9397
/// <summary>
9498
/// Runs a process with the current start informations.
9599
/// </summary>
100+
/// <param name="processStartInfo">
101+
/// The <see cref="System.Diagnostics.ProcessStartInfo"/> for process that should be executed.
102+
/// </param>
96103
/// <returns>
97-
/// A <see cref="ProcessResult"/> object,
104+
/// A <see cref="WGetNET.Models.ProcessResult"/> object,
98105
/// containing the output an exit id of the process.
99106
/// </returns>
100107
private ProcessResult RunProcess(ProcessStartInfo processStartInfo)
@@ -119,11 +126,17 @@ private ProcessResult RunProcess(ProcessStartInfo processStartInfo)
119126
/// <summary>
120127
/// Asynchronous runs a process with the current start informations.
121128
/// </summary>
129+
/// <param name="processStartInfo">
130+
/// The <see cref="System.Diagnostics.ProcessStartInfo"/> for process that should be executed.
131+
/// </param>
132+
/// <param name="cancellationToken">
133+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
134+
/// </param>
122135
/// <returns>
123-
/// A <see cref="ProcessResult"/> object,
136+
/// A <see cref="WGetNET.Models.ProcessResult"/> object,
124137
/// containing the output an exit id of the process.
125138
/// </returns>
126-
private async Task<ProcessResult> RunProcessAsync(ProcessStartInfo processStartInfo)
139+
private async Task<ProcessResult> RunProcessAsync(ProcessStartInfo processStartInfo, CancellationToken cancellationToken = default)
127140
{
128141
ProcessResult result = new();
129142

@@ -132,10 +145,27 @@ private async Task<ProcessResult> RunProcessAsync(ProcessStartInfo processStartI
132145
{
133146
proc.Start();
134147

135-
result.Output = await ReadSreamOutputAsync(proc.StandardOutput);
148+
result.Output = await ReadSreamOutputAsync(proc.StandardOutput, cancellationToken);
136149

137-
//Wait till end and get exit code
150+
// Kill the process and return, if the task is canceled
151+
if (cancellationToken.IsCancellationRequested && !proc.HasExited)
152+
{
153+
proc.Kill();
154+
155+
result.ExitCode = -1;
156+
157+
return result;
158+
}
159+
160+
//Wait for the processs to exit
138161
proc.WaitForExit();
162+
163+
// Make sure the process has exited
164+
if (!proc.HasExited)
165+
{
166+
proc.Kill();
167+
}
168+
139169
result.ExitCode = proc.ExitCode;
140170
}
141171

@@ -175,19 +205,27 @@ private string[] ReadSreamOutput(StreamReader output)
175205
/// Asynchronous reads the data from the process output to a string array.
176206
/// </summary>
177207
/// <param name="output">
178-
/// The <see cref="StreamReader"/> with the process output.
208+
/// The <see cref="System.IO.StreamReader"/> with the process output.
209+
/// </param>
210+
/// <param name="cancellationToken">
211+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
179212
/// </param>
180213
/// <returns>
181214
/// A <see cref="string"/> array
182215
/// containing the process output stream content by lines.
183216
/// </returns>
184-
private async Task<string[]> ReadSreamOutputAsync(StreamReader output)
217+
private async Task<string[]> ReadSreamOutputAsync(StreamReader output, CancellationToken cancellationToken = default)
185218
{
186219
string[] outputArray = new string[0];
187220

188221
//Read output to list
189222
while (!output.EndOfStream)
190223
{
224+
if (cancellationToken.IsCancellationRequested)
225+
{
226+
break;
227+
}
228+
191229
string? outputLine = await output.ReadLineAsync();
192230
if (outputLine is null)
193231
{

src/WGet.NET/Components/WinGet.cs

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.IO;
77
using System.Security;
8+
using System.Threading;
89
using System.Threading.Tasks;
910
using System.Security.Principal;
1011
using System.Collections.Generic;
@@ -154,16 +155,19 @@ public string ExportSettings()
154155
/// <summary>
155156
/// Asynchronous exports the WinGet settings to a json string.
156157
/// </summary>
158+
/// <param name="cancellationToken">
159+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
160+
/// </param>
157161
/// <returns>
158162
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
159163
/// The result is a <see cref="System.String"/> containing the settings json.
160164
/// </returns>
161165
/// <exception cref="WGetNET.Exceptions.WinGetNotInstalledException">
162166
/// WinGet is not installed or not found on the system.
163167
/// </exception>
164-
public async Task<string> ExportSettingsAsync()
168+
public async Task<string> ExportSettingsAsync(CancellationToken cancellationToken = default)
165169
{
166-
ProcessResult result = await ExecuteAsync(_exportSettingsCmd);
170+
ProcessResult result = await ExecuteAsync(_exportSettingsCmd, false, cancellationToken);
167171

168172
return _outputReader.ExportOutputToString(result);
169173
}
@@ -222,6 +226,9 @@ public void ExportSettingsToFile(string file)
222226
/// <param name="file">
223227
/// The file for the export.
224228
/// </param>
229+
/// <param name="cancellationToken">
230+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
231+
/// </param>
225232
/// <returns>
226233
/// The <see cref="System.Threading.Tasks.Task"/> for the action.
227234
/// </returns>
@@ -254,14 +261,14 @@ public void ExportSettingsToFile(string file)
254261
/// <exception cref="System.Security.SecurityException">
255262
/// The caller does not have the required permission.
256263
/// </exception>
257-
public async Task ExportSettingsToFileAsync(string file)
264+
public async Task ExportSettingsToFileAsync(string file, CancellationToken cancellationToken = default)
258265
{
259266
ArgsHelper.ThrowIfStringIsNullOrWhiteSpace(file, "file");
260267
ArgsHelper.ThrowIfPathIsInvalid(file);
261268

262-
ProcessResult result = await ExecuteAsync(_exportSettingsCmd);
269+
ProcessResult result = await ExecuteAsync(_exportSettingsCmd, false, cancellationToken);
263270

264-
await FileHelper.WriteTextToFileAsync(file, OutputReader.ExportOutputToString(result));
271+
await FileHelper.WriteTextToFileAsync(file, OutputReader.ExportOutputToString(result), cancellationToken);
265272
}
266273
//---------------------------------------------------------------------------------------------
267274

@@ -308,21 +315,24 @@ public List<WinGetAdminSetting> GetAdminSettings()
308315
/// <summary>
309316
/// Asynchronously gets all winget admin settings.
310317
/// </summary>
318+
/// <param name="cancellationToken">
319+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
320+
/// </param>
311321
/// <returns>
312322
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
313323
/// The result is a <see cref="System.Collections.Generic.List{T}"/> of <see cref="WGetNET.WinGetAdminSetting"/> object.
314324
/// </returns>
315325
/// <exception cref="WGetNET.Exceptions.WinGetNotInstalledException">
316326
/// WinGet is not installed or not found on the system.
317327
/// </exception>
318-
public async Task<List<WinGetAdminSetting>> GetAdminSettingsAsync()
328+
public async Task<List<WinGetAdminSetting>> GetAdminSettingsAsync(CancellationToken cancellationToken = default)
319329
{
320330
List<WinGetAdminSetting> adminSettings = new();
321331

322-
string settingsJson = await ExportSettingsAsync();
332+
string settingsJson = await ExportSettingsAsync(cancellationToken);
323333

324334
#if NETCOREAPP3_1_OR_GREATER
325-
SettingsModel settings = await JsonHelper.StringToObjectAsync<SettingsModel>(settingsJson);
335+
SettingsModel settings = await JsonHelper.StringToObjectAsync<SettingsModel>(settingsJson, cancellationToken);
326336
#elif NETSTANDARD2_0
327337
SettingsModel settings = JsonHelper.StringToObject<SettingsModel>(settingsJson);
328338
#endif
@@ -334,6 +344,11 @@ public async Task<List<WinGetAdminSetting>> GetAdminSettingsAsync()
334344
AdminSettingBuilder builder = new();
335345
foreach (KeyValuePair<string, bool> entry in settings.AdminSettings)
336346
{
347+
if (cancellationToken.IsCancellationRequested)
348+
{
349+
return adminSettings;
350+
}
351+
337352
builder.Clear();
338353

339354
builder.AddEntryName(entry.Key);
@@ -415,6 +430,9 @@ public bool EnableAdminSetting(WinGetAdminSetting setting)
415430
/// <param name="settingName">
416431
/// Name of the admin setting to enable.
417432
/// </param>
433+
/// <param name="cancellationToken">
434+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
435+
/// </param>
418436
/// <returns>
419437
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
420438
/// The result is <see langword="true"/> if the action was succesfull and <see langword="false"/> if it failed.
@@ -431,13 +449,13 @@ public bool EnableAdminSetting(WinGetAdminSetting setting)
431449
/// <exception cref="System.Security.SecurityException">
432450
/// The current user is missing administrator privileges for this call.
433451
/// </exception>
434-
public async Task<bool> EnableAdminSettingAsync(string settingName)
452+
public async Task<bool> EnableAdminSettingAsync(string settingName, CancellationToken cancellationToken = default)
435453
{
436454
ArgsHelper.ThrowIfStringIsNullOrWhiteSpace(settingName, "settingName");
437455

438456
string cmd = string.Format(_settingsEnableCmd, settingName);
439457

440-
ProcessResult result = await ExecuteAsync(cmd, true);
458+
ProcessResult result = await ExecuteAsync(cmd, true, cancellationToken);
441459

442460
return result.Success;
443461
}
@@ -448,6 +466,9 @@ public async Task<bool> EnableAdminSettingAsync(string settingName)
448466
/// <param name="setting">
449467
/// The <see cref="WGetNET.WinGetAdminSetting"/> to enable.
450468
/// </param>
469+
/// <param name="cancellationToken">
470+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
471+
/// </param>
451472
/// <returns>
452473
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
453474
/// The result is <see langword="true"/> if the action was succesfull and <see langword="false"/> if it failed.
@@ -464,11 +485,11 @@ public async Task<bool> EnableAdminSettingAsync(string settingName)
464485
/// <exception cref="System.Security.SecurityException">
465486
/// The current user is missing administrator privileges for this call.
466487
/// </exception>
467-
public async Task<bool> EnableAdminSettingAsynv(WinGetAdminSetting setting)
488+
public async Task<bool> EnableAdminSettingAsync(WinGetAdminSetting setting, CancellationToken cancellationToken = default)
468489
{
469490
ArgsHelper.ThrowIfObjectIsNull(setting, "setting");
470491

471-
return await EnableAdminSettingAsync(setting.EntryName);
492+
return await EnableAdminSettingAsync(setting.EntryName, cancellationToken);
472493
}
473494

474495
/// <summary>
@@ -537,6 +558,9 @@ public bool DisableAdminSetting(WinGetAdminSetting setting)
537558
/// <param name="settingName">
538559
/// Name of the admin setting to disable.
539560
/// </param>
561+
/// <param name="cancellationToken">
562+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
563+
/// </param>
540564
/// <returns>
541565
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
542566
/// The result is <see langword="true"/> if the action was succesfull and <see langword="false"/> if it failed.
@@ -553,13 +577,13 @@ public bool DisableAdminSetting(WinGetAdminSetting setting)
553577
/// <exception cref="System.Security.SecurityException">
554578
/// The current user is missing administrator privileges for this call.
555579
/// </exception>
556-
public async Task<bool> DisableAdminSettingAsync(string settingName)
580+
public async Task<bool> DisableAdminSettingAsync(string settingName, CancellationToken cancellationToken = default)
557581
{
558582
ArgsHelper.ThrowIfStringIsNullOrWhiteSpace(settingName, "settingName");
559583

560584
string cmd = string.Format(_settingsDisableCmd, settingName);
561585

562-
ProcessResult result = await ExecuteAsync(cmd, true);
586+
ProcessResult result = await ExecuteAsync(cmd, true, cancellationToken);
563587

564588
return result.Success;
565589
}
@@ -570,6 +594,9 @@ public async Task<bool> DisableAdminSettingAsync(string settingName)
570594
/// <param name="setting">
571595
/// The <see cref="WGetNET.WinGetAdminSetting"/> to disable.
572596
/// </param>
597+
/// <param name="cancellationToken">
598+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
599+
/// </param>
573600
/// <returns>
574601
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
575602
/// The result is <see langword="true"/> if the action was succesfull and <see langword="false"/> if it failed.
@@ -586,11 +613,11 @@ public async Task<bool> DisableAdminSettingAsync(string settingName)
586613
/// <exception cref="System.Security.SecurityException">
587614
/// The current user is missing administrator privileges for this call.
588615
/// </exception>
589-
public async Task<bool> DisableAdminSettingAsync(WinGetAdminSetting setting)
616+
public async Task<bool> DisableAdminSettingAsync(WinGetAdminSetting setting, CancellationToken cancellationToken = default)
590617
{
591618
ArgsHelper.ThrowIfObjectIsNull(setting, "setting");
592619

593-
return await DisableAdminSettingAsync(setting.EntryName);
620+
return await DisableAdminSettingAsync(setting.EntryName, cancellationToken);
594621
}
595622
//---------------------------------------------------------------------------------------------
596623

@@ -628,17 +655,21 @@ public WinGetInfo GetInfo()
628655
/// <summary>
629656
/// Asynchronous gets all WinGet related data provided by the WinGet info action.
630657
/// </summary>
658+
/// <param name="cancellationToken">
659+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
660+
/// </param>
631661
/// <returns>
632662
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
633663
/// The result is a <see cref="WGetNET.WinGetInfo"/> object containing winget related information.
634664
/// </returns>
635665
/// <exception cref="WGetNET.Exceptions.WinGetNotInstalledException">
636666
/// WinGet is not installed or not found on the system.
637667
/// </exception>
638-
public async Task<WinGetInfo> GetInfoAsync()
668+
public async Task<WinGetInfo> GetInfoAsync(CancellationToken cancellationToken = default)
639669
{
640-
ProcessResult result = await ExecuteAsync(_infoCmd);
670+
ProcessResult result = await ExecuteAsync(_infoCmd, false, cancellationToken);
641671

672+
// Check the version range the action should be performed for
642673
InfoActionVersionId actionVersionId = InfoActionVersionId.VersionRange1;
643674
if (CheckWinGetVersion(new Version(1, 4, 3531), new Version(1, 5, 101)))
644675
{
@@ -653,6 +684,12 @@ public async Task<WinGetInfo> GetInfoAsync()
653684
actionVersionId = InfoActionVersionId.VersionRange4;
654685
}
655686

687+
// Return empty data if the task was canceled
688+
if (cancellationToken.IsCancellationRequested)
689+
{
690+
return WinGetInfo.Empty;
691+
}
692+
656693
return _outputReader.ToWingetInfo(result.Output, actionVersionId);
657694
}
658695
//---------------------------------------------------------------------------------------------
@@ -723,6 +760,9 @@ private protected ProcessResult Execute(string args, bool needsAdminRights = fal
723760
/// <param name="needsAdminRights">
724761
/// Sets if the process that should be executed needs administrator privileges.
725762
/// </param>
763+
/// <param name="cancellationToken">
764+
/// The <see cref="System.Threading.CancellationToken"/> for the <see cref="System.Threading.Tasks.Task"/>.
765+
/// </param>
726766
/// <returns>
727767
/// A <see cref="System.Threading.Tasks.Task"/>, containing the result.
728768
/// The result is the <see cref="WGetNET.Models.ProcessResult"/> for the process.
@@ -734,7 +774,7 @@ private protected ProcessResult Execute(string args, bool needsAdminRights = fal
734774
/// The current process does not have administrator privileges.
735775
/// (Only if <paramref name="needsAdminRights"/> is set to <see langword="true"/>)
736776
/// </exception>
737-
private protected async Task<ProcessResult> ExecuteAsync(string args, bool needsAdminRights = false)
777+
private protected async Task<ProcessResult> ExecuteAsync(string args, bool needsAdminRights = false, CancellationToken cancellationToken = default)
738778
{
739779
ThrowIfNotInstalled();
740780

@@ -743,7 +783,7 @@ private protected async Task<ProcessResult> ExecuteAsync(string args, bool needs
743783
ThrowIfNotAdmin();
744784
}
745785

746-
return await _processManager.ExecuteWingetProcessAsync(args);
786+
return await _processManager.ExecuteWingetProcessAsync(args, cancellationToken);
747787
}
748788
// \endcond
749789
//---------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)