-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShellWrapper.cs
More file actions
97 lines (85 loc) · 3.95 KB
/
Copy pathShellWrapper.cs
File metadata and controls
97 lines (85 loc) · 3.95 KB
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
// ============================================================
// File: ShellWrapper.cs
// Project: OpticCli
// Namespace: OpticCli
// Description: Runs PowerShell commands in a hidden process and
// returns the output. Cleans up CLIXML error format
// and handles progress stream noise from PowerShell.
// ============================================================
using System;
using System.Diagnostics;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace OpticCli
{
public class ShellWrapper
{
public async Task<string> ExecuteCommandAsync(string command)
{
return await Task.Run(() =>
{
try
{
// Silence PowerShell's progress stream to avoid polluting the error output
string safeCommand = $"$ProgressPreference = 'SilentlyContinue'; {command}";
var plainTextBytes = System.Text.Encoding.Unicode.GetBytes(safeCommand);
string encodedCommand = Convert.ToBase64String(plainTextBytes);
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = "powershell.exe",
Arguments = $"-NoProfile -EncodedCommand {encodedCommand}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = System.Environment.GetFolderPath(
System.Environment.SpecialFolder.UserProfile)
};
using (Process process = new Process())
{
process.StartInfo = processInfo;
process.Start();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
if (!string.IsNullOrEmpty(error))
{
if (error.Contains("#< CLIXML"))
{
// Extract ONLY the actual error text inside the <S S="Error"> tags
var errorMatches = Regex.Matches(error, @"<S S=""Error"">(.*?)</S>", RegexOptions.Singleline);
if (errorMatches.Count > 0)
{
string cleanError = "";
foreach (Match match in errorMatches)
{
cleanError += match.Groups[1].Value;
}
error = WebUtility.HtmlDecode(cleanError);
error = error.Replace("_x000D__x000A_", "\n")
.Replace("_x000D_", "")
.Replace("_x000A_", "\n");
}
else
{
error = string.Empty;
}
}
error = error.Trim();
if (!string.IsNullOrEmpty(error))
return $"[ERROR]\n{error}";
}
return string.IsNullOrEmpty(output)
? "[Command Executed Successfully - No Output Returned]"
: output.Trim();
}
}
catch (Exception ex)
{
return $"[SYSTEM EXCEPTION]\n{ex.Message}";
}
});
}
}
}