Script for automating a large assortment of AME related actions
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.

260 lines
12 KiB

  1. using System;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Security;
  6. using System.Windows.Forms;
  7. using Ameliorated.ConsoleUtils;
  8. using Microsoft.Dism;
  9. // Asks user to select Windows installation media, and mounts it if applicable
  10. // Returns path to where it's mounted
  11. namespace amecs.Misc
  12. {
  13. public static class SelectWindowsImage
  14. {
  15. private static string _fileViolationTest;
  16. private static bool CheckFileViolation(string inputFile)
  17. {
  18. try
  19. {
  20. _fileViolationTest = inputFile;
  21. }
  22. catch (SecurityException e)
  23. {
  24. Console.WriteLine();
  25. ConsoleTUI.OpenFrame.Close("Security exception: " + e.Message, ConsoleColor.Red, Console.BackgroundColor, new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  26. return true;
  27. }
  28. return false;
  29. }
  30. public static string GetWindowsVersion(float majorMinor, int isoBuild)
  31. {
  32. return (majorMinor, isoBuild) switch
  33. {
  34. (6, _) => "Windows Vista",
  35. (6.1f, _) => "Windows 7",
  36. (6.2f, _) => "Windows 8",
  37. (6.3f, _) => "Windows 8.1",
  38. (10, var a) when a < 19041 => "Windows 10 (Old)",
  39. (10, var a) when a >= 22000 => "Windows 11",
  40. (10, _) => "Windows 10",
  41. _ => "Unknown"
  42. };
  43. }
  44. public static bool DismountIso(string imagePath)
  45. {
  46. var startInfo = new ProcessStartInfo
  47. {
  48. CreateNoWindow = false,
  49. UseShellExecute = false,
  50. FileName = "PowerShell.exe",
  51. WindowStyle = ProcessWindowStyle.Hidden,
  52. Arguments = $"-NoP -C \"Dismount-DiskImage '{imagePath}'\"",
  53. RedirectStandardOutput = true
  54. };
  55. var proc = Process.Start(startInfo);
  56. if (proc == null) return false;
  57. proc.WaitForExit();
  58. return true;
  59. }
  60. private static string _mountedPath;
  61. private static string _isoPath;
  62. private static string _isoWinVer;
  63. private static int _isoBuild;
  64. /// <summary>
  65. /// Asks user to select Windows installation media, mounts it if applicable, and checks its version
  66. /// </summary>
  67. /// <param name="winVersionsMustMatch">If true when ISO and host versions mismatch, prompts user that things can break if they continue</param>
  68. /// <param name="isoBuildMustBeReturned">If true and the ISO build can't be retrieved, prompts a user with an error</param>
  69. public static (
  70. string MountedPath, string IsoPath, string Winver, int? Build, bool? VersionsMatch
  71. ) GetMediaPath(bool winVersionsMustMatch = false, bool isoBuildMustBeReturned = false)
  72. {
  73. var error = ((string)null, "none", (string)null, (int?)null, (bool?)null);
  74. var choice =
  75. new ChoicePrompt { Text = "To continue, Windows installation media is needed.\r\nDo you have a Windows USB instead of an ISO file? (Y/N): " }.Start();
  76. if (!choice.HasValue) return error;
  77. // Folder/drive chosen
  78. var usingFolder = choice == 0;
  79. if (usingFolder)
  80. {
  81. var dlg = new FolderPicker
  82. {
  83. InputPath = Globals.UserFolder
  84. };
  85. if (dlg.ShowDialog(IntPtr.Zero).GetValueOrDefault())
  86. {
  87. if (CheckFileViolation(dlg.ResultPath))
  88. return error;
  89. _mountedPath = dlg.ResultPath;
  90. }
  91. else
  92. {
  93. Console.WriteLine();
  94. ConsoleTUI.OpenFrame.Close("\r\nYou must select a folder or drive containing Windows installation media.",
  95. new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  96. return error;
  97. }
  98. }
  99. else
  100. {
  101. // Mounting the ISO
  102. var dialog = new OpenFileDialog();
  103. dialog.Filter = "ISO Files (*.ISO)| *.ISO";
  104. dialog.Multiselect = false;
  105. dialog.InitialDirectory = Globals.UserFolder;
  106. var window = new NativeWindow();
  107. window.AssignHandle(Process.GetCurrentProcess().MainWindowHandle);
  108. if (dialog.ShowDialog(window) == DialogResult.OK)
  109. {
  110. _isoPath = dialog.FileName;
  111. if (CheckFileViolation(_isoPath)) return error;
  112. Console.WriteLine();
  113. ConsoleTUI.OpenFrame.WriteCentered("\r\nMounting ISO");
  114. }
  115. else
  116. {
  117. Console.WriteLine();
  118. ConsoleTUI.OpenFrame.Close("\r\nYou must select an ISO.",
  119. new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  120. return error;
  121. }
  122. using (new ConsoleUtils.LoadingIndicator(true))
  123. {
  124. var startInfo = new ProcessStartInfo
  125. {
  126. CreateNoWindow = false,
  127. UseShellExecute = false,
  128. FileName = "PowerShell.exe",
  129. WindowStyle = ProcessWindowStyle.Hidden,
  130. Arguments = $"-NoP -C \"(Mount-DiskImage '{_isoPath}' -PassThru | Get-Volume).DriveLetter + ':\'\"",
  131. RedirectStandardOutput = true
  132. };
  133. var proc = Process.Start(startInfo);
  134. if (proc == null) return error;
  135. proc.WaitForExit();
  136. _mountedPath = proc.StandardOutput.ReadLine();
  137. }
  138. }
  139. // Check WIM version
  140. var wimOrEsdPath = new[] { $@"{_mountedPath}\sources\install.esd", $@"{_mountedPath}\sources\install.wim" }.FirstOrDefault(File.Exists);
  141. if (!string.IsNullOrEmpty(wimOrEsdPath))
  142. {
  143. try
  144. {
  145. DismApi.Initialize(DismLogLevel.LogErrors);
  146. string previousIndexVersion = null;
  147. string isoFullVersion = null;
  148. var multiVersion = false;
  149. var imageInfos = DismApi.GetImageInfo(wimOrEsdPath);
  150. foreach (var imageInfo in imageInfos)
  151. {
  152. isoFullVersion = imageInfo.ProductVersion.ToString();
  153. if (isoFullVersion != previousIndexVersion && previousIndexVersion != null)
  154. {
  155. // If it's multi-version, WinVer will be "Unknown" as well
  156. multiVersion = true;
  157. isoFullVersion = "0.0.0.0";
  158. break;
  159. }
  160. previousIndexVersion = isoFullVersion;
  161. }
  162. switch (multiVersion)
  163. {
  164. case true when isoBuildMustBeReturned:
  165. ConsoleTUI.OpenFrame.Close(
  166. "Multiple Windows versions were found in the Windows image, can't determine which Windows build it is. Please use an unmodified Windows ISO.",
  167. ConsoleColor.Red, Console.BackgroundColor,
  168. new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  169. return error;
  170. case true when winVersionsMustMatch:
  171. ConsoleTUI.OpenFrame.Close(
  172. "Multiple Windows versions were found in the Windows image, can't determine which Windows build it is. If your Windows version doesn't match the ISO, there will be problems.",
  173. ConsoleColor.Red, Console.BackgroundColor,
  174. new ChoicePrompt { AnyKey = true, Text = "Press any key to continue anyways: " });
  175. Program.Frame.Clear();
  176. ConsoleTUI.OpenFrame.WriteCentered("\r\nContinuing without version check...\r\n");
  177. break;
  178. }
  179. var buildSplit = isoFullVersion.Split('.');
  180. _isoBuild = int.Parse(buildSplit[2]);
  181. _isoWinVer = GetWindowsVersion(float.Parse($"{buildSplit[0]}.{buildSplit[1]}"), _isoBuild);
  182. }
  183. catch (Exception e)
  184. {
  185. Console.WriteLine();
  186. ConsoleTUI.OpenFrame.Close(
  187. "Error checking ISO version: " + e.Message.TrimEnd('\n').TrimEnd('\r'),
  188. ConsoleColor.Red, Console.BackgroundColor,
  189. new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  190. return error;
  191. }
  192. finally
  193. {
  194. try
  195. {
  196. DismApi.Shutdown();
  197. }
  198. catch
  199. {
  200. // do nothing
  201. }
  202. }
  203. // Check the current OS version
  204. var hostVersion = Environment.OSVersion.Version;
  205. var hostWinver = GetWindowsVersion(float.Parse($"{hostVersion.Major}.{hostVersion.Minor}"), hostVersion.Build);
  206. // If it all matches & winVersionsMustMatch
  207. if (hostWinver == _isoWinVer) return (_mountedPath, _isoPath, _isoWinVer, _isoBuild, true);
  208. // If ISO version doesn't match host version & winVersionsMustMatch
  209. if (hostWinver != _isoWinVer && winVersionsMustMatch)
  210. {
  211. if (!string.IsNullOrEmpty(_isoPath)) DismountIso(_isoPath);
  212. ConsoleTUI.OpenFrame.Close(
  213. $"You're on {hostWinver}, but the selected image is {_isoWinVer}. You can only use an ISO that matches your Windows version.",
  214. ConsoleColor.Red, Console.BackgroundColor,
  215. new ChoicePrompt { AnyKey = true, Text = "Press any key to return to the Menu: " });
  216. return error;
  217. }
  218. // If ISO version doesn't match host version, and winVersionsMustMatch is true
  219. if (hostWinver != _isoWinVer) return (_mountedPath, _isoPath, _isoWinVer, _isoBuild, false);
  220. }
  221. var noWimText = isoBuildMustBeReturned
  222. ? "Press any key to return to the Menu"
  223. : "Press any key to continue anyways";
  224. ConsoleTUI.OpenFrame.Close(
  225. "No Windows installation image was found inside the selected Windows media. No version check can be done, things might break.",
  226. ConsoleColor.Red, Console.BackgroundColor,
  227. new ChoicePrompt { AnyKey = true, Text = $"{noWimText}: " });
  228. Program.Frame.Clear();
  229. ConsoleTUI.OpenFrame.WriteCentered("\r\nContinuing without version check\r\n");
  230. return isoBuildMustBeReturned ? error : (_mountedPath, _isoPath, null, null, null);
  231. }
  232. }
  233. }