Codemaru 1.0.0.0
QR 코드, 모바일 랜딩 페이지, vCard 연락처 저장과 명함 디자인을 한 화면에서 제공하는 디지털 명함 서비스입니다.
로딩중...
검색중...
일치하는것 없음
ProcessRunner.cs
이 파일의 문서화 페이지로 가기
2using System.Diagnostics;
3using System.IO;
4using System.Text;
5
7
16public sealed class ProcessRunner : IProcessRunner
17{
82 public async Task<ProcessExecutionResult> RunAsync(
83 string fileName,
84 string arguments,
85 string? workingDirectory,
86 TimeSpan timeout,
87 int maxOutputChars,
88 CancellationToken cancellationToken)
89 {
90 if (string.IsNullOrWhiteSpace(fileName))
91 {
92 return new ProcessExecutionResult
93 {
94 IsSuccess = false,
95 ExitCode = -1,
96 Message = "Executable path is empty."
97 };
98 }
99
100 if (!File.Exists(fileName))
101 {
102 return new ProcessExecutionResult
103 {
104 IsSuccess = false,
105 ExitCode = -1,
106 Message = $"Executable not found: {fileName}"
107 };
108 }
109
110 string resolvedWorkingDirectory = ResolveWorkingDirectory(fileName, workingDirectory);
111 if (!Directory.Exists(resolvedWorkingDirectory))
112 {
113 return new ProcessExecutionResult
114 {
115 IsSuccess = false,
116 ExitCode = -1,
117 Message = $"Working directory not found: {resolvedWorkingDirectory}"
118 };
119 }
120
121 StringBuilder output = new();
122 StringBuilder error = new();
123 int outputLimit = maxOutputChars <= 0 ? 6000 : maxOutputChars;
124
125 ProcessStartInfo startInfo = new()
126 {
127 FileName = fileName,
128 Arguments = arguments,
129 WorkingDirectory = resolvedWorkingDirectory,
130 UseShellExecute = false,
131 RedirectStandardOutput = true,
132 RedirectStandardError = true,
133 CreateNoWindow = true
134 };
135
136 using Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true };
137
138 try
139 {
140 process.OutputDataReceived += (_, e) => AppendLine(output, e.Data, outputLimit);
141 process.ErrorDataReceived += (_, e) => AppendLine(error, e.Data, outputLimit);
142
143 if (!process.Start())
144 {
145 return new ProcessExecutionResult
146 {
147 IsSuccess = false,
148 ExitCode = -1,
149 Message = $"Failed to start process: {fileName}"
150 };
151 }
152
153 process.BeginOutputReadLine();
154 process.BeginErrorReadLine();
155
156 using CancellationTokenSource timeoutSource = new(timeout);
157 using CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(
158 cancellationToken,
159 timeoutSource.Token);
160
161 try
162 {
163 await process.WaitForExitAsync(linkedSource.Token).ConfigureAwait(false);
164 }
165 catch (OperationCanceledException)
166 {
167 TryKill(process);
168
169 return new ProcessExecutionResult
170 {
171 IsSuccess = false,
172 ExitCode = -1,
173 Output = output.ToString(),
174 Error = error.ToString(),
175 Message = timeoutSource.IsCancellationRequested
176 ? $"Process timed out: {fileName} {arguments}"
177 : $"Process canceled: {fileName} {arguments}"
178 };
179 }
180
181 return new ProcessExecutionResult
182 {
183 IsSuccess = process.ExitCode == 0,
184 ExitCode = process.ExitCode,
185 Output = output.ToString(),
186 Error = error.ToString(),
187 Message = process.ExitCode == 0
188 ? "Process completed successfully."
189 : $"Process failed with exit code {process.ExitCode}."
190 };
191 }
192 catch (Exception ex)
193 {
194 return new ProcessExecutionResult
195 {
196 IsSuccess = false,
197 ExitCode = -1,
198 Output = output.ToString(),
199 Error = error.ToString(),
200 Message = ex.Message
201 };
202 }
203 }
204
237 private static string ResolveWorkingDirectory(string fileName, string? workingDirectory)
238 {
239 if (!string.IsNullOrWhiteSpace(workingDirectory))
240 {
241 return workingDirectory;
242 }
243
244 return Path.GetDirectoryName(fileName) ?? Environment.CurrentDirectory;
245 }
246
279 private static void AppendLine(StringBuilder builder, string? line, int maxOutputChars)
280 {
281 if (string.IsNullOrEmpty(line))
282 {
283 return;
284 }
285
286 if (builder.Length >= maxOutputChars)
287 {
288 return;
289 }
290
291 builder.AppendLine(line);
292
293 if (builder.Length > maxOutputChars)
294 {
295 builder.Length = maxOutputChars;
296 }
297 }
298
315 private static void TryKill(Process process)
316 {
317 try
318 {
319 if (!process.HasExited)
320 {
321 process.Kill(entireProcessTree: true);
322 }
323 }
324 catch
325 {
326 // Process cleanup best effort.
327 }
328 }
329}
async Task< ProcessExecutionResult > RunAsync(string fileName, string arguments, string? workingDirectory, TimeSpan timeout, int maxOutputChars, CancellationToken cancellationToken)
static void AppendLine(StringBuilder builder, string? line, int maxOutputChars)
static string ResolveWorkingDirectory(string fileName, string? workingDirectory)