diff --git a/ConsoleApp/src/ArgumentParser.cs b/ConsoleApp/src/ArgumentParser.cs index 68f5c51..621e566 100644 --- a/ConsoleApp/src/ArgumentParser.cs +++ b/ConsoleApp/src/ArgumentParser.cs @@ -64,6 +64,9 @@ internal static void ParseArgs(ref Parameters parameters, string[] args) case "/firmwaretype": parameters.FirmwareType = args[Array.IndexOf(args, arg) + 1].ToUpperInvariant(); continue; + case "/multithreaded": + parameters.UseMultiThreading = args[Array.IndexOf(args, arg) + 1].Equals("TRUE", StringComparison.OrdinalIgnoreCase); + continue; } } #if DEBUG @@ -76,6 +79,7 @@ internal static void ParseArgs(ref Parameters parameters, string[] args) Console.WriteLine($" Image File Path: {parameters.ImageFilePath}"); Console.WriteLine($" Install Extra Drivers: {parameters.InstallExtraDrivers}"); Console.WriteLine($" Firmware Type: {parameters.FirmwareType}"); + Console.WriteLine($" Multithreaded: {parameters.UseMultiThreading}"); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); #endif diff --git a/WindowsInstallerLib/src/InstallerManager.cs b/WindowsInstallerLib/src/InstallerManager.cs index 18a9e6d..712e8d2 100644 --- a/WindowsInstallerLib/src/InstallerManager.cs +++ b/WindowsInstallerLib/src/InstallerManager.cs @@ -37,6 +37,7 @@ public struct Parameters(string DestinationDrive, public string ImageFilePath { get; set; } = ImageFilePath; public bool InstallExtraDrivers { get; set; } = InstallExtraDrivers; public string FirmwareType { get; set; } = FirmwareType; + public bool UseMultiThreading { get; set; } = false; } /// @@ -61,6 +62,40 @@ public sealed class InstallerManager /// Thrown if the firmware type cannot be determined or is invalid. public static void Configure(ref Parameters parameters) { + #region UseMultiThreading + Console.WriteLine("\nDo you want to enable multithreading?: "); + string? p_UseMultiThreading = Console.ReadLine(); + + if (!string.IsNullOrWhiteSpace(p_UseMultiThreading)) + { + try + { + parameters.UseMultiThreading = p_UseMultiThreading.ToLowerInvariant() switch + { + "yes" or "y" or "true" => true, + "no" or "n" or "false" => false, + _ => throw new ArgumentException("Invalid input for multithreading option. Please type 'yes' or 'no'.") + }; + } + catch (Exception) + { + throw; + } + } + + switch (parameters.UseMultiThreading) + { + case true: + parameters.UseMultiThreading = true; + Console.WriteLine($"\nMultithreading is enabled.", ConsoleColor.Yellow); + break; + case false: + parameters.UseMultiThreading = false; + Console.WriteLine($"\nMultithreading is disabled.", ConsoleColor.Yellow); + break; + } + #endregion + #region DestinationDrive if (string.IsNullOrEmpty(parameters.DestinationDrive) || string.IsNullOrWhiteSpace(parameters.DestinationDrive)) diff --git a/WindowsInstallerLib/src/ProcessManager.cs b/WindowsInstallerLib/src/ProcessManager.cs index 2d43b6a..8e46d52 100644 --- a/WindowsInstallerLib/src/ProcessManager.cs +++ b/WindowsInstallerLib/src/ProcessManager.cs @@ -40,7 +40,6 @@ internal static int StartCmdProcess(string fileName, string args) process.Start(); process.WaitForExit(); ExitCode = process.ExitCode; - } catch (InvalidOperationException) { @@ -144,6 +143,14 @@ internal static int StartDiskPartProcess(int DiskNumber, string EfiDrive, string return ExitCode; } + internal static int StartDiskPartProcessThreaded(int DiskNumber, string EfiDrive, string DestinationDrive) + { + ThreadManager.CreateThread( + () => StartDiskPartProcess(DiskNumber, EfiDrive, DestinationDrive) + ); + return ExitCode; + } + /// /// Starts a new process to execute the Deployment Image Servicing and Management (DISM) tool with the specified /// arguments. @@ -188,6 +195,15 @@ internal static int StartDismProcess(string args) return ExitCode; } + internal static int StartDismProcessThreaded(string args) + { + ThreadManager.CreateThread( + () => StartDismProcess(args) + ); + + return ExitCode; + } + /// /// Starts a process with the specified executable file and arguments, waits for it to exit, and returns the /// process's exit code. @@ -235,5 +251,14 @@ internal static int StartProcess(string filename, string args) return ExitCode; } + + internal static int StartProcessThreaded(string filename, string args) + { + ThreadManager.CreateThread( + () => StartProcess(filename, args) + ); + + return ExitCode; + } } } diff --git a/WindowsInstallerLib/src/ThreadManager.cs b/WindowsInstallerLib/src/ThreadManager.cs new file mode 100644 index 0000000..7302964 --- /dev/null +++ b/WindowsInstallerLib/src/ThreadManager.cs @@ -0,0 +1,117 @@ +using System; +using System.Threading; + +namespace WindowsInstallerLib +{ + /// + /// This class handles the priority, the state, etcetera of threads. + /// + internal sealed class ThreadManager + { + /// + /// Gets the current priority of the thread. + /// + /// + /// + /// BelowNormal, Normal, AboveNormal or Highest + /// + private static ThreadPriority GetThreadPriority(Thread thread) { return thread.Priority; } + private static ThreadPriority SetThreadPriority(Thread thread, ThreadPriority threadPriority) { return thread.Priority = threadPriority; } + private static ThreadState GetThreadState(Thread thread) { return thread.ThreadState; } + private static ApartmentState GetThreadApartmentState(Thread thread) { return thread.GetApartmentState(); } + private static bool TrySetThreadApartmentState(Thread thread, ApartmentState apartmentState) + { + try + { + return thread.TrySetApartmentState(apartmentState); + } + catch (PlatformNotSupportedException) + { + throw; + } + catch (ArgumentException) + { + throw; + } + catch (ThreadStateException) + { + throw; + } + } + private static Thread? GetCurrentThread() { return Thread.CurrentThread; } + internal static bool IsBackground(Thread thread) { return thread.IsBackground; } + internal static bool IsThreadAlive(Thread thread) { return thread.IsAlive; } + + internal static Thread CreateThread(Action action) + { + Thread thread = new(() => + { + action.Invoke(); + } + ); + + if (GetThreadState(thread) == ThreadState.Running) + { + //throw new InvalidOperationException($"Cannot modify the state of a thread when it is already running."); + thread.Join(); + } + + try + { + thread.IsBackground = true; + } + catch (ThreadStateException ex) + { + if (ex.InnerException != null) + { + throw ex.InnerException; + } + } + catch + { + throw; + } + + try + { + if (GetThreadPriority(thread) == ThreadPriority.Normal) + { + SetThreadPriority(thread, ThreadPriority.AboveNormal); + } + } + catch + { + throw; + } + + try + { + TrySetThreadApartmentState(thread, ApartmentState.MTA); + } + catch + { + throw; + } + + try + { + if (GetCurrentThread != null && + GetThreadState(thread) == ThreadState.Unstarted) + { + thread.Start(); + } + } + catch (Exception ex) + { + switch (ex.InnerException != null) + { + case true: + throw ex.InnerException; + case false: + throw; + } + } + return thread; + } + } +}