CLI tool for running Playbooks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

716 lines
33 KiB

1 year ago
6 months ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
10 months ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
10 months ago
6 months ago
10 months ago
6 months ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
10 months ago
6 months ago
10 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
10 months ago
1 year ago
10 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
6 months ago
1 year ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Configuration.Install;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Management;
  8. using System.Reflection;
  9. using System.Runtime.InteropServices;
  10. using System.Security.AccessControl;
  11. using System.ServiceProcess;
  12. using System.Text.RegularExpressions;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. using System.Windows;
  16. using TrustedUninstaller.Shared.Exceptions;
  17. using TrustedUninstaller.Shared.Tasks;
  18. using YamlDotNet.Serialization;
  19. namespace TrustedUninstaller.Shared.Actions
  20. {
  21. public class FileAction : TaskAction, ITaskAction
  22. {
  23. public void RunTaskOnMainThread() { throw new NotImplementedException(); }
  24. [YamlMember(typeof(string), Alias = "path")]
  25. public string RawPath { get; set; }
  26. [YamlMember(typeof(string), Alias = "prioritizeExe")]
  27. public bool ExeFirst { get; set; } = false;
  28. [YamlMember(typeof(string), Alias = "weight")]
  29. public int ProgressWeight { get; set; } = 2;
  30. [YamlMember(typeof(string), Alias = "useNSudoTI")]
  31. public bool TrustedInstaller { get; set; } = false;
  32. public int GetProgressWeight() => ProgressWeight;
  33. private bool InProgress { get; set; }
  34. public void ResetProgress() => InProgress = false;
  35. public string ErrorString() => $"FileAction failed to remove file or directory '{Environment.ExpandEnvironmentVariables(RawPath)}'.";
  36. private string GetRealPath()
  37. {
  38. return Environment.ExpandEnvironmentVariables(RawPath);
  39. }
  40. private string GetRealPath(string path)
  41. {
  42. return Environment.ExpandEnvironmentVariables(path);
  43. }
  44. public UninstallTaskStatus GetStatus()
  45. {
  46. if (InProgress) return UninstallTaskStatus.InProgress; var realPath = GetRealPath();
  47. if (realPath.Contains("*"))
  48. {
  49. var lastToken = realPath.LastIndexOf("\\");
  50. var parentPath = realPath.Remove(lastToken).TrimEnd('\\');
  51. // This is to prevent it from re-iterating with an incorrect argument
  52. if (parentPath.Contains("*")) return UninstallTaskStatus.Completed;
  53. var filter = realPath.Substring(lastToken + 1);
  54. if (Directory.Exists(parentPath) && (Directory.GetFiles(parentPath, filter).Any() || Directory.GetDirectories(parentPath, filter).Any()))
  55. {
  56. return UninstallTaskStatus.ToDo;
  57. }
  58. else return UninstallTaskStatus.Completed;
  59. }
  60. var isFile = File.Exists(realPath);
  61. var isDirectory = Directory.Exists(realPath);
  62. return isFile || isDirectory ? UninstallTaskStatus.ToDo : UninstallTaskStatus.Completed;
  63. }
  64. [DllImport("Unlocker.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
  65. private static extern bool EzUnlockFileW(string path);
  66. private async Task DeleteFile(string file, bool log = false)
  67. {
  68. if (!TrustedInstaller)
  69. {
  70. try { File.Delete(file);} catch (Exception e) { }
  71. if (File.Exists(file))
  72. {
  73. try
  74. {
  75. var result = EzUnlockFileW(file);
  76. Testing.WriteLine($"ExUnlock on ({file}) result: " + result);
  77. }
  78. catch (Exception e)
  79. {
  80. ErrorLogger.WriteToErrorLog($"Error while unlocking file: " + e.Message, e.StackTrace,
  81. $"FileAction Error", file);
  82. }
  83. try {await Task.Run(() => File.Delete(file));} catch (Exception e) {Testing.WriteLine(e, "DeleteFile > File.Delete(File)");}
  84. CmdAction delAction = new CmdAction()
  85. {
  86. Command = $"del /q /f \"{file}\""
  87. };
  88. delAction.RunTaskOnMainThread();
  89. }
  90. }
  91. else if (File.Exists("NSudoLC.exe"))
  92. {
  93. try
  94. {
  95. var result = EzUnlockFileW(file);
  96. Testing.WriteLine($"ExUnlock on ({file}) result: " + result);
  97. }
  98. catch (Exception e)
  99. {
  100. ErrorLogger.WriteToErrorLog($"Error while unlocking file: " + e.Message, e.StackTrace,
  101. $"FileAction Error", file);
  102. }
  103. RunAction tiDelAction = new RunAction()
  104. {
  105. Exe = "NSudoLC.exe",
  106. Arguments = $"-U:T -P:E -M:S -Priority:RealTime -UseCurrentConsole -Wait cmd /c \"del /q /f \"{file}\"\"",
  107. BaseDir = true,
  108. CreateWindow = false
  109. };
  110. tiDelAction.RunTaskOnMainThread();
  111. if (tiDelAction.Output != null)
  112. {
  113. if (log) ErrorLogger.WriteToErrorLog(tiDelAction.Output, Environment.StackTrace,
  114. $"FileAction Error", file);
  115. }
  116. }
  117. else
  118. {
  119. ErrorLogger.WriteToErrorLog($"NSudo was invoked with no supplied NSudo executable.", Environment.StackTrace,
  120. $"FileAction Error", file);
  121. }
  122. }
  123. private async Task RemoveDirectory(string dir, bool log = false)
  124. {
  125. if (!TrustedInstaller)
  126. {
  127. try { Directory.Delete(dir, true); } catch { }
  128. if (Directory.Exists(dir))
  129. {
  130. Console.WriteLine("Directory still exists.. trying second method.");
  131. var deleteDirCmd = new CmdAction()
  132. {
  133. Command = $"rmdir /Q /S \"{dir}\""
  134. };
  135. deleteDirCmd.RunTaskOnMainThread();
  136. if (deleteDirCmd.StandardError != null)
  137. {
  138. Console.WriteLine($"Error Output: {deleteDirCmd.StandardError}");
  139. }
  140. if (deleteDirCmd.StandardOutput != null)
  141. {
  142. Console.WriteLine($"Standard Output: {deleteDirCmd.StandardOutput}");
  143. }
  144. }
  145. }
  146. else if (File.Exists("NSudoLC.exe"))
  147. {
  148. RunAction tiDelAction = new RunAction()
  149. {
  150. Exe = "NSudoLC.exe",
  151. Arguments = $"-U:T -P:E -M:S -Priority:RealTime -UseCurrentConsole -Wait cmd /c \"rmdir /q /s \"{dir}\"\"",
  152. BaseDir = true,
  153. CreateWindow = false
  154. };
  155. tiDelAction.RunTaskOnMainThread();
  156. if (tiDelAction.Output != null)
  157. {
  158. if (log) ErrorLogger.WriteToErrorLog(tiDelAction.Output, Environment.StackTrace,
  159. $"FileAction Error", dir);
  160. }
  161. }
  162. else
  163. {
  164. ErrorLogger.WriteToErrorLog($"NSudo was invoked with no supplied NSudo executable.", Environment.StackTrace,
  165. $"FileAction Error", dir);
  166. }
  167. }
  168. private async Task DeleteItemsInDirectory(string dir, string filter = "*")
  169. {
  170. var realPath = GetRealPath(dir);
  171. var files = Directory.EnumerateFiles(realPath, filter);
  172. var directories = Directory.EnumerateDirectories(realPath, filter);
  173. if (ExeFirst) files = files.ToList().OrderByDescending(x => x.EndsWith(".exe"));
  174. var lockedFilesList = new List<string> { "MpOAV.dll", "MsMpLics.dll", "EppManifest.dll", "MpAsDesc.dll", "MpClient.dll", "MsMpEng.exe" };
  175. foreach (var file in files)
  176. {
  177. Console.WriteLine($"Deleting {file}...");
  178. System.GC.Collect();
  179. System.GC.WaitForPendingFinalizers();
  180. await DeleteFile(file);
  181. if (File.Exists(file))
  182. {
  183. TaskKillAction taskKillAction = new TaskKillAction();
  184. if (file.EndsWith(".sys"))
  185. {
  186. var driverService = Path.GetFileNameWithoutExtension(file);
  187. try
  188. {
  189. //ServiceAction won't work here due to it not being able to detect driver services.
  190. var cmdAction = new CmdAction();
  191. Console.WriteLine($"Removing driver service {driverService}...");
  192. // TODO: Replace with win32
  193. try
  194. {
  195. ServiceInstaller ServiceInstallerObj = new ServiceInstaller();
  196. ServiceInstallerObj.Context = new InstallContext();
  197. ServiceInstallerObj.ServiceName = driverService;
  198. ServiceInstallerObj.Uninstall(null);
  199. }
  200. catch (Exception e)
  201. {
  202. ErrorLogger.WriteToErrorLog("Service uninstall failed: " + e.Message, e.StackTrace, "FileAction Warning", RawPath);
  203. }
  204. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  205. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" :
  206. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop";
  207. if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
  208. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  209. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" :
  210. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete";
  211. if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
  212. }
  213. catch (Exception servException)
  214. {
  215. ErrorLogger.WriteToErrorLog(servException.Message, servException.StackTrace,
  216. $"FileAction Error: Error while trying to delete driver service {driverService}.", file);
  217. }
  218. }
  219. if (lockedFilesList.Contains(Path.GetFileName(file)))
  220. {
  221. TaskKillAction killAction = new TaskKillAction()
  222. {
  223. ProcessName = "MsMpEng"
  224. };
  225. await killAction.RunTask();
  226. killAction.ProcessName = "NisSrv";
  227. await killAction.RunTask();
  228. killAction.ProcessName = "SecurityHealthService";
  229. await killAction.RunTask();
  230. killAction.ProcessName = "smartscreen";
  231. await killAction.RunTask();
  232. }
  233. var processes = new List<Process>();
  234. try
  235. {
  236. processes = WinUtil.WhoIsLocking(file);
  237. }
  238. catch (Exception e)
  239. {
  240. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  241. $"FileAction Error", file);
  242. }
  243. var delay = 0;
  244. int svcCount = 0;
  245. foreach (var svchost in processes.Where(x => x.ProcessName.Equals("svchost")))
  246. {
  247. try
  248. {
  249. foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(svchost.Id))
  250. {
  251. svcCount++;
  252. try
  253. {
  254. var serviceController = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
  255. if (serviceController != null)
  256. svcCount += serviceController.DependentServices.Length;
  257. }
  258. catch (Exception e)
  259. {
  260. Console.WriteLine($"\r\nError: Could not get amount of dependent services for {serviceName}.\r\nException: " + e.Message);
  261. }
  262. }
  263. } catch (Exception e)
  264. {
  265. Console.WriteLine($"\r\nError: Could not get amount of services locking file.\r\nException: " + e.Message);
  266. }
  267. }
  268. while (processes.Any() && delay <= 800)
  269. {
  270. Console.WriteLine("Processes locking the file:");
  271. foreach (var process in processes)
  272. {
  273. Console.WriteLine(process.ProcessName);
  274. }
  275. if (svcCount > 10)
  276. {
  277. Console.WriteLine("Amount of locking services exceeds 10, skipping...");
  278. break;
  279. }
  280. foreach (var process in processes)
  281. {
  282. try
  283. {
  284. if (process.ProcessName.Equals("TrustedUninstaller.CLI"))
  285. {
  286. Console.WriteLine("Skipping TU.CLI...");
  287. continue;
  288. }
  289. if (Regex.Match(process.ProcessName, "ame.?wizard", RegexOptions.IgnoreCase).Success)
  290. {
  291. Console.WriteLine("Skipping AME Wizard...");
  292. continue;
  293. }
  294. taskKillAction.ProcessName = process.ProcessName;
  295. taskKillAction.ProcessID = process.Id;
  296. Console.WriteLine($"Killing locking process {process.ProcessName} with PID {process.Id}...");
  297. }
  298. catch (InvalidOperationException)
  299. {
  300. // Calling ProcessName on a process object that has exited will thrown this exception causing the
  301. // entire loop to abort. Since killing a process takes a bit of time, another process in the loop
  302. // could exit during that time. This accounts for that.
  303. continue;
  304. }
  305. try
  306. {
  307. await taskKillAction.RunTask();
  308. }
  309. catch (Exception e)
  310. {
  311. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  312. $"FileAction Error: Could not kill process {taskKillAction.ProcessName}.");
  313. }
  314. }
  315. // This gives any obstinant processes some time to unlock the file on their own.
  316. //
  317. // This could be done above but it's likely to cause HasExited errors if delays are
  318. // introduced after WhoIsLocking.
  319. System.Threading.Thread.Sleep(delay);
  320. try
  321. {
  322. processes = WinUtil.WhoIsLocking(file);
  323. }
  324. catch (Exception e)
  325. {
  326. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  327. $"FileAction Error", file);
  328. }
  329. delay += 100;
  330. }
  331. if (delay >= 800)
  332. ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{file}'. Process termination loop exceeded max cycles (8).",
  333. Environment.StackTrace, "FileAction Error");
  334. if (Path.GetExtension(file).Equals(".exe", StringComparison.OrdinalIgnoreCase))
  335. {
  336. await new TaskKillAction() { ProcessName = Path.GetFileNameWithoutExtension(file) }.RunTask();
  337. }
  338. await DeleteFile(file, true);
  339. }
  340. }
  341. //Loop through any subdirectories
  342. foreach (var directory in directories)
  343. {
  344. //Deletes the content of the directory
  345. await DeleteItemsInDirectory(directory);
  346. System.GC.Collect();
  347. System.GC.WaitForPendingFinalizers();
  348. await RemoveDirectory(directory, true);
  349. if (Directory.Exists(directory))
  350. ErrorLogger.WriteToErrorLog($"Could not remove directory '{directory}'.",
  351. Environment.StackTrace, $"FileAction Error");
  352. }
  353. }
  354. public async Task<bool> RunTask()
  355. {
  356. if (InProgress) throw new TaskInProgressException("Another File action was called while one was in progress.");
  357. InProgress = true;
  358. var realPath = GetRealPath();
  359. Console.WriteLine($"Removing file or directory '{realPath}'...");
  360. if (realPath.Contains("*"))
  361. {
  362. var lastToken = realPath.LastIndexOf("\\");
  363. var parentPath = realPath.Remove(lastToken).TrimEnd('\\');
  364. if (parentPath.Contains("*")) throw new ArgumentException("Parent directories to a given file filter cannot contain wildcards.");
  365. var filter = realPath.Substring(lastToken + 1);
  366. await DeleteItemsInDirectory(parentPath, filter);
  367. InProgress = false;
  368. return true;
  369. }
  370. var isFile = File.Exists(realPath);
  371. var isDirectory = Directory.Exists(realPath);
  372. if (isDirectory)
  373. {
  374. System.GC.Collect();
  375. System.GC.WaitForPendingFinalizers();
  376. await RemoveDirectory(realPath);
  377. if (Directory.Exists(realPath))
  378. {
  379. CmdAction permAction = new CmdAction()
  380. {
  381. Command = $"takeown /f \"{realPath}\" /r /d Y>NUL & icacls \"{realPath}\" /t /grant Administrators:F /c > NUL",
  382. Timeout = 5000
  383. };
  384. try
  385. {
  386. permAction.RunTaskOnMainThread();
  387. }
  388. catch (Exception e)
  389. {
  390. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "FileAction Error", realPath);
  391. }
  392. try
  393. {
  394. if (realPath.Contains("Defender"))
  395. {
  396. TaskKillAction killAction = new TaskKillAction()
  397. {
  398. ProcessName = "MsMpEng"
  399. };
  400. await killAction.RunTask();
  401. killAction.ProcessName = "NisSrv";
  402. await killAction.RunTask();
  403. killAction.ProcessName = "SecurityHealthService";
  404. await killAction.RunTask();
  405. killAction.ProcessName = "smartscreen";
  406. await killAction.RunTask();
  407. }
  408. }
  409. catch (Exception e)
  410. {
  411. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  412. $"FileAction Error", realPath);
  413. }
  414. await RemoveDirectory(realPath, true);
  415. if (Directory.Exists(realPath))
  416. {
  417. //Delete the files in the initial directory. DOES delete directories.
  418. await DeleteItemsInDirectory(realPath);
  419. System.GC.Collect();
  420. System.GC.WaitForPendingFinalizers();
  421. await RemoveDirectory(realPath, true);
  422. }
  423. }
  424. }
  425. if (isFile)
  426. {
  427. try
  428. {
  429. var lockedFilesList = new List<string> { "MpOAV.dll", "MsMpLics.dll", "EppManifest.dll", "MpAsDesc.dll", "MpClient.dll", "MsMpEng.exe" };
  430. var fileName = realPath.Split('\\').LastOrDefault();
  431. System.GC.Collect();
  432. System.GC.WaitForPendingFinalizers();
  433. await DeleteFile(realPath);
  434. if (File.Exists(realPath))
  435. {
  436. CmdAction permAction = new CmdAction()
  437. {
  438. Command = $"takeown /f \"{realPath}\" /r /d Y>NUL & icacls \"{realPath}\" /t /grant Administrators:F /c > NUL",
  439. Timeout = 5000
  440. };
  441. try
  442. {
  443. permAction.RunTaskOnMainThread();
  444. }
  445. catch (Exception e)
  446. {
  447. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace, "FileAction Error", realPath);
  448. }
  449. TaskKillAction taskKillAction = new TaskKillAction();
  450. if (realPath.EndsWith(".sys"))
  451. {
  452. var driverService = Path.GetFileNameWithoutExtension(realPath);
  453. try
  454. {
  455. //ServiceAction won't work here due to it not being able to detect driver services.
  456. var cmdAction = new CmdAction();
  457. Console.WriteLine($"Removing driver service {driverService}...");
  458. // TODO: Replace with win32
  459. try
  460. {
  461. ServiceInstaller ServiceInstallerObj = new ServiceInstaller();
  462. ServiceInstallerObj.Context = new InstallContext();
  463. ServiceInstallerObj.ServiceName = driverService;
  464. ServiceInstallerObj.Uninstall(null);
  465. }
  466. catch (Exception e)
  467. {
  468. ErrorLogger.WriteToErrorLog("Service uninstall failed: " + e.Message, e.StackTrace, "FileAction Warning", RawPath);
  469. }
  470. WinUtil.CheckKph();
  471. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  472. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop" :
  473. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction stop";
  474. if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
  475. cmdAction.Command = Environment.Is64BitOperatingSystem ?
  476. $"ProcessHacker\\x64\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete" :
  477. $"ProcessHacker\\x86\\ProcessHacker.exe -s -elevate -c -ctype service -cobject {driverService} -caction delete";
  478. if (AmeliorationUtil.UseKernelDriver) cmdAction.RunTaskOnMainThread();
  479. }
  480. catch (Exception servException)
  481. {
  482. ErrorLogger.WriteToErrorLog(servException.Message, servException.StackTrace,
  483. $"FileAction Error: Error trying to delete driver service {driverService}.", realPath);
  484. }
  485. }
  486. if (lockedFilesList.Contains(fileName))
  487. {
  488. TaskKillAction killAction = new TaskKillAction()
  489. {
  490. ProcessName = "MsMpEng"
  491. };
  492. await killAction.RunTask();
  493. killAction.ProcessName = "NisSrv";
  494. await killAction.RunTask();
  495. killAction.ProcessName = "SecurityHealthService";
  496. await killAction.RunTask();
  497. killAction.ProcessName = "smartscreen";
  498. await killAction.RunTask();
  499. }
  500. var processes = new List<Process>();
  501. try
  502. {
  503. processes = WinUtil.WhoIsLocking(realPath);
  504. }
  505. catch (Exception e)
  506. {
  507. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  508. $"FileAction Error", realPath);
  509. }
  510. var delay = 0;
  511. int svcCount = 0;
  512. foreach (var svchost in processes.Where(x => x.ProcessName.Equals("svchost")))
  513. {
  514. try
  515. {
  516. foreach (var serviceName in Win32.ServiceEx.GetServicesFromProcessId(svchost.Id))
  517. {
  518. svcCount++;
  519. try
  520. {
  521. var serviceController = ServiceController.GetServices().FirstOrDefault(x => x.ServiceName.Equals(serviceName));
  522. if (serviceController != null)
  523. svcCount += serviceController.DependentServices.Length;
  524. }
  525. catch (Exception e)
  526. {
  527. Console.WriteLine($"\r\nError: Could not get amount of dependent services for {serviceName}.\r\nException: " + e.Message);
  528. }
  529. }
  530. } catch (Exception e)
  531. {
  532. Console.WriteLine($"\r\nError: Could not get amount of services locking file.\r\nException: " + e.Message);
  533. }
  534. }
  535. if (svcCount > 8) Console.WriteLine("Amount of locking services exceeds 8, skipping...");
  536. while (processes.Any() && delay <= 800 && svcCount <= 8)
  537. {
  538. Console.WriteLine("Processes locking the file:");
  539. foreach (var process in processes)
  540. {
  541. Console.WriteLine(process.ProcessName);
  542. }
  543. foreach (var process in processes)
  544. {
  545. try
  546. {
  547. if (process.ProcessName.Equals("TrustedUninstaller.CLI"))
  548. {
  549. Console.WriteLine("Skipping TU.CLI...");
  550. continue;
  551. }
  552. if (Regex.Match(process.ProcessName, "ame.?wizard", RegexOptions.IgnoreCase).Success)
  553. {
  554. Console.WriteLine("Skipping AME Wizard...");
  555. continue;
  556. }
  557. taskKillAction.ProcessName = process.ProcessName;
  558. taskKillAction.ProcessID = process.Id;
  559. Console.WriteLine($"Killing {process.ProcessName} with PID {process.Id}... it is locking {realPath}");
  560. }
  561. catch (InvalidOperationException)
  562. {
  563. // Calling ProcessName on a process object that has exited will thrown this exception causing the
  564. // entire loop to abort. Since killing a process takes a bit of time, another process in the loop
  565. // could exit during that time. This accounts for that.
  566. continue;
  567. }
  568. try
  569. {
  570. await taskKillAction.RunTask();
  571. }
  572. catch (Exception e)
  573. {
  574. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  575. $"FileAction Error: Could not kill process {process.ProcessName}.");
  576. }
  577. }
  578. // This gives any obstinant processes some time to unlock the file on their own.
  579. //
  580. // This could be done above but it's likely to cause HasExited errors if delays are
  581. // introduced after WhoIsLocking.
  582. System.Threading.Thread.Sleep(delay);
  583. try
  584. {
  585. processes = WinUtil.WhoIsLocking(realPath);
  586. }
  587. catch (Exception e)
  588. {
  589. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  590. $"FileAction Error", realPath);
  591. }
  592. delay += 100;
  593. }
  594. if (delay >= 800)
  595. ErrorLogger.WriteToErrorLog($"Could not kill locking processes for file '{realPath}'. Process termination loop exceeded max cycles (8).",
  596. Environment.StackTrace, "FileAction Error");
  597. if (Path.GetExtension(realPath).Equals(".exe", StringComparison.OrdinalIgnoreCase))
  598. {
  599. await new TaskKillAction() { ProcessName = Path.GetFileNameWithoutExtension(realPath) }.RunTask();
  600. }
  601. await DeleteFile(realPath, true);
  602. }
  603. }
  604. catch (Exception e)
  605. {
  606. ErrorLogger.WriteToErrorLog(e.Message, e.StackTrace,
  607. $"FileAction Error: Error while trying to delete {realPath}.");
  608. }
  609. }
  610. else
  611. {
  612. Console.WriteLine($"File or directory '{realPath}' not found.");
  613. }
  614. InProgress = false;
  615. return true;
  616. }
  617. }
  618. }