From 5e494ad463fb9f57c246758651662403f473481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:11:25 +0000 Subject: [PATCH 01/22] WindowsInstallerLib: implement new functions Implemented some new functions: GetDisks, GetDiskletter and GetImageInfoD. --- WindowsInstallerLib/src/DeployManager.cs | 55 ++++++++++++++++++++++++ WindowsInstallerLib/src/DiskManager.cs | 53 +++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/WindowsInstallerLib/src/DeployManager.cs b/WindowsInstallerLib/src/DeployManager.cs index 684f09e..9f6a892 100644 --- a/WindowsInstallerLib/src/DeployManager.cs +++ b/WindowsInstallerLib/src/DeployManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Runtime.Versioning; using Microsoft.Dism; @@ -169,6 +170,60 @@ internal static void GetImageInfo(ref Parameters parameters) } } + /// + /// Gets all Windows editions available using DISM, if any, and returns them as a collection. + /// + /// + /// + /// + /// + /// + internal static DismImageInfoCollection GetImageInfoD(ref Parameters parameters) + { + List> TransitionalTuple = []; + + ArgumentException.ThrowIfNullOrEmpty(parameters.ImageFilePath, nameof(parameters.ImageFilePath)); + + if (!PrivilegesManager.IsAdmin()) + { + throw new UnauthorizedAccessException("You do not have enough privileges to initialize the DISM API."); + } + + try + { + DismApi.Initialize(DismLogLevel.LogErrorsWarnings); + + DismImageInfoCollection images = DismApi.GetImageInfo(parameters.ImageFilePath); + + switch (images.Count) + { + case > 1: + Console.WriteLine($"\nFound {images.Count} images in {parameters.ImageFilePath}, shown below.\n", ConsoleColor.Yellow); + break; + case 1: + Console.WriteLine($"\nFound {images.Count} image in {parameters.ImageFilePath}, shown below.\n", ConsoleColor.Yellow); + break; + case 0: + Console.WriteLine($"\nNo images were found in {parameters.ImageFilePath}\n", ConsoleColor.Red); + throw new InvalidDataException($"images.Count is {images.Count}. This is considered to be invalid, the program cannot continue."); + } + + return images; + } + catch (DismException) + { + throw; + } + catch (Exception) + { + throw; + } + finally + { + DismApi.Shutdown(); + } + } + /// /// Gets all Windows editions available using DISM, if any. /// diff --git a/WindowsInstallerLib/src/DiskManager.cs b/WindowsInstallerLib/src/DiskManager.cs index 1ee4ac2..7b326fb 100644 --- a/WindowsInstallerLib/src/DiskManager.cs +++ b/WindowsInstallerLib/src/DiskManager.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Management; +using System.Runtime.CompilerServices; using System.Runtime.Versioning; namespace WindowsInstallerLib @@ -40,6 +42,57 @@ internal static int FormatDisk(ref Parameters parameters) } } + internal static List> GetDisks() + { + List> DisksList = []; + try + { + WqlObjectQuery DeviceTable = new("SELECT * FROM Win32_DiskDrive"); + ManagementObjectSearcher DeviceInfo = new(DeviceTable); + foreach (ManagementObject o in DeviceInfo.Get().Cast()) + { + int Index = Convert.ToInt32(o["Index"]); + string Model = o["Model"].ToString() ?? string.Empty; + string Letter = GetDiskLetter(o["DeviceID"]); + + DisksList.Add(new(Index, Model, Letter)); + } + return DisksList; + } + catch (Exception) + { + throw; + } + } + + private static string GetDiskLetter(object DeviceID) + { + string DriveLetter = string.Empty; + + ArgumentNullException.ThrowIfNull(DeviceID); + + try + { + WqlObjectQuery PartitionQuery = new($"ASSOCIATORS OF {{Win32_DiskDrive.DeviceID='{DeviceID}'}} WHERE AssocClass=Win32_DiskDriveToDiskPartition"); + ManagementObjectSearcher PartitionSearcher = new(PartitionQuery); + foreach (ManagementObject partition in PartitionSearcher.Get().Cast()) + { + WqlObjectQuery LogicalDiskQuery = new($"ASSOCIATORS OF {{Win32_DiskPartition.DeviceID='{partition["DeviceID"]}'}} WHERE AssocClass=Win32_LogicalDiskToPartition"); + ManagementObjectSearcher LogicalDiskSearcher = new(LogicalDiskQuery); + foreach (ManagementObject logicalDisk in LogicalDiskSearcher.Get().Cast()) + { + DriveLetter = logicalDisk["DeviceID"].ToString() ?? throw new ArgumentNullException("DeviceID cannot be null", DeviceID.ToString()); + } + } + } + catch (Exception) + { + throw; + } + return DriveLetter; + } + + /// /// Lists all the disks on the system. /// From 34d9bc1564c48a686b2dd151c77a6ee21065bd47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:12:20 +0000 Subject: [PATCH 02/22] WindowsInstallerLib: implement new namespace Helpers This namespace is used to expose some required functions to the GUIApp. --- WindowsInstallerLib/src/Helpers.cs | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 WindowsInstallerLib/src/Helpers.cs diff --git a/WindowsInstallerLib/src/Helpers.cs b/WindowsInstallerLib/src/Helpers.cs new file mode 100644 index 0000000..8e3366e --- /dev/null +++ b/WindowsInstallerLib/src/Helpers.cs @@ -0,0 +1,34 @@ +using Microsoft.Dism; +using System; +using System.Collections.Generic; +using System.IO; + +namespace WindowsInstallerLib +{ + namespace Helpers + { + public class Privileges + { + public static bool IsAdmin() { return PrivilegesManager.IsAdmin(); } + } + + public class Devices + { + public static List> GetDisks() { return DiskManager.GetDisks(); } + } + + public class SystemInfo + { + public static bool SystemSupportsEFI() { return SystemInfoManager.IsEFI(); } + } + + public class Deployment + { + public static int ExitCode { get; private set; } + public static DismImageInfoCollection GetImageInfo(ref Parameters parameters) { return DeployManager.GetImageInfoD(ref parameters); } + public static void FormatDisk(ref Parameters parameters) { DiskManager.FormatDisk(ref parameters); } + public static void ApplyImage(ref Parameters parameters) { DeployManager.ApplyImage(ref parameters); } + public static void InstallBootloader(ref Parameters parameters) { DeployManager.InstallBootloader(ref parameters); } + } + } +} From 7ed4ff6c6f2a8fbf9b7443cc3b451601982a545c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:13:06 +0000 Subject: [PATCH 03/22] WindowsInstallerLib: bump version to 1.2.0.0 --- WindowsInstallerLib/WindowsInstallerLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WindowsInstallerLib/WindowsInstallerLib.csproj b/WindowsInstallerLib/WindowsInstallerLib.csproj index 0bfcb28..abe1afd 100644 --- a/WindowsInstallerLib/WindowsInstallerLib.csproj +++ b/WindowsInstallerLib/WindowsInstallerLib.csproj @@ -8,7 +8,7 @@ none $(AssemblyName) latest - 1.1.2.2 + 1.2.0.0 x64 true From 2b54652d144bec2d3dbd6ad68d92dce59b531b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Thu, 20 Feb 2025 16:15:01 +0000 Subject: [PATCH 04/22] GUIApp: reimplementation of the GUI program Taken from previous commits, I have updated and refactored this old project to make it work with the new library. --- GUIApp/GUIApp.csproj | 52 ++ GUIApp/src/AboutWindow.Designer.cs | 187 +++++++ GUIApp/src/AboutWindow.cs | 93 ++++ GUIApp/src/AboutWindow.resx | 120 +++++ GUIApp/src/MainWindow.Designer.cs | 306 +++++++++++ GUIApp/src/MainWindow.cs | 234 +++++++++ GUIApp/src/MainWindow.resx | 813 +++++++++++++++++++++++++++++ GUIApp/src/Program.cs | 20 + GUIApp/src/Utilities.cs | 143 +++++ GUIApp/src/ValidateDiskLetter.cs | 64 +++ Windows Installer.sln | 6 + 11 files changed, 2038 insertions(+) create mode 100644 GUIApp/GUIApp.csproj create mode 100644 GUIApp/src/AboutWindow.Designer.cs create mode 100644 GUIApp/src/AboutWindow.cs create mode 100644 GUIApp/src/AboutWindow.resx create mode 100644 GUIApp/src/MainWindow.Designer.cs create mode 100644 GUIApp/src/MainWindow.cs create mode 100644 GUIApp/src/MainWindow.resx create mode 100644 GUIApp/src/Program.cs create mode 100644 GUIApp/src/Utilities.cs create mode 100644 GUIApp/src/ValidateDiskLetter.cs diff --git a/GUIApp/GUIApp.csproj b/GUIApp/GUIApp.csproj new file mode 100644 index 0000000..686e871 --- /dev/null +++ b/GUIApp/GUIApp.csproj @@ -0,0 +1,52 @@ + + + + WinExe + net9.0-windows + enable + true + wit.Program + latest + x64 + none + true + en-US + latest-recommended + wit + wit + x64 + + + + 0 + True + none + True + 0.0.3.0 + $(Version) + $(Version) + + + + 9999 + True + full + testing + True + 0.0.3.0 + testing + $(Version) + $(Version) + + + + + + + + + + + + + diff --git a/GUIApp/src/AboutWindow.Designer.cs b/GUIApp/src/AboutWindow.Designer.cs new file mode 100644 index 0000000..863d6b2 --- /dev/null +++ b/GUIApp/src/AboutWindow.Designer.cs @@ -0,0 +1,187 @@ +namespace wit +{ + partial class AboutWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + logoPictureBox = new System.Windows.Forms.PictureBox(); + labelProductName = new System.Windows.Forms.Label(); + labelVersion = new System.Windows.Forms.Label(); + labelCopyright = new System.Windows.Forms.Label(); + labelCompanyName = new System.Windows.Forms.Label(); + textBoxDescription = new System.Windows.Forms.TextBox(); + okButton = new System.Windows.Forms.Button(); + tableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)logoPictureBox).BeginInit(); + SuspendLayout(); + // + // tableLayoutPanel + // + tableLayoutPanel.ColumnCount = 2; + tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F)); + tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F)); + tableLayoutPanel.Controls.Add(logoPictureBox, 0, 0); + tableLayoutPanel.Controls.Add(labelProductName, 1, 0); + tableLayoutPanel.Controls.Add(labelVersion, 1, 1); + tableLayoutPanel.Controls.Add(labelCopyright, 1, 2); + tableLayoutPanel.Controls.Add(labelCompanyName, 1, 3); + tableLayoutPanel.Controls.Add(textBoxDescription, 1, 4); + tableLayoutPanel.Controls.Add(okButton, 1, 5); + tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel.Location = new System.Drawing.Point(10, 10); + tableLayoutPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + tableLayoutPanel.Name = "tableLayoutPanel"; + tableLayoutPanel.RowCount = 6; + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F)); + tableLayoutPanel.Size = new System.Drawing.Size(487, 307); + tableLayoutPanel.TabIndex = 0; + // + // logoPictureBox + // + logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill; + logoPictureBox.Location = new System.Drawing.Point(4, 3); + logoPictureBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + logoPictureBox.Name = "logoPictureBox"; + tableLayoutPanel.SetRowSpan(logoPictureBox, 6); + logoPictureBox.Size = new System.Drawing.Size(152, 301); + logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + logoPictureBox.TabIndex = 12; + logoPictureBox.TabStop = false; + // + // labelProductName + // + labelProductName.Dock = System.Windows.Forms.DockStyle.Fill; + labelProductName.Location = new System.Drawing.Point(167, 0); + labelProductName.Margin = new System.Windows.Forms.Padding(7, 0, 4, 0); + labelProductName.MaximumSize = new System.Drawing.Size(0, 20); + labelProductName.Name = "labelProductName"; + labelProductName.Size = new System.Drawing.Size(316, 20); + labelProductName.TabIndex = 19; + labelProductName.Text = "Product Name"; + labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelVersion + // + labelVersion.Dock = System.Windows.Forms.DockStyle.Fill; + labelVersion.Location = new System.Drawing.Point(167, 30); + labelVersion.Margin = new System.Windows.Forms.Padding(7, 0, 4, 0); + labelVersion.MaximumSize = new System.Drawing.Size(0, 20); + labelVersion.Name = "labelVersion"; + labelVersion.Size = new System.Drawing.Size(316, 20); + labelVersion.TabIndex = 0; + labelVersion.Text = "Version"; + labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelCopyright + // + labelCopyright.Dock = System.Windows.Forms.DockStyle.Fill; + labelCopyright.Location = new System.Drawing.Point(167, 60); + labelCopyright.Margin = new System.Windows.Forms.Padding(7, 0, 4, 0); + labelCopyright.MaximumSize = new System.Drawing.Size(0, 20); + labelCopyright.Name = "labelCopyright"; + labelCopyright.Size = new System.Drawing.Size(316, 20); + labelCopyright.TabIndex = 21; + labelCopyright.Text = "Copyright"; + labelCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // labelCompanyName + // + labelCompanyName.Dock = System.Windows.Forms.DockStyle.Fill; + labelCompanyName.Location = new System.Drawing.Point(167, 90); + labelCompanyName.Margin = new System.Windows.Forms.Padding(7, 0, 4, 0); + labelCompanyName.MaximumSize = new System.Drawing.Size(0, 20); + labelCompanyName.Name = "labelCompanyName"; + labelCompanyName.Size = new System.Drawing.Size(316, 20); + labelCompanyName.TabIndex = 22; + labelCompanyName.Text = "Company Name"; + labelCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // textBoxDescription + // + textBoxDescription.Dock = System.Windows.Forms.DockStyle.Fill; + textBoxDescription.Location = new System.Drawing.Point(167, 123); + textBoxDescription.Margin = new System.Windows.Forms.Padding(7, 3, 4, 3); + textBoxDescription.Multiline = true; + textBoxDescription.Name = "textBoxDescription"; + textBoxDescription.ReadOnly = true; + textBoxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both; + textBoxDescription.Size = new System.Drawing.Size(316, 147); + textBoxDescription.TabIndex = 23; + textBoxDescription.TabStop = false; + textBoxDescription.Text = "Description"; + // + // okButton + // + okButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; + okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + okButton.Location = new System.Drawing.Point(395, 277); + okButton.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + okButton.Name = "okButton"; + okButton.Size = new System.Drawing.Size(88, 27); + okButton.TabIndex = 24; + okButton.Text = "&OK"; + // + // AboutWindow + // + AcceptButton = okButton; + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(507, 327); + Controls.Add(tableLayoutPanel); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + MaximizeBox = false; + MinimizeBox = false; + Name = "AboutWindow"; + Padding = new System.Windows.Forms.Padding(10); + ShowIcon = false; + ShowInTaskbar = false; + StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + Text = "AboutWindow"; + tableLayoutPanel.ResumeLayout(false); + tableLayoutPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)logoPictureBox).EndInit(); + ResumeLayout(false); + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.PictureBox logoPictureBox; + private System.Windows.Forms.Label labelProductName; + private System.Windows.Forms.Label labelVersion; + private System.Windows.Forms.Label labelCopyright; + private System.Windows.Forms.Label labelCompanyName; + private System.Windows.Forms.TextBox textBoxDescription; + private System.Windows.Forms.Button okButton; + } +} diff --git a/GUIApp/src/AboutWindow.cs b/GUIApp/src/AboutWindow.cs new file mode 100644 index 0000000..b4f9868 --- /dev/null +++ b/GUIApp/src/AboutWindow.cs @@ -0,0 +1,93 @@ +using System.Reflection; +using System.Windows.Forms; + +namespace wit +{ + sealed partial class AboutWindow : Form + { + public AboutWindow() + { + InitializeComponent(); + Text = "About: " + AssemblyTitle; + labelProductName.Text = "Program name: " + AssemblyProduct; + labelVersion.Text = $"Version: {AssemblyVersion}"; + labelCopyright.Text = "Copyright: " + AssemblyCopyright; + labelCompanyName.Text = "Company: " + AssemblyCompany; + textBoxDescription.Text = "Description: " + AssemblyDescription; + } + + #region Assembly Attribute Accessors + + public static string AssemblyTitle + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + if (titleAttribute.Title != "") + { + return titleAttribute.Title; + } + } + return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location); + } + } + + public static string? AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version?.ToString(); + + public static string AssemblyDescription + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyDescriptionAttribute)attributes[0]).Description; + } + } + + public static string AssemblyProduct + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyProductAttribute)attributes[0]).Product; + } + } + + public static string AssemblyCopyright + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyCopyrightAttribute)attributes[0]).Copyright; + } + } + + public static string AssemblyCompany + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attributes.Length == 0) + { + return ""; + } + return ((AssemblyCompanyAttribute)attributes[0]).Company; + } + } + #endregion + } +} diff --git a/GUIApp/src/AboutWindow.resx b/GUIApp/src/AboutWindow.resx new file mode 100644 index 0000000..6392ed1 --- /dev/null +++ b/GUIApp/src/AboutWindow.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUIApp/src/MainWindow.Designer.cs b/GUIApp/src/MainWindow.Designer.cs new file mode 100644 index 0000000..82f2ab2 --- /dev/null +++ b/GUIApp/src/MainWindow.Designer.cs @@ -0,0 +1,306 @@ + +using System.Diagnostics.CodeAnalysis; +using System.Windows.Forms; + +namespace wit +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + [RequiresUnreferencedCode("Calls System.ComponentModel.ComponentResourceManager.ApplyResources(Object, String)")] + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow)); + InstallButton = new Button(); + tableLayoutPanel1 = new TableLayoutPanel(); + EfiDriveLabel = new Label(); + DiskNumber = new NumericUpDown(); + DestinationDriveLabel = new Label(); + DestinationDrive = new ComboBox(); + EfiDrive = new ComboBox(); + RescanDisks = new Button(); + WindowsEditionIndex = new NumericUpDown(); + ImageFileLabel = new Label(); + WindowsEditionIndexLabel = new Label(); + ImageFilePath = new Label(); + ChooseISOImage = new Button(); + SourceDrive = new ComboBox(); + DiskNumberLabel = new Label(); + SourceDrive_Label = new Label(); + SourceDrive_Scan = new Button(); + ImageList = new DataGridView(); + DiskList = new DataGridView(); + MenuBar = new MenuStrip(); + fileToolStripMenuItem = new ToolStripMenuItem(); + exitToolStripMenuItem = new ToolStripMenuItem(); + aboutToolStripMenuItem = new ToolStripMenuItem(); + aboutTheProgramToolStripMenuItem = new ToolStripMenuItem(); + saveFileDialog1 = new SaveFileDialog(); + tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)DiskNumber).BeginInit(); + ((System.ComponentModel.ISupportInitialize)WindowsEditionIndex).BeginInit(); + ((System.ComponentModel.ISupportInitialize)ImageList).BeginInit(); + ((System.ComponentModel.ISupportInitialize)DiskList).BeginInit(); + MenuBar.SuspendLayout(); + SuspendLayout(); + // + // InstallButton + // + resources.ApplyResources(InstallButton, "InstallButton"); + InstallButton.Name = "InstallButton"; + InstallButton.UseVisualStyleBackColor = true; + InstallButton.Click += InstallButton_Click; + // + // tableLayoutPanel1 + // + resources.ApplyResources(tableLayoutPanel1, "tableLayoutPanel1"); + tableLayoutPanel1.Controls.Add(EfiDriveLabel, 0, 1); + tableLayoutPanel1.Controls.Add(DiskNumber, 1, 2); + tableLayoutPanel1.Controls.Add(DestinationDriveLabel, 0, 0); + tableLayoutPanel1.Controls.Add(DestinationDrive, 1, 0); + tableLayoutPanel1.Controls.Add(EfiDrive, 1, 1); + tableLayoutPanel1.Controls.Add(RescanDisks, 2, 2); + tableLayoutPanel1.Controls.Add(WindowsEditionIndex, 1, 5); + tableLayoutPanel1.Controls.Add(ImageFileLabel, 0, 4); + tableLayoutPanel1.Controls.Add(WindowsEditionIndexLabel, 0, 5); + tableLayoutPanel1.Controls.Add(ImageFilePath, 1, 4); + tableLayoutPanel1.Controls.Add(ChooseISOImage, 2, 4); + tableLayoutPanel1.Controls.Add(SourceDrive, 1, 3); + tableLayoutPanel1.Controls.Add(DiskNumberLabel, 0, 2); + tableLayoutPanel1.Controls.Add(SourceDrive_Label, 0, 3); + tableLayoutPanel1.Controls.Add(SourceDrive_Scan, 2, 3); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + // + // EfiDriveLabel + // + resources.ApplyResources(EfiDriveLabel, "EfiDriveLabel"); + EfiDriveLabel.FlatStyle = FlatStyle.System; + EfiDriveLabel.Name = "EfiDriveLabel"; + // + // DiskNumber + // + DiskNumber.BorderStyle = BorderStyle.FixedSingle; + resources.ApplyResources(DiskNumber, "DiskNumber"); + DiskNumber.Name = "DiskNumber"; + // + // DestinationDriveLabel + // + resources.ApplyResources(DestinationDriveLabel, "DestinationDriveLabel"); + DestinationDriveLabel.FlatStyle = FlatStyle.System; + DestinationDriveLabel.Name = "DestinationDriveLabel"; + // + // DestinationDrive + // + resources.ApplyResources(DestinationDrive, "DestinationDrive"); + DestinationDrive.DropDownStyle = ComboBoxStyle.DropDownList; + DestinationDrive.FormattingEnabled = true; + DestinationDrive.Name = "DestinationDrive"; + // + // EfiDrive + // + resources.ApplyResources(EfiDrive, "EfiDrive"); + EfiDrive.DropDownStyle = ComboBoxStyle.DropDownList; + EfiDrive.FormattingEnabled = true; + EfiDrive.Name = "EfiDrive"; + // + // RescanDisks + // + resources.ApplyResources(RescanDisks, "RescanDisks"); + RescanDisks.Name = "RescanDisks"; + RescanDisks.UseVisualStyleBackColor = false; + RescanDisks.Click += RescanDisks_Click; + // + // WindowsEditionIndex + // + resources.ApplyResources(WindowsEditionIndex, "WindowsEditionIndex"); + WindowsEditionIndex.BorderStyle = BorderStyle.FixedSingle; + WindowsEditionIndex.Name = "WindowsEditionIndex"; + // + // ImageFileLabel + // + resources.ApplyResources(ImageFileLabel, "ImageFileLabel"); + ImageFileLabel.FlatStyle = FlatStyle.System; + ImageFileLabel.Name = "ImageFileLabel"; + // + // WindowsEditionIndexLabel + // + resources.ApplyResources(WindowsEditionIndexLabel, "WindowsEditionIndexLabel"); + WindowsEditionIndexLabel.FlatStyle = FlatStyle.System; + WindowsEditionIndexLabel.Name = "WindowsEditionIndexLabel"; + // + // ImageFilePath + // + resources.ApplyResources(ImageFilePath, "ImageFilePath"); + ImageFilePath.AutoEllipsis = true; + ImageFilePath.FlatStyle = FlatStyle.System; + ImageFilePath.Name = "ImageFilePath"; + // + // ChooseISOImage + // + resources.ApplyResources(ChooseISOImage, "ChooseISOImage"); + ChooseISOImage.Name = "ChooseISOImage"; + ChooseISOImage.UseVisualStyleBackColor = false; + ChooseISOImage.Click += ChooseISOImage_Click; + // + // SourceDrive + // + resources.ApplyResources(SourceDrive, "SourceDrive"); + SourceDrive.DropDownStyle = ComboBoxStyle.DropDownList; + SourceDrive.FormattingEnabled = true; + SourceDrive.Name = "SourceDrive"; + // + // DiskNumberLabel + // + resources.ApplyResources(DiskNumberLabel, "DiskNumberLabel"); + DiskNumberLabel.FlatStyle = FlatStyle.System; + DiskNumberLabel.Name = "DiskNumberLabel"; + // + // SourceDrive_Label + // + resources.ApplyResources(SourceDrive_Label, "SourceDrive_Label"); + SourceDrive_Label.FlatStyle = FlatStyle.System; + SourceDrive_Label.Name = "SourceDrive_Label"; + // + // SourceDrive_Scan + // + resources.ApplyResources(SourceDrive_Scan, "SourceDrive_Scan"); + SourceDrive_Scan.Name = "SourceDrive_Scan"; + SourceDrive_Scan.UseVisualStyleBackColor = false; + SourceDrive_Scan.Click += SourceDrive_Scan_Click; + // + // ImageList + // + ImageList.AllowUserToAddRows = false; + ImageList.AllowUserToDeleteRows = false; + ImageList.AllowUserToResizeColumns = false; + ImageList.AllowUserToResizeRows = false; + ImageList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; + ImageList.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells; + ImageList.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; + resources.ApplyResources(ImageList, "ImageList"); + ImageList.Name = "ImageList"; + ImageList.ReadOnly = true; + // + // DiskList + // + DiskList.AllowUserToAddRows = false; + DiskList.AllowUserToDeleteRows = false; + DiskList.AllowUserToResizeColumns = false; + DiskList.AllowUserToResizeRows = false; + DiskList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; + DiskList.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells; + DiskList.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; + resources.ApplyResources(DiskList, "DiskList"); + DiskList.Name = "DiskList"; + DiskList.ReadOnly = true; + DiskList.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing; + // + // MenuBar + // + MenuBar.BackColor = System.Drawing.Color.Transparent; + MenuBar.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, aboutToolStripMenuItem }); + resources.ApplyResources(MenuBar, "MenuBar"); + MenuBar.Name = "MenuBar"; + MenuBar.RenderMode = ToolStripRenderMode.System; + // + // fileToolStripMenuItem + // + fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { exitToolStripMenuItem }); + fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + resources.ApplyResources(fileToolStripMenuItem, "fileToolStripMenuItem"); + // + // exitToolStripMenuItem + // + exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + resources.ApplyResources(exitToolStripMenuItem, "exitToolStripMenuItem"); + exitToolStripMenuItem.Click += CloseApplication; + // + // aboutToolStripMenuItem + // + aboutToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { aboutTheProgramToolStripMenuItem }); + aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + resources.ApplyResources(aboutToolStripMenuItem, "aboutToolStripMenuItem"); + // + // aboutTheProgramToolStripMenuItem + // + aboutTheProgramToolStripMenuItem.Name = "aboutTheProgramToolStripMenuItem"; + resources.ApplyResources(aboutTheProgramToolStripMenuItem, "aboutTheProgramToolStripMenuItem"); + aboutTheProgramToolStripMenuItem.Click += AboutWindow_Click; + // + // MainWindow + // + resources.ApplyResources(this, "$this"); + AutoScaleMode = AutoScaleMode.Dpi; + Controls.Add(InstallButton); + Controls.Add(ImageList); + Controls.Add(tableLayoutPanel1); + Controls.Add(DiskList); + Controls.Add(MenuBar); + DoubleBuffered = true; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + Name = "MainWindow"; + Load += MainWindow_Load; + tableLayoutPanel1.ResumeLayout(false); + tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)DiskNumber).EndInit(); + ((System.ComponentModel.ISupportInitialize)WindowsEditionIndex).EndInit(); + ((System.ComponentModel.ISupportInitialize)ImageList).EndInit(); + ((System.ComponentModel.ISupportInitialize)DiskList).EndInit(); + MenuBar.ResumeLayout(false); + MenuBar.PerformLayout(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + private DataGridView DiskList; + private MenuStrip MenuBar; + private ToolStripMenuItem fileToolStripMenuItem; + private ToolStripMenuItem exitToolStripMenuItem; + private ToolStripMenuItem aboutToolStripMenuItem; + private Label DestinationDriveLabel; + private Label EfiDriveLabel; + private Button InstallButton; + private DataGridView ImageList; + private TableLayoutPanel tableLayoutPanel1; + private NumericUpDown WindowsEditionIndex; + private Label WindowsEditionIndexLabel; + private Button ChooseISOImage; + private Label ImageFilePath; + private Label DiskNumberLabel; + private NumericUpDown DiskNumber; + private Label ImageFileLabel; + private ToolStripMenuItem aboutTheProgramToolStripMenuItem; + private ComboBox DestinationDrive; + private ComboBox EfiDrive; + private Button RescanDisks; + private ComboBox SourceDrive; + private SaveFileDialog saveFileDialog1; + private Label SourceDrive_Label; + private Button SourceDrive_Scan; + } +} diff --git a/GUIApp/src/MainWindow.cs b/GUIApp/src/MainWindow.cs new file mode 100644 index 0000000..cfd3f2d --- /dev/null +++ b/GUIApp/src/MainWindow.cs @@ -0,0 +1,234 @@ +using System; +using System.IO; +using System.Runtime.Versioning; +using System.Windows.Forms; +using WindowsInstallerLib; +using WindowsInstallerLib.Helpers; + +namespace wit +{ + [SupportedOSPlatform("windows")] + public partial class MainWindow : Form + { + Parameters parameters = new() + { + DiskNumber = -1, + ImageIndex = -1 + }; + + public MainWindow() + { + + if (!Privileges.IsAdmin()) + { + MessageBox.Show("You must have administrator privileges to run this program.", + "Insufficient privileges", MessageBoxButtons.OK, MessageBoxIcon.Error); + throw new UnauthorizedAccessException("You must have administrator privileges to run this program."); + } + + InitializeComponent(); + } + + private void MainWindow_Load(object sender, EventArgs e) + { + try + { + GetDisksData(sender, e); + GetDiskLetters(sender, e); + + WindowsEditionIndex.Enabled = false; + InstallButton.Enabled = false; + + DestinationDrive.SelectedIndexChanged += ValidateDiskLetter; + EfiDrive.SelectedIndexChanged += ValidateDiskLetter; + SourceDrive.SelectedIndexChanged += ValidateDiskLetter; + } + catch (Exception) + { + throw; + } + } + + private void CloseApplication(object sender, EventArgs e) + { + Application.Exit(); + } + + private void ChooseISOImage_Click(object sender, EventArgs e) + { + try + { + SourceDrive_Scan.Enabled = false; + + if (ImageFilePath.Text == "No image file is selected.") + { + OpenFileDialog OpenFileDialog = new() + { + Filter = "ESD file (*.esd)|*.esd|WIM file (*.wim)|*.wim", + CheckFileExists = true, + CheckPathExists = true, + AddExtension = true, + }; + + DialogResult DialogResult = OpenFileDialog.ShowDialog(); + + if (DialogResult == DialogResult.Cancel) + { + return; + } + + if (string.IsNullOrWhiteSpace(OpenFileDialog.FileName)) + { + MessageBox.Show("No image file was selected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + + ImageFilePath.Text = "No image file is selected"; + + return; + } + + if (!OpenFileDialog.FileName.Contains("install")) + { + MessageBox.Show("Invalid image file. It must be 'install.wim' or 'install.esd'.", "Invalid file", MessageBoxButtons.OK, MessageBoxIcon.Error); + + throw new InvalidDataException("Invalid image file. It must be 'install.wim' or 'install.esd'."); + } + + ImageFilePath.Text = OpenFileDialog.FileName; + + parameters.ImageFilePath = ImageFilePath.Text; + } + + if (ImageList.Columns.Count >= 1) + { + ImageList.Columns.Clear(); + } + + GetImageInfo(sender, e); + } + catch (Exception) + { + throw; + } + } + + private void SourceDrive_Scan_Click(object sender, EventArgs e) + { + try + { + if (SourceDrive.Text.Length > 0) + { + foreach (string file in Directory.EnumerateFiles(SourceDrive.Text + @"\sources\")) + { + if (file.Contains("install") && file.EndsWith(".esd", StringComparison.CurrentCulture) || + file.Contains("install") && file.EndsWith(".wim", StringComparison.CurrentCulture)) + { + ImageFilePath.Text = file; + parameters.ImageFilePath = ImageFilePath.Text; + ChooseISOImage.Enabled = false; + break; + } + } + } + else + { + MessageBox.Show("Please select a source drive.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + if (ImageList.Columns.Count >= 1) + { + ImageList.Columns.Clear(); + } + + GetImageInfo(sender, e); + } + catch (Exception) + { + throw; + } + } + + private void InstallButton_Click(object sender, EventArgs e) + { + try + { + parameters.DestinationDrive = DestinationDrive.Text; + parameters.EfiDrive = EfiDrive.Text; + parameters.DiskNumber = Convert.ToInt32(DiskNumber.Value); + parameters.ImageFilePath = ImageFilePath.Text; + parameters.ImageIndex = Convert.ToInt32(WindowsEditionIndex.Value); + + if (string.IsNullOrWhiteSpace(parameters.FirmwareType)) + { + switch (SystemInfo.SystemSupportsEFI()) + { + case true: + parameters.FirmwareType = "UEFI"; + break; + case false: + parameters.FirmwareType = "BIOS"; + break; + } + } + } + catch + { + throw; + } + + try + { + InstallButton.Text = "Please wait..."; + Deployment.FormatDisk(ref parameters); + Deployment.ApplyImage(ref parameters); + Deployment.InstallBootloader(ref parameters); + InstallButton.Text = "Installation complete"; + } + catch + { + throw; + } + } + + private void AboutWindow_Click(object sender, EventArgs e) + { + try + { + new AboutWindow().ShowDialog(this); + } + catch (ArgumentException) + { + throw; + } + catch (InvalidOperationException) + { + throw; + } + catch + { + throw; + } + } + + private void RescanDisks_Click(object sender, EventArgs e) + { + try + { + RescanDisks.Text = "Please wait..."; + RescanDisks.Enabled = false; + DiskList.Columns.Clear(); + DiskList.Rows.Clear(); + GetDisksData(sender, e); + } + catch + { + throw; + } + finally + { + RescanDisks.Text = "Rescan disks"; + RescanDisks.Enabled = true; + } + } + } +} diff --git a/GUIApp/src/MainWindow.resx b/GUIApp/src/MainWindow.resx new file mode 100644 index 0000000..df2c877 --- /dev/null +++ b/GUIApp/src/MainWindow.resx @@ -0,0 +1,813 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + System + + + NoControl + + + + 668, 206 + + + 104, 27 + + + 4 + + + Install Windows + + + InstallButton + + + System.Windows.Forms.Button, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + Single + + + 3 + + + Top, Bottom, Left, Right + + + True + + + 4, 31 + + + 96, 29 + + + 3 + + + Bootloader drive + + + EfiDriveLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 0 + + + 107, 64 + + + 41, 23 + + + 7 + + + DiskNumber + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 1 + + + Top, Bottom, Left, Right + + + True + + + 4, 1 + + + 96, 29 + + + 2 + + + OS drive + + + DestinationDriveLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 2 + + + Left + + + System + + + 107, 4 + + + 41, 23 + + + 14 + + + DestinationDrive + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 3 + + + Left + + + System + + + 107, 34 + + + 41, 23 + + + 15 + + + EfiDrive + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 4 + + + Top, Bottom, Left, Right + + + GrowAndShrink + + + System + + + NoControl + + + 252, 64 + + + 118, 24 + + + 16 + + + Rescan devices + + + RescanDisks + + + System.Windows.Forms.Button, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 5 + + + True + + + 107, 157 + + + 41, 23 + + + 10 + + + WindowsEditionIndex + + + System.Windows.Forms.NumericUpDown, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 6 + + + True + + + Fill + + + NoControl + + + 4, 123 + + + 96, 30 + + + 8 + + + Image file + + + ImageFileLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 7 + + + True + + + NoControl + + + 4, 154 + + + 96, 15 + + + 11 + + + Windows edition + + + WindowsEditionIndexLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 8 + + + Top, Bottom, Left, Right + + + True + + + NoControl + + + 107, 123 + + + 138, 30 + + + 13 + + + No image file is selected. + + + MiddleCenter + + + ImageFilePath + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 9 + + + Top, Bottom, Left, Right + + + GrowAndShrink + + + System + + + NoControl + + + 252, 126 + + + 118, 24 + + + 12 + + + Choose ISO image + + + ChooseISOImage + + + System.Windows.Forms.Button, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 10 + + + Left + + + System + + + 107, 95 + + + 41, 23 + + + 17 + + + SourceDrive + + + System.Windows.Forms.ComboBox, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 11 + + + Top, Bottom, Left, Right + + + True + + + NoControl + + + 4, 61 + + + 96, 30 + + + 6 + + + Storage device + + + DiskNumberLabel + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 12 + + + True + + + Fill + + + NoControl + + + 4, 92 + + + 96, 30 + + + 18 + + + Source drive + + + SourceDrive_Label + + + System.Windows.Forms.Label, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 13 + + + Top, Bottom, Left, Right + + + GrowAndShrink + + + System + + + NoControl + + + 252, 95 + + + 118, 24 + + + 19 + + + Scan source drive + + + SourceDrive_Scan + + + System.Windows.Forms.Button, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableLayoutPanel1 + + + 14 + + + 12, 27 + + + 6 + + + 374, 206 + + + 5 + + + tableLayoutPanel1 + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="EfiDriveLabel" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="DiskNumber" Row="2" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="DestinationDriveLabel" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="DestinationDrive" Row="0" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="EfiDrive" Row="1" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="RescanDisks" Row="2" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="WindowsEditionIndex" Row="5" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="ImageFileLabel" Row="4" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="WindowsEditionIndexLabel" Row="5" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="ImageFilePath" Row="4" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="ChooseISOImage" Row="4" RowSpan="1" Column="2" ColumnSpan="1" /><Control Name="SourceDrive" Row="3" RowSpan="1" Column="1" ColumnSpan="1" /><Control Name="DiskNumberLabel" Row="2" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="SourceDrive_Label" Row="3" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="SourceDrive_Scan" Row="3" RowSpan="1" Column="2" ColumnSpan="1" /></Controls><Columns Styles="AutoSize,0,AutoSize,0,AutoSize,0" /><Rows Styles="AutoSize,0,AutoSize,0,AutoSize,0,Absolute,30,AutoSize,0,Absolute,20" /></TableLayoutSettings> + + + 12, 239 + + + Vertical + + + 760, 152 + + + 4 + + + ImageList + + + System.Windows.Forms.DataGridView, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 12, 397 + + + 760, 152 + + + 0 + + + DiskList + + + System.Windows.Forms.DataGridView, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + 17, 17 + + + 92, 22 + + + Exit + + + 37, 20 + + + File + + + 176, 22 + + + About the program + + + 52, 20 + + + About + + + 0, 0 + + + 784, 24 + + + 1 + + + Menu + + + MenuBar + + + System.Windows.Forms.MenuStrip, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + 119, 17 + + + True + + + 96, 96 + + + True + + + True + + + GrowAndShrink + + + 784, 561 + + + 800, 600 + + + 800, 600 + + + Windows Installer + + + fileToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + exitToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + aboutToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + aboutTheProgramToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + saveFileDialog1 + + + System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + MainWindow + + + System.Windows.Forms.Form, System.Windows.Forms, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUIApp/src/Program.cs b/GUIApp/src/Program.cs new file mode 100644 index 0000000..fec56fc --- /dev/null +++ b/GUIApp/src/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Windows.Forms; + +namespace wit +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new MainWindow()); + } + } +} diff --git a/GUIApp/src/Utilities.cs b/GUIApp/src/Utilities.cs new file mode 100644 index 0000000..ed5c9f1 --- /dev/null +++ b/GUIApp/src/Utilities.cs @@ -0,0 +1,143 @@ +using Microsoft.Dism; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Windows.Forms; +using WindowsInstallerLib.Helpers; + +namespace wit +{ + public partial class MainWindow + { + private void GetDiskLetters(object sender, EventArgs e) + { + List DiskLetters = [ + "A:\\", + "B:\\", + "C:\\", + "D:\\", + "E:\\", + "F:\\", + "G:\\", + "H:\\", + "I:\\", + "J:\\", + "K:\\", + "L:\\", + "M:\\", + "N:\\", + "O:\\", + "P:\\", + "Q:\\", + "R:\\", + "S:\\", + "T:\\", + "U:\\", + "V:\\", + "W:\\", + "X:\\", + "Y:\\", + "Z:\\" + ]; + List DiskLettersToRemove = []; + List> Disks = Devices.GetDisks(); + + try + { + foreach (string DiskLetter in DiskLetters) + { + foreach (Tuple disk in Disks) + { + string Letter = disk.Item3 + @"\"; + + if (DiskLetter.Equals(Letter, StringComparison.Ordinal)) + { + DiskLettersToRemove.Add(Letter); + } + } + } + + foreach (string DiskLetter in DiskLettersToRemove) + { + DiskLetters.Remove(DiskLetter); + } + + DestinationDrive.Items.AddRange(DiskLetters.ToArray()); + EfiDrive.Items.AddRange(DiskLetters.ToArray()); + SourceDrive.Items.AddRange(DiskLetters.ToArray()); + } + catch (Exception) + { + throw; + } + } + + private void GetDisksData(object sender, EventArgs e) + { + DiskList.Columns.Add("DiskNumber", "Disk"); + DiskList.Columns.Add("Model", "Model"); + DiskList.Columns.Add("Label", "Label"); + + List> disks = Devices.GetDisks(); + + foreach (Tuple disk in disks) + { + try + { + DiskList.Rows.Add(disk.Item1, disk.Item2, disk.Item3); + } + catch (ArgumentNullException) + { + throw; + } + catch (InvalidOperationException) + { + throw; + } + catch + { + throw; + } + } + + DiskList.Sort(DiskList.Columns[0], ListSortDirection.Ascending); + DiskList.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader; + DiskNumber.Maximum = DiskList.Rows.Count - 1; + } + + private void GetImageInfo(object sender, EventArgs e) + { + try + { + DismImageInfoCollection imageInfoCollection = Deployment.GetImageInfo(ref parameters); + + WindowsEditionIndex.Minimum = 1; + WindowsEditionIndex.Maximum = imageInfoCollection.Count; + + WindowsEditionIndex.Enabled = true; + + ImageList.Columns.Add("Index", "Index"); + ImageList.Columns.Add("Edition", "Name"); + ImageList.Columns.Add("Version", "Version"); + + foreach (DismImageInfo DismImage in imageInfoCollection) + { + ImageList.Rows.Add(DismImage.ImageIndex, DismImage.ImageName, DismImage.ProductVersion); + } + } + catch (DismException) + { + throw; + } + catch (Exception) + { + throw; + } + finally + { + DismApi.Shutdown(); + } + } + } +} diff --git a/GUIApp/src/ValidateDiskLetter.cs b/GUIApp/src/ValidateDiskLetter.cs new file mode 100644 index 0000000..c476bf7 --- /dev/null +++ b/GUIApp/src/ValidateDiskLetter.cs @@ -0,0 +1,64 @@ +using System.Windows.Forms; +using System; + +namespace wit +{ + public partial class MainWindow + { + private static void ShowMessage(string text) + { + MessageBox.Show(text, "Duplicate letters", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + private void ValidateDiskLetter(object? sender, EventArgs e) + { + if (DestinationDrive.Text.Length > 0) + { + if (DestinationDrive.Text == EfiDrive.Text) + { + ShowMessage("The OS drive letter cannot be the same as the bootloader drive."); + DestinationDrive.Text = null; + EfiDrive.Text = null; + } + else if (DestinationDrive.Text == SourceDrive.Text) + { + ShowMessage("The OS drive letter cannot be the same as the source drive."); + DestinationDrive.Text = null; + SourceDrive.Text = null; + } + } + + if (EfiDrive.Text.Length > 0) + { + if (EfiDrive.Text == DestinationDrive.Text) + { + ShowMessage("The bootloader drive letter cannot be the same as the OS drive."); + EfiDrive.Text = null; + DestinationDrive.Text = null; + } + else if (EfiDrive.Text == SourceDrive.Text) + { + ShowMessage("The bootloader drive letter cannot be the same as the source drive."); + EfiDrive.Text = null; + SourceDrive.Text = null; + } + } + + if (SourceDrive.Text.Length > 0) + { + if (SourceDrive.Text == DestinationDrive.Text) + { + ShowMessage("The source drive letter cannot be the same as the OS drive."); + SourceDrive.Text = null; + DestinationDrive.Text = null; + } + else if (SourceDrive.Text == EfiDrive.Text) + { + ShowMessage("The source drive letter cannot be the same as the bootloader drive."); + SourceDrive.Text = null; + EfiDrive.Text = null; + } + } + } + } +} diff --git a/Windows Installer.sln b/Windows Installer.sln index d2766a2..629e65d 100644 --- a/Windows Installer.sln +++ b/Windows Installer.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp", "ConsoleApp\Co EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInstallerLib", "WindowsInstallerLib\WindowsInstallerLib.csproj", "{0621F1C4-7D92-4863-8BF3-39E2D8D0661C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUIApp", "GUIApp\GUIApp.csproj", "{DEB8CFC7-E65A-B638-0B78-5385B78C1865}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -21,6 +23,10 @@ Global {0621F1C4-7D92-4863-8BF3-39E2D8D0661C}.Debug|x64.Build.0 = Debug|x64 {0621F1C4-7D92-4863-8BF3-39E2D8D0661C}.Release|x64.ActiveCfg = Release|x64 {0621F1C4-7D92-4863-8BF3-39E2D8D0661C}.Release|x64.Build.0 = Release|x64 + {DEB8CFC7-E65A-B638-0B78-5385B78C1865}.Debug|x64.ActiveCfg = Debug|x64 + {DEB8CFC7-E65A-B638-0B78-5385B78C1865}.Debug|x64.Build.0 = Debug|x64 + {DEB8CFC7-E65A-B638-0B78-5385B78C1865}.Release|x64.ActiveCfg = Release|x64 + {DEB8CFC7-E65A-B638-0B78-5385B78C1865}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 0651b2491033f1c4e9634ed221a31cfe2bf714b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Tue, 25 Feb 2025 19:05:27 +0000 Subject: [PATCH 05/22] GUIApp: set version to 1.0.0.0 --- GUIApp/GUIApp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUIApp/GUIApp.csproj b/GUIApp/GUIApp.csproj index 686e871..e54a4e4 100644 --- a/GUIApp/GUIApp.csproj +++ b/GUIApp/GUIApp.csproj @@ -22,7 +22,7 @@ True none True - 0.0.3.0 + 1.0.0.0 $(Version) $(Version) From 0aa56726fb4f0ac61bc198bfa7c33861fe574fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Mon, 3 Mar 2025 15:46:50 +0000 Subject: [PATCH 06/22] GUIApp: remove unnecessary library --- GUIApp/src/Utilities.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/GUIApp/src/Utilities.cs b/GUIApp/src/Utilities.cs index ed5c9f1..6f2c7a7 100644 --- a/GUIApp/src/Utilities.cs +++ b/GUIApp/src/Utilities.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.IO; using System.Windows.Forms; using WindowsInstallerLib.Helpers; From 23e0b604d84f748816a79d844c7432fb0b9457df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Fri, 7 Mar 2025 19:52:58 +0000 Subject: [PATCH 07/22] GUIApp: fix warning regarding trimming code --- GUIApp/src/MainWindow.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GUIApp/src/MainWindow.cs b/GUIApp/src/MainWindow.cs index cfd3f2d..aff01ea 100644 --- a/GUIApp/src/MainWindow.cs +++ b/GUIApp/src/MainWindow.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.Versioning; using System.Windows.Forms; @@ -16,6 +17,7 @@ public partial class MainWindow : Form ImageIndex = -1 }; + [RequiresUnreferencedCode("Calls System.ComponentModel.ComponentResourceManager.ApplyResources(Object, String)")] public MainWindow() { From 3057a846110fe42a65e74e40a271ee65e42bfd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:52:21 +0100 Subject: [PATCH 08/22] GUIApp: add application manifest file and reference in project --- GUIApp/GUIApp.csproj | 1 + GUIApp/app.manifest | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 GUIApp/app.manifest diff --git a/GUIApp/GUIApp.csproj b/GUIApp/GUIApp.csproj index e54a4e4..165adef 100644 --- a/GUIApp/GUIApp.csproj +++ b/GUIApp/GUIApp.csproj @@ -15,6 +15,7 @@ wit wit x64 + app.manifest diff --git a/GUIApp/app.manifest b/GUIApp/app.manifest new file mode 100644 index 0000000..6e77ae2 --- /dev/null +++ b/GUIApp/app.manifest @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + true + true + + + + + + + + + From 1f3c80b1d4b0f2d33102d59f8c239d30e652108b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:54:02 +0100 Subject: [PATCH 09/22] GUIApp: fix IL2026 warning about trimming code --- GUIApp/src/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GUIApp/src/Program.cs b/GUIApp/src/Program.cs index fec56fc..ee1952d 100644 --- a/GUIApp/src/Program.cs +++ b/GUIApp/src/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Windows.Forms; namespace wit @@ -9,6 +10,7 @@ internal static class Program /// The main entry point for the application. /// [STAThread] + [RequiresUnreferencedCode("Calls wit.MainWindow.MainWindow()")] static void Main() { // To customize application configuration such as set high DPI settings or default font, From 6b40ef73d626079d07dbd6b6937d29b1aa03a1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:54:23 +0100 Subject: [PATCH 10/22] GUIApp: format Parameters struct constructor for improved readability --- WindowsInstallerLib/src/InstallerManager.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/WindowsInstallerLib/src/InstallerManager.cs b/WindowsInstallerLib/src/InstallerManager.cs index 17934c5..623d673 100644 --- a/WindowsInstallerLib/src/InstallerManager.cs +++ b/WindowsInstallerLib/src/InstallerManager.cs @@ -18,13 +18,13 @@ namespace WindowsInstallerLib /// [SupportedOSPlatform("windows")] public struct Parameters(string DestinationDrive, - string EfiDrive, - int DiskNumber, - string SourceDrive, - int ImageIndex, - string ImageFilePath, - bool InstallExtraDrivers, - string FirmwareType) + string EfiDrive, + int DiskNumber, + string SourceDrive, + int ImageIndex, + string ImageFilePath, + bool InstallExtraDrivers, + string FirmwareType) { public string DestinationDrive { get; set; } = DestinationDrive; public string EfiDrive { get; set; } = EfiDrive; From 6df28f437b7a10161829560a5b96f07277ef6273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:55:23 +0100 Subject: [PATCH 11/22] GUIApp: remove unused library System.IO --- WindowsInstallerLib/src/Helpers.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/WindowsInstallerLib/src/Helpers.cs b/WindowsInstallerLib/src/Helpers.cs index 8e3366e..4364311 100644 --- a/WindowsInstallerLib/src/Helpers.cs +++ b/WindowsInstallerLib/src/Helpers.cs @@ -1,7 +1,6 @@ using Microsoft.Dism; using System; using System.Collections.Generic; -using System.IO; namespace WindowsInstallerLib { From c8a18cda9fd2509468f78d897b440b3255f7d578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:56:15 +0100 Subject: [PATCH 12/22] GUIApp: add caption parameter to ShowMessage for future reuse --- GUIApp/src/ValidateDiskLetter.cs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/GUIApp/src/ValidateDiskLetter.cs b/GUIApp/src/ValidateDiskLetter.cs index c476bf7..8e5be60 100644 --- a/GUIApp/src/ValidateDiskLetter.cs +++ b/GUIApp/src/ValidateDiskLetter.cs @@ -5,9 +5,16 @@ namespace wit { public partial class MainWindow { - private static void ShowMessage(string text) + private static void ShowMessage(string text, string caption) { - MessageBox.Show(text, "Duplicate letters", MessageBoxButtons.OK, MessageBoxIcon.Error); + try + { + MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + catch + { + throw; + } } private void ValidateDiskLetter(object? sender, EventArgs e) @@ -16,13 +23,13 @@ private void ValidateDiskLetter(object? sender, EventArgs e) { if (DestinationDrive.Text == EfiDrive.Text) { - ShowMessage("The OS drive letter cannot be the same as the bootloader drive."); + ShowMessage("The OS drive letter cannot be the same as the bootloader drive.", "Duplicate letter"); DestinationDrive.Text = null; EfiDrive.Text = null; } else if (DestinationDrive.Text == SourceDrive.Text) { - ShowMessage("The OS drive letter cannot be the same as the source drive."); + ShowMessage("The OS drive letter cannot be the same as the source drive.", "Duplicate letter"); DestinationDrive.Text = null; SourceDrive.Text = null; } @@ -32,13 +39,13 @@ private void ValidateDiskLetter(object? sender, EventArgs e) { if (EfiDrive.Text == DestinationDrive.Text) { - ShowMessage("The bootloader drive letter cannot be the same as the OS drive."); + ShowMessage("The bootloader drive letter cannot be the same as the OS drive.", "Duplicate letter"); EfiDrive.Text = null; DestinationDrive.Text = null; } else if (EfiDrive.Text == SourceDrive.Text) { - ShowMessage("The bootloader drive letter cannot be the same as the source drive."); + ShowMessage("The bootloader drive letter cannot be the same as the source drive.", "Duplicate letter"); EfiDrive.Text = null; SourceDrive.Text = null; } @@ -48,13 +55,13 @@ private void ValidateDiskLetter(object? sender, EventArgs e) { if (SourceDrive.Text == DestinationDrive.Text) { - ShowMessage("The source drive letter cannot be the same as the OS drive."); + ShowMessage("The source drive letter cannot be the same as the OS drive.", "Duplicate letter"); SourceDrive.Text = null; DestinationDrive.Text = null; } else if (SourceDrive.Text == EfiDrive.Text) { - ShowMessage("The source drive letter cannot be the same as the bootloader drive."); + ShowMessage("The source drive letter cannot be the same as the bootloader drive.", "Duplicate letter"); SourceDrive.Text = null; EfiDrive.Text = null; } From d8bae9585c3d87f123e564ce06d22091534c3e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:57:47 +0100 Subject: [PATCH 13/22] GUIApp: remove checking for admin privileges --- GUIApp/src/MainWindow.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/GUIApp/src/MainWindow.cs b/GUIApp/src/MainWindow.cs index aff01ea..9082f77 100644 --- a/GUIApp/src/MainWindow.cs +++ b/GUIApp/src/MainWindow.cs @@ -20,14 +20,6 @@ public partial class MainWindow : Form [RequiresUnreferencedCode("Calls System.ComponentModel.ComponentResourceManager.ApplyResources(Object, String)")] public MainWindow() { - - if (!Privileges.IsAdmin()) - { - MessageBox.Show("You must have administrator privileges to run this program.", - "Insufficient privileges", MessageBoxButtons.OK, MessageBoxIcon.Error); - throw new UnauthorizedAccessException("You must have administrator privileges to run this program."); - } - InitializeComponent(); } From 58ce0444118970615ece9b48e7c18be4539627fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 5 Apr 2025 09:10:19 +0100 Subject: [PATCH 14/22] GUIApp: fix warning WFO0003 --- GUIApp/GUIApp.csproj | 1 + GUIApp/app.manifest | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/GUIApp/GUIApp.csproj b/GUIApp/GUIApp.csproj index 165adef..b903b1b 100644 --- a/GUIApp/GUIApp.csproj +++ b/GUIApp/GUIApp.csproj @@ -16,6 +16,7 @@ wit x64 app.manifest + SystemAware diff --git a/GUIApp/app.manifest b/GUIApp/app.manifest index 6e77ae2..b87b45b 100644 --- a/GUIApp/app.manifest +++ b/GUIApp/app.manifest @@ -15,13 +15,6 @@ - - - true - true - - - Date: Tue, 15 Apr 2025 09:12:51 +0100 Subject: [PATCH 15/22] GUIApp: refactor code for image choosing - Rename the function from ChooseISOImage_Click to ChooseImage_Click. - Remove the oversized try..catch block and only use it on the GetImageInfo function call. - Set the SourceDrive to the root path of the chosen image file. --- GUIApp/src/MainWindow.Designer.cs | 4 +- GUIApp/src/MainWindow.cs | 78 +++++++++++++++---------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/GUIApp/src/MainWindow.Designer.cs b/GUIApp/src/MainWindow.Designer.cs index 82f2ab2..43e54f0 100644 --- a/GUIApp/src/MainWindow.Designer.cs +++ b/GUIApp/src/MainWindow.Designer.cs @@ -163,7 +163,7 @@ private void InitializeComponent() resources.ApplyResources(ChooseISOImage, "ChooseISOImage"); ChooseISOImage.Name = "ChooseISOImage"; ChooseISOImage.UseVisualStyleBackColor = false; - ChooseISOImage.Click += ChooseISOImage_Click; + ChooseISOImage.Click += ChooseImage_Click; // // SourceDrive // @@ -298,9 +298,9 @@ private void InitializeComponent() private ComboBox DestinationDrive; private ComboBox EfiDrive; private Button RescanDisks; - private ComboBox SourceDrive; private SaveFileDialog saveFileDialog1; private Label SourceDrive_Label; private Button SourceDrive_Scan; + private ComboBox SourceDrive; } } diff --git a/GUIApp/src/MainWindow.cs b/GUIApp/src/MainWindow.cs index 9082f77..29f2f74 100644 --- a/GUIApp/src/MainWindow.cs +++ b/GUIApp/src/MainWindow.cs @@ -48,58 +48,58 @@ private void CloseApplication(object sender, EventArgs e) Application.Exit(); } - private void ChooseISOImage_Click(object sender, EventArgs e) + private void ChooseImage_Click(object sender, EventArgs e) { - try - { - SourceDrive_Scan.Enabled = false; + SourceDrive_Scan.Enabled = false; - if (ImageFilePath.Text == "No image file is selected.") + if (ImageFilePath.Text == "No image file is selected.") + { + OpenFileDialog OpenFileDialog = new() { - OpenFileDialog OpenFileDialog = new() - { - Filter = "ESD file (*.esd)|*.esd|WIM file (*.wim)|*.wim", - CheckFileExists = true, - CheckPathExists = true, - AddExtension = true, - }; - - DialogResult DialogResult = OpenFileDialog.ShowDialog(); - - if (DialogResult == DialogResult.Cancel) - { - return; - } + Filter = "ESD file (*.esd)|*.esd|WIM file (*.wim)|*.wim", + CheckFileExists = true, + CheckPathExists = true, + AddExtension = true, + }; - if (string.IsNullOrWhiteSpace(OpenFileDialog.FileName)) - { - MessageBox.Show("No image file was selected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - - ImageFilePath.Text = "No image file is selected"; - - return; - } - - if (!OpenFileDialog.FileName.Contains("install")) - { - MessageBox.Show("Invalid image file. It must be 'install.wim' or 'install.esd'.", "Invalid file", MessageBoxButtons.OK, MessageBoxIcon.Error); - - throw new InvalidDataException("Invalid image file. It must be 'install.wim' or 'install.esd'."); - } + DialogResult DialogResult = OpenFileDialog.ShowDialog(); - ImageFilePath.Text = OpenFileDialog.FileName; + if (DialogResult == DialogResult.Cancel) + { + SourceDrive_Label.Enabled = true; + return; + } - parameters.ImageFilePath = ImageFilePath.Text; + if (string.IsNullOrWhiteSpace(OpenFileDialog.FileName)) + { + MessageBox.Show("No image file was selected.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + SourceDrive_Scan.Enabled = true; + ImageFilePath.Text = "No image file is selected"; + return; } - if (ImageList.Columns.Count >= 1) + if (!OpenFileDialog.FileName.Contains("install")) { - ImageList.Columns.Clear(); + MessageBox.Show("Invalid image file. It must be 'install.wim' or 'install.esd'.", "Invalid file", MessageBoxButtons.OK, MessageBoxIcon.Error); + SourceDrive_Scan.Enabled = true; + throw new InvalidDataException("Invalid image file. It must be 'install.wim' or 'install.esd'."); } + parameters.ImageFilePath = OpenFileDialog.FileName; + ImageFilePath.Text = parameters.ImageFilePath; + SourceDrive.SelectedItem = Path.GetPathRoot(ImageFilePath.Text?.TrimEnd('\\')); + } + + if (ImageList.Columns.Count >= 1) + { + ImageList.Columns.Clear(); + } + + try + { GetImageInfo(sender, e); } - catch (Exception) + catch { throw; } From 34bdb423e96178dab288a9076798638d0307c407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:54:28 +0000 Subject: [PATCH 16/22] ConsoleApp: Improve error and parameter handling Replaces 'AdditionalDriversDrive' with 'AdditionalDrive' in ArgumentParser for consistency. Updates Program.cs to log exception messages instead of rethrowing, providing clearer error output during execution. --- ConsoleApp/src/ArgumentParser.cs | 4 ++-- ConsoleApp/src/Program.cs | 25 +++++++++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ConsoleApp/src/ArgumentParser.cs b/ConsoleApp/src/ArgumentParser.cs index 7deb9bb..4ca2def 100644 --- a/ConsoleApp/src/ArgumentParser.cs +++ b/ConsoleApp/src/ArgumentParser.cs @@ -59,7 +59,7 @@ internal static void ParseArgs(ref Parameters parameters, string[] args) parameters.ImageFilePath = args[Array.IndexOf(args, arg) + 1].ToLowerInvariant(); continue; case "/additionaldriversdrive": - parameters.AdditionalDriversDrive = args[Array.IndexOf(args, arg) + 1].ToLowerInvariant(); + parameters.AdditionalDrive = args[Array.IndexOf(args, arg) + 1].ToLowerInvariant(); continue; case "/firmwaretype": parameters.FirmwareType = args[Array.IndexOf(args, arg) + 1].ToUpperInvariant(); @@ -80,7 +80,7 @@ internal static void ParseArgs(ref Parameters parameters, string[] args) Console.WriteLine($" Source Drive: {parameters.SourceDrive}"); Console.WriteLine($" Image Index: {parameters.ImageIndex}"); Console.WriteLine($" Image File Path: {parameters.ImageFilePath}"); - Console.WriteLine($" Additional Drivers Drive: {parameters.AdditionalDriversDrive}"); + Console.WriteLine($" Additional Drivers Drive: {parameters.AdditionalDrive}"); Console.WriteLine($" Firmware Type: {parameters.FirmwareType}"); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); diff --git a/ConsoleApp/src/Program.cs b/ConsoleApp/src/Program.cs index 30d1231..aba4735 100644 --- a/ConsoleApp/src/Program.cs +++ b/ConsoleApp/src/Program.cs @@ -30,36 +30,45 @@ internal static int Main(string[] args) Console.WriteLine($"Created by {ProgramInfo.GetAuthor()}"); #endif } - catch (Exception) + catch (Exception ex) { - throw; + Console.WriteLine($"An error has occurred: {ex.Message}"); } try { ArgumentParser.ParseArgs(ref parameters, args); } - catch (Exception) + catch (Exception ex) { - throw; + Console.WriteLine($"An error has occurred: {ex.Message}"); } try { InstallerManager.Configure(ref parameters); } - catch (Exception) + catch (Exception ex) { - throw; + Console.WriteLine($"An error has occurred: {ex.Message}"); + } + + try + { + InstallerManager.Prepare(ref parameters); + } + catch (Exception ex) + { + Console.WriteLine($"An error has occurred: {ex.Message}"); } try { InstallerManager.InstallWindows(ref parameters); } - catch (Exception) + catch (Exception ex) { - throw; + Console.WriteLine($"An error has occurred: {ex.Message}"); } return 0; From 91ed137c2b4f29e7c13ebd3f9d7a4aefed52170a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:55:23 +0000 Subject: [PATCH 17/22] WindowsInstallerLib: Rewrite logic and improve parameter handling Refactored disk and driver parameter names for clarity, improved privilege checks, and streamlined user input validation in InstallerManager. Replaced DiskManager.ListAll with GetDisks to return disk info programmatically. Enhanced ProcessManager with more detailed console output during disk formatting. Separated preparation and installation logic in InstallerManager for better structure and error handling. --- WindowsInstallerLib/src/DeployManager.cs | 72 ++++----- WindowsInstallerLib/src/DiskManager.cs | 50 ++---- WindowsInstallerLib/src/InstallerManager.cs | 162 ++++++++------------ WindowsInstallerLib/src/ProcessManager.cs | 13 +- 4 files changed, 123 insertions(+), 174 deletions(-) diff --git a/WindowsInstallerLib/src/DeployManager.cs b/WindowsInstallerLib/src/DeployManager.cs index 1b9e272..6181ff3 100644 --- a/WindowsInstallerLib/src/DeployManager.cs +++ b/WindowsInstallerLib/src/DeployManager.cs @@ -337,49 +337,35 @@ internal static DismImageInfoCollection GetImageInfoT(ref Parameters parameters) /// internal static void InstallAdditionalDrivers(ref Parameters parameters) { + bool IS_ADMIN = PrivilegesManager.IsAdmin(); + DismSession? session = null; - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.AdditionalDriversDrive, nameof(parameters)); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.AdditionalDrive, nameof(parameters)); - if (!PrivilegesManager.IsAdmin()) + switch (IS_ADMIN) { - throw new UnauthorizedAccessException("You do not have enough privileges to install additional drivers."); + case true: + try + { + DismApi.Initialize(DismLogLevel.LogErrorsWarnings); + } + catch (DismException) + { + throw; + } + catch (Exception) + { + throw; + } + break; + case false: + throw new UnauthorizedAccessException("You do not have enough privileges to initialize the DISM API."); } try { - switch (PrivilegesManager.IsAdmin()) - { - case true: - try - { - DismApi.Initialize(DismLogLevel.LogErrorsWarnings); - } - catch (DismException) - { - throw; - } - catch (Exception) - { - throw; - } - break; - case false: - throw new UnauthorizedAccessException("You do not have enough privileges to initialize the DISM API."); - } - - try - { - session ??= DismApi.OpenOfflineSession(parameters.DestinationDrive); - } - catch (DismException) - { - throw; - } - catch (Exception) - { - throw; - } + session ??= DismApi.OpenOfflineSession(parameters.DestinationDrive); } catch (DismException) { @@ -392,7 +378,7 @@ internal static void InstallAdditionalDrivers(ref Parameters parameters) try { - DismApi.AddDriversEx(session, parameters.AdditionalDriversDrive, false, true); + DismApi.AddDriversEx(session, parameters.AdditionalDrive, false, true); } finally { @@ -416,9 +402,11 @@ internal static void InstallAdditionalDrivers(ref Parameters parameters) /// Thrown if the current process does not have administrative privileges required to perform the installation. internal static int InstallBootloader(ref Parameters parameters) { - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.DestinationDrive, nameof(parameters.DestinationDrive)); - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.EfiDrive, nameof(parameters.EfiDrive)); - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.FirmwareType, nameof(parameters.FirmwareType)); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.DestinationDrive, nameof(parameters)); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.EfiDrive, nameof(parameters)); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.FirmwareType, nameof(parameters)); + + bool IS_ADMIN = PrivilegesManager.IsAdmin(); string EFI_BOOT_PATH = Path.Join(parameters.EfiDrive, @"\EFI\Boot"); string EFI_MICROSOFT_PATH = Path.Join(parameters.EfiDrive, @"\EFI\Microsoft"); @@ -428,7 +416,7 @@ internal static int InstallBootloader(ref Parameters parameters) bool EFI_MICROSOFT_EXISTS = Directory.Exists(EFI_MICROSOFT_PATH); bool WINDIR_EXISTS = Directory.Exists(WINDIR_PATH); - if (!PrivilegesManager.IsAdmin()) + if (IS_ADMIN) { throw new UnauthorizedAccessException($"You do not have enough privileges to install the bootloader to {parameters.EfiDrive}"); } @@ -445,9 +433,8 @@ internal static int InstallBootloader(ref Parameters parameters) throw new DirectoryNotFoundException(@$"The directory {WINDIR_PATH} does not exist!"); } - Console.WriteLine($"Firmware type is set to: {parameters.FirmwareType}"); Console.WriteLine($"\n==> Installing bootloader to drive {parameters.EfiDrive} in disk {parameters.DiskNumber}"); - ProcessManager.StartCmdProcess("bcdboot", @$"{WINDIR_PATH} /s {parameters.EfiDrive} /f {parameters.FirmwareType}"); + ProcessManager.StartCmdProcess("bcdboot.exe", @$"{WINDIR_PATH} /s {parameters.EfiDrive} /f {parameters.FirmwareType}"); } catch (IOException) { @@ -457,7 +444,6 @@ internal static int InstallBootloader(ref Parameters parameters) { throw; } - return ProcessManager.ExitCode; } } diff --git a/WindowsInstallerLib/src/DiskManager.cs b/WindowsInstallerLib/src/DiskManager.cs index 7f4780f..9189b67 100644 --- a/WindowsInstallerLib/src/DiskManager.cs +++ b/WindowsInstallerLib/src/DiskManager.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Management; @@ -51,50 +53,32 @@ internal static int FormatDisk(ref Parameters parameters) /// model, and device ID for each disk drive found. Note that this method is intended for internal use /// and writes directly to the console. It does not return the retrieved data or provide a way to /// programmatically access it. - internal static void ListAll() + internal static SortedDictionary GetDisks() { + SortedDictionary SystemDisks = []; + try { - WqlObjectQuery DeviceTable = new("SELECT * FROM Win32_DiskDrive"); - ManagementObjectSearcher DeviceInfo = new(DeviceTable); - foreach (ManagementObject o in DeviceInfo.Get().Cast()) + WqlObjectQuery Query = new("SELECT * FROM Win32_DiskDrive"); + ManagementObjectSearcher ObjectSearcher = new(Query); + foreach (ManagementObject obj in ObjectSearcher.Get().Cast()) { - Console.WriteLine("Disk number = " + o["Index"]); - Console.WriteLine("Model = " + o["Model"]); - Console.WriteLine("DeviceID = " + o["DeviceID"]); - Console.WriteLine(""); - } - } - catch (Exception) - { - throw; - } - } + int Index = Convert.ToInt32(obj["Index"], CultureInfo.CurrentCulture); + string? Model = obj["Model"].ToString(); - /// - /// Retrieves an array of all available drive information on the system. - /// - /// An array of objects representing the drives available on the system. - internal static DriveInfo[] GetDisksT() - { - try - { - DriveInfo[] drives = DriveInfo.GetDrives(); + if (Model != null) + { + SystemDisks.Add(Index, Model); + } + } - return drives; - } - catch (IOException) - { - throw; - } - catch (UnauthorizedAccessException) - { - throw; } catch (Exception) { throw; } + + return SystemDisks; } } } diff --git a/WindowsInstallerLib/src/InstallerManager.cs b/WindowsInstallerLib/src/InstallerManager.cs index 5107214..228f0dc 100644 --- a/WindowsInstallerLib/src/InstallerManager.cs +++ b/WindowsInstallerLib/src/InstallerManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Runtime.Versioning; @@ -17,7 +18,7 @@ namespace WindowsInstallerLib /// Gets or sets the source drive containing the data to be imaged. /// Gets or sets the index of the image within the image file to be applied. /// Gets or sets the file path of the image file to be used in the operation. - /// Gets or sets a value indicating whether additional drivers should be installed during the imaging process. + /// Gets or sets a value indicating whether additional drivers should be installed during the imaging process. /// Gets or sets the firmware type of the system being imaged. [SupportedOSPlatform("windows")] public struct Parameters(string DestinationDrive, @@ -26,7 +27,7 @@ public struct Parameters(string DestinationDrive, string SourceDrive, int ImageIndex, string ImageFilePath, - string AdditionalDriversDrive, + string AdditionalDrive, string FirmwareType) { public string DestinationDrive { get; set; } = DestinationDrive; @@ -35,7 +36,7 @@ public struct Parameters(string DestinationDrive, public string SourceDrive { get; set; } = SourceDrive; public int ImageIndex { get; set; } = ImageIndex; public string ImageFilePath { get; set; } = ImageFilePath; - public string AdditionalDriversDrive { get; set; } = AdditionalDriversDrive; + public string AdditionalDrive { get; set; } = AdditionalDrive; public string FirmwareType { get; set; } = FirmwareType; } @@ -62,15 +63,14 @@ public sealed class InstallerManager public static void Configure(ref Parameters parameters) { #region DestinationDrive - if (string.IsNullOrEmpty(parameters.DestinationDrive) || - string.IsNullOrWhiteSpace(parameters.DestinationDrive)) + if (string.IsNullOrWhiteSpace(parameters.DestinationDrive)) { - string p_DestinationDrive; - Console.Write("\n==> Type the mountpoint to use for deploying Windows (e.g. Z:): "); + try { - p_DestinationDrive = Console.ReadLine() ?? throw new ArgumentNullException(nameof(parameters), "DestinationDrive is null!"); + parameters.DestinationDrive = Console.ReadLine() ?? + throw new ArgumentException("A valid destination drive is required.", nameof(parameters)); } catch (IOException) { @@ -89,35 +89,28 @@ public static void Configure(ref Parameters parameters) throw; } - ArgumentException.ThrowIfNullOrWhiteSpace(p_DestinationDrive); - - if (p_DestinationDrive.Length != 2 || - p_DestinationDrive.Length > 2) + if (parameters.DestinationDrive.Length > 2 || + parameters.DestinationDrive.Length < 2) { - throw new ArgumentException(@$"Invalid source drive {p_DestinationDrive}. Too many characters."); + throw new ArgumentException($"Invalid destination drive {parameters.DestinationDrive}."); } - if (p_DestinationDrive.StartsWith(':') || - !p_DestinationDrive.EndsWith(':')) + if (parameters.DestinationDrive.StartsWith(':') || !parameters.DestinationDrive.EndsWith(':')) { - throw new InvalidDataException(@$"Invalid source drive {p_DestinationDrive}. A valid drive is for example: 'Z:'."); + throw new InvalidDataException($"Invalid source drive {parameters.DestinationDrive}."); } - - parameters.DestinationDrive = p_DestinationDrive; } #endregion #region EfiDrive - if (string.IsNullOrEmpty(parameters.EfiDrive) || - string.IsNullOrWhiteSpace(parameters.EfiDrive)) + if (string.IsNullOrWhiteSpace(parameters.EfiDrive)) { - string p_EfiDrive; - Console.Write("\n==> Type the mountpoint to use for the bootloader (e.g. Y:): "); try { - p_EfiDrive = Console.ReadLine() ?? throw new ArgumentNullException(nameof(parameters), "EfiDrive is null!"); ; + parameters.EfiDrive = Console.ReadLine() ?? + throw new ArgumentException("A valid EFI drive is required.", nameof(parameters)); } catch (IOException) { @@ -136,32 +129,31 @@ public static void Configure(ref Parameters parameters) throw; } - ArgumentException.ThrowIfNullOrWhiteSpace(p_EfiDrive); - - if (p_EfiDrive.StartsWith(':')) + if (parameters.EfiDrive.StartsWith(':')) { - throw new ArgumentException(@$"Invalid EFI drive {p_EfiDrive}, it must have a colon at the end not at the beginning. For example: 'Y:'."); + throw new ArgumentException(@$"Invalid EFI drive {parameters.EfiDrive}, it must have a colon at the end not at the beginning. For example: 'Y:'."); } - else if (!p_EfiDrive.EndsWith(':')) + + if (!parameters.EfiDrive.EndsWith(':')) { - throw new ArgumentException($"Invalid EFI drive {p_EfiDrive}, it must have a colon. For example: 'Y:'."); + throw new ArgumentException($"Invalid EFI drive {parameters.EfiDrive}, it must have a colon. For example: 'Y:'."); } - - parameters.EfiDrive = p_EfiDrive; } #endregion #region DiskNumber - if (string.IsNullOrEmpty(parameters.DiskNumber.ToString()) || - string.IsNullOrWhiteSpace(parameters.DiskNumber.ToString()) || - parameters.DiskNumber == -1) + if (string.IsNullOrWhiteSpace(parameters.DiskNumber.ToString()) || parameters.DiskNumber == -1) { - int p_DiskNumber; + Console.WriteLine("\n==> These are the disks available on your system:"); try { - Console.WriteLine("\n==> These are the disks available on your system:"); - DiskManager.ListAll(); + SortedDictionary Disks = DiskManager.GetDisks(); + + foreach (KeyValuePair Disk in Disks) + { + Console.WriteLine("Disk number: {0}\nDisk model: {1}\n", Disk.Key, Disk.Value); + } } catch (Exception) { @@ -171,7 +163,7 @@ public static void Configure(ref Parameters parameters) Console.Write("\n==> Please type the disk number to format (e.g. 0): "); try { - p_DiskNumber = Convert.ToInt32(Console.ReadLine(), CultureInfo.CurrentCulture); + parameters.DiskNumber = Convert.ToInt32(Console.ReadLine(), CultureInfo.CurrentCulture); } catch (FormatException) { @@ -189,21 +181,18 @@ public static void Configure(ref Parameters parameters) { throw; } - - parameters.DiskNumber = p_DiskNumber; } #endregion #region SourceDrive - if (string.IsNullOrEmpty(parameters.SourceDrive) || - string.IsNullOrWhiteSpace(parameters.SourceDrive)) + if (string.IsNullOrWhiteSpace(parameters.SourceDrive)) { - string? p_SourceDrive; + Console.Write("\n==> Specify the mount point where the source are mounted at (e.g. X:): "); - Console.Write("\n==> Specify the mountpount where the source are mounted at (e.g. X:): "); try { - p_SourceDrive = Console.ReadLine(); + parameters.SourceDrive = Console.ReadLine() ?? + throw new ArgumentException("A sourced drive is required.", nameof(parameters)); } catch (IOException) { @@ -222,74 +211,49 @@ public static void Configure(ref Parameters parameters) throw; } - ArgumentException.ThrowIfNullOrWhiteSpace(p_SourceDrive); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.SourceDrive); - if (p_SourceDrive.StartsWith(':')) + if (parameters.SourceDrive.StartsWith(':')) { - throw new ArgumentException(@$"Invalid source drive {p_SourceDrive}, it must have a colon at the end not at the beginning. For example: 'H:'."); + throw new ArgumentException(@$"Invalid source drive {parameters.SourceDrive}, it must have a colon at the end not at the beginning. For example: 'H:'."); } - else if (!p_SourceDrive.EndsWith(':')) + else if (!parameters.SourceDrive.EndsWith(':')) { - throw new ArgumentException($"Invalid source drive {p_SourceDrive}, it must have a colon. For example: 'H:'."); + throw new ArgumentException($"Invalid source drive {parameters.SourceDrive}, it must have a colon. For example: 'H:'."); } - - parameters.SourceDrive = p_SourceDrive; } #endregion #region ImageFilePath - if (string.IsNullOrEmpty(parameters.ImageFilePath) || - string.IsNullOrWhiteSpace(parameters.ImageFilePath)) + if (string.IsNullOrWhiteSpace(parameters.ImageFilePath)) { - string p_ImageFilePath = DeployManager.GetImageFile(ref parameters); + parameters.ImageFilePath = DeployManager.GetImageFile(ref parameters); - Console.WriteLine($"\nImage file path has been set to {p_ImageFilePath}."); - - parameters.ImageFilePath = p_ImageFilePath; + Console.WriteLine($"\nImage file path has been set to {parameters.ImageFilePath}."); } #endregion #region ImageIndex - if (string.IsNullOrEmpty(parameters.ImageIndex.ToString()) || - string.IsNullOrWhiteSpace(parameters.ImageIndex.ToString()) || - parameters.ImageIndex == -1) + if (parameters.ImageIndex == -1) { DeployManager.GetImageInfo(ref parameters); Console.Write("\n==> Type the index number of the Windows edition you wish to install (e.g. 1): "); - string? SelectedIndex = Console.ReadLine(); - - if (string.IsNullOrEmpty(SelectedIndex) || string.IsNullOrWhiteSpace(SelectedIndex)) - { - throw new ArgumentException("No Windows edition was specified."); - } - - parameters.ImageIndex = Convert.ToInt32(SelectedIndex, CultureInfo.CurrentCulture); + parameters.ImageIndex = Convert.ToInt32(Console.ReadLine() ?? + throw new ArgumentException("No Windows edition was specified."), CultureInfo.CurrentCulture); } #endregion #region FirmwareType - if (string.IsNullOrEmpty(parameters.FirmwareType) || - string.IsNullOrWhiteSpace(parameters.FirmwareType)) + if (string.IsNullOrWhiteSpace(parameters.FirmwareType)) { - switch (SystemInfoManager.IsEFI()) - { - case true: - parameters.FirmwareType = "UEFI"; - Console.WriteLine($"\nThe installer has set the firmware type to {parameters.FirmwareType}.", ConsoleColor.Yellow); - break; - case false: - parameters.FirmwareType = "BIOS"; - Console.WriteLine($"\nThe installer has set the firmware type to {parameters.FirmwareType}.", ConsoleColor.Yellow); - break; - default: - throw new InvalidDataException($"Invalid firmware type: {parameters.FirmwareType}"); - } + parameters.FirmwareType = SystemInfoManager.IsEFI() ? "UEFI" : "BIOS"; + Console.WriteLine($"\nThe installer has set the firmware type to {parameters.FirmwareType}.", ConsoleColor.Yellow); } #endregion - #region AdditionalDriversList - if (string.IsNullOrWhiteSpace(parameters.AdditionalDriversDrive)) + #region AdditionalDrive + if (string.IsNullOrWhiteSpace(parameters.AdditionalDrive)) { Console.Write("\n=> Do you want to add additional drivers to your installation?: [Y/N]: "); string? UserWantsExtraDrivers = Console.ReadLine()?.ToLower(CultureInfo.CurrentCulture); @@ -317,7 +281,7 @@ public static void Configure(ref Parameters parameters) throw new FileNotFoundException($"The directory {driversPath} does not exist"); } - parameters.AdditionalDriversDrive = driversPath; + parameters.AdditionalDrive = driversPath; break; default: return; @@ -340,7 +304,7 @@ public static void Configure(ref Parameters parameters) /// Thrown if the object contains invalid or missing values, such as: - Disk /// number is -1. - EFI drive is null, empty, or whitespace. [SupportedOSPlatform("windows")] - public static void InstallWindows(ref Parameters parameters) + public static void Prepare(ref Parameters parameters) { if (parameters.DiskNumber.Equals(-1)) { @@ -352,10 +316,11 @@ public static void InstallWindows(ref Parameters parameters) throw new InvalidDataException("No EFI drive was specified, required for the bootloader installation."); } - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.DestinationDrive); - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.ImageFilePath); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.DestinationDrive, parameters.DestinationDrive); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.EfiDrive, parameters.EfiDrive); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.ImageFilePath, parameters.ImageFilePath); ArgumentOutOfRangeException.ThrowIfEqual(parameters.ImageIndex, -1); - ArgumentException.ThrowIfNullOrWhiteSpace(parameters.FirmwareType); + ArgumentException.ThrowIfNullOrWhiteSpace(parameters.FirmwareType, parameters.FirmwareType); try { @@ -365,32 +330,35 @@ public static void InstallWindows(ref Parameters parameters) { Console.WriteLine($"An error occurred while formatting the disk: {ex}", ConsoleColor.Red); } + } + public static void InstallWindows(ref Parameters parameters) + { try { DeployManager.ApplyImage(ref parameters); } - catch (Exception ex) + catch { - Console.WriteLine($"An error occurred while applying the image: {ex}", ConsoleColor.Red); + throw; } try { DeployManager.InstallAdditionalDrivers(ref parameters); } - catch (Exception ex) + catch { - Console.WriteLine($"An error occurred when installing additional drivers: {ex}", ConsoleColor.Yellow); + throw; } try { DeployManager.InstallBootloader(ref parameters); } - catch (Exception ex) + catch { - Console.WriteLine($"An error occurred while installing the bootloader: {ex}", ConsoleColor.Red); + throw; } } } diff --git a/WindowsInstallerLib/src/ProcessManager.cs b/WindowsInstallerLib/src/ProcessManager.cs index f4859ce..9b5264d 100644 --- a/WindowsInstallerLib/src/ProcessManager.cs +++ b/WindowsInstallerLib/src/ProcessManager.cs @@ -86,20 +86,31 @@ internal static int StartDiskPartProcess(int DiskNumber, string EfiDrive, string process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.CreateNoWindow = true; + process.Start(); Console.WriteLine($"Formatting disk {DiskNumber}, please wait..."); process.StandardInput.WriteLine($"select disk {DiskNumber}"); process.StandardInput.WriteLine("clean"); + Console.WriteLine($"Cleaning disk {DiskNumber}..."); process.StandardInput.WriteLine("convert gpt"); + Console.WriteLine($"Converting disk {DiskNumber} to GPT"); process.StandardInput.WriteLine("create partition efi size=100"); - process.StandardInput.WriteLine("create partition msr size=16"); + Console.WriteLine("Creating a 100MB EFI partition..."); process.StandardInput.WriteLine("format fs=fat32 quick"); + Console.WriteLine("Formatting EFI partition with FAT32..."); process.StandardInput.WriteLine($"assign letter {EfiDrive}"); + Console.WriteLine($"Assigning letter {EfiDrive} to EFI partition..."); + process.StandardInput.WriteLine("create partition msr size=16"); + Console.WriteLine("Creating MSR partition..."); process.StandardInput.WriteLine("create partition primary"); + Console.WriteLine($"Creating main partition..."); process.StandardInput.WriteLine("format fs=ntfs quick"); + Console.WriteLine($"Formatting main partition with NTFS..."); process.StandardInput.WriteLine($"assign letter {DestinationDrive}"); + Console.WriteLine($"Assigning letter {DestinationDrive} to main partition..."); process.StandardInput.WriteLine("exit"); process.WaitForExit(); From ed439e149b953b21b17ac34c00d0d410abce79a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:56:01 +0000 Subject: [PATCH 18/22] WindowsInstallerLib: bump version to 1.1.5.0 --- WindowsInstallerLib/WindowsInstallerLib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WindowsInstallerLib/WindowsInstallerLib.csproj b/WindowsInstallerLib/WindowsInstallerLib.csproj index 3969d30..4965f4d 100644 --- a/WindowsInstallerLib/WindowsInstallerLib.csproj +++ b/WindowsInstallerLib/WindowsInstallerLib.csproj @@ -8,7 +8,7 @@ none $(AssemblyName) latest - 1.1.4.0 + 1.1.5.0 x64 true From 60405122624b78c0defcbfb2bbb0cd3741c36bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 17:56:33 +0000 Subject: [PATCH 19/22] ConsoleApp: bump version to 0.0.8.0 --- ConsoleApp/ConsoleApp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConsoleApp/ConsoleApp.csproj b/ConsoleApp/ConsoleApp.csproj index da2b86d..c8aea25 100644 --- a/ConsoleApp/ConsoleApp.csproj +++ b/ConsoleApp/ConsoleApp.csproj @@ -12,7 +12,7 @@ none latest-recommended ConsoleApp.Program - 0.0.7.0 + 0.0.8.0 true app.manifest From dac46f0560545f65e8dec0003e1e4b9b9f02c7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:46:40 +0000 Subject: [PATCH 20/22] GUIApp: upgrade to .NET 10 --- GUIApp/GUIApp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUIApp/GUIApp.csproj b/GUIApp/GUIApp.csproj index b903b1b..c2fcb04 100644 --- a/GUIApp/GUIApp.csproj +++ b/GUIApp/GUIApp.csproj @@ -2,7 +2,7 @@ WinExe - net9.0-windows + net10.0-windows enable true wit.Program From ba082420b5bca78f4a08409d8f09e7f9df294e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 18:47:11 +0000 Subject: [PATCH 21/22] GUIApp: update assemblyIdentity information Change 'name' to GUIApp.app' --- GUIApp/app.manifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUIApp/app.manifest b/GUIApp/app.manifest index b87b45b..180aa93 100644 --- a/GUIApp/app.manifest +++ b/GUIApp/app.manifest @@ -1,6 +1,6 @@  - + From 3d3324de09d701c994f8b3130c55323fc24bf945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Gonz=C3=A1lez=20Mart=C3=ADn?= <158048821+felgmar@users.noreply.github.com> Date: Sat, 17 Jan 2026 19:10:07 +0000 Subject: [PATCH 22/22] WindowsInstallerLib: Fix a typo --- WindowsInstallerLib/src/DeployManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WindowsInstallerLib/src/DeployManager.cs b/WindowsInstallerLib/src/DeployManager.cs index c7c4e87..0f97d1f 100644 --- a/WindowsInstallerLib/src/DeployManager.cs +++ b/WindowsInstallerLib/src/DeployManager.cs @@ -471,7 +471,7 @@ internal static int InstallBootloader(ref Parameters parameters) bool EFI_MICROSOFT_EXISTS = Directory.Exists(EFI_MICROSOFT_PATH); bool WINDIR_EXISTS = Directory.Exists(WINDIR_PATH); - if (IS_ADMIN) + if (!IS_ADMIN) { throw new UnauthorizedAccessException($"You do not have enough privileges to install the bootloader to {parameters.EfiDrive}"); }