mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Feat: Add configurable thread limit for parallel operations
Adds a "Threads" setting to the UI, allowing users to control the throttle limit for parallel tasks like driver and application processing. This introduces a new textbox in the build options and updates the parallel processing function to use this configurable value instead of a hardcoded one. Input validation is also added to ensure the threads value is a valid integer and is at least 1. The new setting is integrated into the configuration save/load functionality.
This commit is contained in:
@@ -300,7 +300,8 @@ function Invoke-CopyBYOApps {
|
||||
-CompletedStatusText "Copied" `
|
||||
-ErrorStatusPrefix "Error: " `
|
||||
-WindowObject $State.Window `
|
||||
-MainThreadLogPath $State.LogFilePath
|
||||
-MainThreadLogPath $State.LogFilePath `
|
||||
-ThrottleLimit $State.Controls.txtThreads.Text
|
||||
|
||||
# Final status update (handled by Invoke-ParallelProcessing)
|
||||
$State.Controls.pbOverallProgress.Visibility = 'Collapsed'
|
||||
|
||||
@@ -89,6 +89,7 @@ function Get-UIConfig {
|
||||
UserAppListPath = "$($State.Controls.txtApplicationPath.Text)\UserAppList.json"
|
||||
USBDriveList = @{}
|
||||
Username = $State.Controls.txtUsername.Text
|
||||
Threads = [int]$State.Controls.txtThreads.Text
|
||||
Verbose = $State.Controls.chkVerbose.IsChecked
|
||||
VMHostIPAddress = $State.Controls.txtVMHostIPAddress.Text
|
||||
VMLocation = $State.Controls.txtVMLocation.Text
|
||||
@@ -272,6 +273,7 @@ function Update-UIFromConfig {
|
||||
Set-UIValue -ControlName 'txtFFUCaptureLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUCaptureLocation' -State $State
|
||||
Set-UIValue -ControlName 'txtShareName' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'ShareName' -State $State
|
||||
Set-UIValue -ControlName 'txtUsername' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Username' -State $State
|
||||
Set-UIValue -ControlName 'txtThreads' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Threads' -State $State
|
||||
Set-UIValue -ControlName 'chkBuildUSBDriveEnable' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'BuildUSBDrive' -State $State
|
||||
Set-UIValue -ControlName 'chkCompactOS' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'CompactOS' -State $State
|
||||
Set-UIValue -ControlName 'chkUpdateADK' -PropertyName 'IsChecked' -ConfigObject $ConfigContent -ConfigKey 'UpdateADK' -State $State
|
||||
|
||||
@@ -633,7 +633,8 @@ function Invoke-DownloadSelectedDrivers {
|
||||
-CompletedStatusText 'Completed' `
|
||||
-ErrorStatusPrefix 'Error: ' `
|
||||
-WindowObject $State.Window `
|
||||
-MainThreadLogPath $State.LogFilePath
|
||||
-MainThreadLogPath $State.LogFilePath `
|
||||
-ThrottleLimit $State.Controls.txtThreads.Text
|
||||
|
||||
$overallSuccess = $true
|
||||
$successfullyDownloaded = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
@@ -670,10 +671,10 @@ function Invoke-DownloadSelectedDrivers {
|
||||
$make = $makeLookup[$modelName]
|
||||
if ($make) {
|
||||
$successfullyDownloaded.Add([PSCustomObject]@{
|
||||
Make = $make
|
||||
Model = $modelName
|
||||
DriverPath = $driverPath
|
||||
})
|
||||
Make = $make
|
||||
Model = $modelName
|
||||
DriverPath = $driverPath
|
||||
})
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: Could not find 'Make' for successful download of model '$modelName'. Skipping from DriverMapping.json."
|
||||
@@ -722,7 +723,7 @@ function Invoke-DownloadSelectedDrivers {
|
||||
}
|
||||
'HP' {
|
||||
$modelObject = @{
|
||||
Name = $driverItem.Model
|
||||
Name = $driverItem.Model
|
||||
}
|
||||
}
|
||||
'Lenovo' {
|
||||
|
||||
@@ -2,6 +2,69 @@ function Register-EventHandlers {
|
||||
param([PSCustomObject]$State)
|
||||
WriteLog "Registering UI event handlers..."
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# SECTION: Shared Input Validation Handlers
|
||||
# --------------------------------------------------------------------------
|
||||
# Define a shared event handler for TextBoxes that should only accept integer input
|
||||
$integerPreviewTextInputHandler = {
|
||||
param($eventSource, $textCompositionEventArgs)
|
||||
# Use a regex to check if the input text is NOT a digit. \D matches any non-digit character.
|
||||
if ($textCompositionEventArgs.Text -match '\D') {
|
||||
# If the input is not a digit, mark the event as handled to prevent the character from being entered.
|
||||
$textCompositionEventArgs.Handled = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Define a handler to validate pasted text, ensuring it's only integers
|
||||
$integerPastingHandler = {
|
||||
param($sender, $pastingEventArgs)
|
||||
if ($pastingEventArgs.DataObject.GetDataPresent([string])) {
|
||||
$pastedText = $pastingEventArgs.DataObject.GetData([string])
|
||||
# Check if the pasted text consists ONLY of one or more digits.
|
||||
if ($pastedText -notmatch '^\d+$') {
|
||||
# If not, cancel the paste operation.
|
||||
$pastingEventArgs.CancelCommand()
|
||||
}
|
||||
}
|
||||
else {
|
||||
# If the pasted data is not in a string format, cancel it.
|
||||
$pastingEventArgs.CancelCommand()
|
||||
}
|
||||
}
|
||||
|
||||
# List of TextBox controls that require integer-only input
|
||||
$integerOnlyTextBoxes = @(
|
||||
$State.Controls.txtDiskSize,
|
||||
$State.Controls.txtMemory,
|
||||
$State.Controls.txtProcessors,
|
||||
$State.Controls.txtThreads
|
||||
)
|
||||
|
||||
# Attach the handlers to each relevant textbox
|
||||
foreach ($textBox in $integerOnlyTextBoxes) {
|
||||
if ($null -ne $textBox) {
|
||||
$textBox.Add_PreviewTextInput($integerPreviewTextInputHandler)
|
||||
[System.Windows.DataObject]::AddPastingHandler($textBox, $integerPastingHandler)
|
||||
}
|
||||
}
|
||||
|
||||
# Add specific validation for the Threads textbox to ensure it's not empty and is at least 1
|
||||
if ($null -ne $State.Controls.txtThreads) {
|
||||
$State.Controls.txtThreads.Add_LostFocus({
|
||||
param($eventSource, $routedEventArgs)
|
||||
$textBox = $eventSource
|
||||
$currentValue = 0
|
||||
# Try to parse the current text as an integer
|
||||
$isValidInteger = [int]::TryParse($textBox.Text, [ref]$currentValue)
|
||||
|
||||
# If the text is not a valid integer OR the value is less than 1, reset it to the default value '1'
|
||||
if (-not $isValidInteger -or $currentValue -lt 1) {
|
||||
$textBox.Text = '1'
|
||||
WriteLog "Threads value was invalid or less than 1. Reset to 1."
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Build Tab Event Handlers
|
||||
$State.Controls.btnBrowseFFUDevPath.Add_Click({
|
||||
param($eventSource, $routedEventArgs)
|
||||
|
||||
@@ -95,6 +95,7 @@ function Initialize-UIControls {
|
||||
$State.Controls.txtFFUCaptureLocation = $window.FindName('txtFFUCaptureLocation')
|
||||
$State.Controls.txtShareName = $window.FindName('txtShareName')
|
||||
$State.Controls.txtUsername = $window.FindName('txtUsername')
|
||||
$State.Controls.txtThreads = $window.FindName('txtThreads')
|
||||
$State.Controls.chkCompactOS = $window.FindName('chkCompactOS')
|
||||
$State.Controls.chkOptimize = $window.FindName('chkOptimize')
|
||||
$State.Controls.chkAllowVHDXCaching = $window.FindName('chkAllowVHDXCaching')
|
||||
@@ -206,6 +207,7 @@ function Initialize-UIDefaults {
|
||||
$State.Controls.txtFFUCaptureLocation.Text = $State.Defaults.generalDefaults.FFUCaptureLocation
|
||||
$State.Controls.txtShareName.Text = $State.Defaults.generalDefaults.ShareName
|
||||
$State.Controls.txtUsername.Text = $State.Defaults.generalDefaults.Username
|
||||
$State.Controls.txtThreads.Text = $State.Defaults.generalDefaults.Threads
|
||||
$State.Controls.chkBuildUSBDriveEnable.IsChecked = $State.Defaults.generalDefaults.BuildUSBDriveEnable
|
||||
$State.Controls.chkCompactOS.IsChecked = $State.Defaults.generalDefaults.CompactOS
|
||||
$State.Controls.chkUpdateADK.IsChecked = $State.Defaults.generalDefaults.UpdateADK
|
||||
|
||||
@@ -155,12 +155,12 @@ function Search-WingetPackagesPublic {
|
||||
# for large datasets as it avoids holding complex objects in memory and bypasses the
|
||||
# expensive formatting system for the raw results.
|
||||
Find-WinGetPackage -Query $Query -ErrorAction Stop |
|
||||
Select-Object -Property @{Name = 'IsSelected'; Expression = { $false } },
|
||||
Name,
|
||||
Id,
|
||||
Version,
|
||||
Source,
|
||||
@{Name = 'DownloadStatus'; Expression = { '' } }
|
||||
Select-Object -Property @{Name = 'IsSelected'; Expression = { $false } },
|
||||
Name,
|
||||
Id,
|
||||
Version,
|
||||
Source,
|
||||
@{Name = 'DownloadStatus'; Expression = { '' } }
|
||||
}
|
||||
catch {
|
||||
WriteLog "Error during Winget search: $($_.Exception.Message)"
|
||||
@@ -731,7 +731,8 @@ function Invoke-WingetDownload {
|
||||
-CompletedStatusText "Completed" `
|
||||
-ErrorStatusPrefix "Error: " `
|
||||
-WindowObject $State.Window `
|
||||
-MainThreadLogPath $State.LogFilePath
|
||||
-MainThreadLogPath $State.LogFilePath `
|
||||
-ThrottleLimit $State.Controls.txtThreads.Text
|
||||
|
||||
# Final status update is handled by Invoke-ParallelProcessing, but we need to re-enable the button
|
||||
$State.Controls.pbOverallProgress.Visibility = 'Collapsed'
|
||||
@@ -754,10 +755,10 @@ function Update-WingetVersionFields {
|
||||
[string]$moduleText
|
||||
)
|
||||
$State.Window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Normal, [Action] {
|
||||
$State.Controls.txtWingetVersion.Text = $wingetText
|
||||
$State.Controls.txtWingetModuleVersion.Text = $moduleText
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
})
|
||||
$State.Controls.txtWingetVersion.Text = $wingetText
|
||||
$State.Controls.txtWingetModuleVersion.Text = $moduleText
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
})
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
@@ -112,6 +112,7 @@ function Get-GeneralDefaults {
|
||||
FFUCaptureLocation = $ffuCapturePath
|
||||
ShareName = "FFUCaptureShare"
|
||||
Username = "ffu_user"
|
||||
Threads = 5
|
||||
BuildUSBDriveEnable = $false
|
||||
CompactOS = $true
|
||||
Optimize = $true
|
||||
|
||||
Reference in New Issue
Block a user