\Sources."
} catch {
Write-Host "Unable to get information about the features. MicroWin processing will continue, but features will not be processed"
+ Write-Host "Error information: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
@@ -156,6 +157,8 @@ function Remove-Packages {
$_ -NotLike "*Wifi*"
}
+ $failedCount = 0
+
foreach ($pkg in $pkglist) {
try {
$status = "Removing $pkg"
@@ -164,12 +167,18 @@ function Remove-Packages {
} catch {
# This can happen if the package that is being removed is a permanent one, like FodMetadata
Write-Host "Could not remove OS package $($pkg)"
+ $failedCount += 1
continue
}
}
Write-Progress -Activity "Removing Apps" -Status "Ready" -Completed
+ if ($failedCount -gt 0)
+ {
+ Write-Host "Some packages could not be removed. Do not worry: your image will still work fine. This can happen if the package is permanent or has been superseded by a newer one."
+ }
} catch {
Write-Host "Unable to get information about the packages. MicroWin processing will continue, but packages will not be processed"
+ Write-Host "Error information: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
@@ -225,6 +234,7 @@ function Remove-ProvisionedPackages() {
{
# This can happen if getting AppX packages fails
Write-Host "Unable to get information about the AppX packages. MicroWin processing will continue, but AppX packages will not be processed"
+ Write-Host "Error information: $($_.Exception.Message)" -ForegroundColor Yellow
}
}
diff --git a/functions/private/Invoke-WinUtilSnapFlyout.ps1 b/functions/private/Invoke-WinUtilSnapFlyout.ps1
index bfb83c31..39d1335b 100644
--- a/functions/private/Invoke-WinUtilSnapFlyout.ps1
+++ b/functions/private/Invoke-WinUtilSnapFlyout.ps1
@@ -14,7 +14,7 @@ function Invoke-WinUtilSnapFlyout {
Write-Host "Disabling Snap Assist Flyout On startup"
$value = 0
}
- # taskkill.exe /F /IM "explorer.exe"
+
$Path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
taskkill.exe /F /IM "explorer.exe"
Set-ItemProperty -Path $Path -Name EnableSnapAssistFlyout -Value $value
diff --git a/functions/private/Set-WinUtilUITheme.ps1 b/functions/private/Set-WinUtilUITheme.ps1
new file mode 100644
index 00000000..c71ae88c
--- /dev/null
+++ b/functions/private/Set-WinUtilUITheme.ps1
@@ -0,0 +1,83 @@
+function Set-WinUtilUITheme {
+ <#
+ .SYNOPSIS
+ Sets the theme of the XAML file
+
+ .PARAMETER inputXML
+ A string representing the XAML object to modify
+
+ .PARAMETER customThemeName
+ The name of the custom theme to set the XAML to. Defaults to 'matrix'
+
+ .PARAMETER defaultThemeName
+ The name of the default theme to use when setting the XAML. Defaults to '_default'
+
+ .EXAMPLE
+ $returnVal = Set-WinUtilUITheme -inputXAML $inputXAML
+ if ($returnVal[0] -eq "") {
+ Write-Host "Failed to process inputXML"
+ } else {
+ $inputXML = $returnVal[0]
+ }
+ # to know which theme this function has used, access the second item in returned value.
+ Write-Host "Theme used in processing: $($returnVal[1])"
+ #>
+
+ param (
+ [Parameter(Mandatory, position=0)]
+ [string]$inputXML,
+
+ [Parameter(position=1)]
+ [string]$customThemeName = 'matrix',
+
+ [Parameter(position=2)]
+ [string]$defaultThemeName = '_default'
+ )
+
+ try {
+ # Note:
+ # Reason behind not caching the '$sync.configs.themes` object into a variable,
+ # because this code can modify the themes object.. meaning it's better to access it
+ # using the more verbose way, rather than introduce possible bugs into the code, just for the sake of readability.
+ #
+ if (-NOT $sync.configs.themes) {
+ throw [GenericException]::new("[Set-WinUtilTheme] Did not find 'config.themes' inside `$sync variable.")
+ }
+
+ if (-NOT $sync.configs.themes.$defaultThemeName) {
+ throw [GenericException]::new("[Set-WinUtilTheme] Did not find '$defaultThemeName' theme in the themes config file.")
+ }
+
+ $themeToUse = $customThemeName
+ if ($sync.configs.themes.$themeToUse) {
+ # Loop through every default theme option, and modify the custom theme in $sync variable,
+ # so that it has full options available for other functions to use.
+ foreach ($option in $sync.configs.themes.$defaultThemeName.PSObject.Properties) {
+ $optionName = $option.Name
+ $optionValue = $option.Value
+ if (-NOT $sync.configs.themes.$themeToUse.$optionName) {
+ $sync.configs.themes.$themeToUse | Add-Member -MemberType NoteProperty -Name $optionName -Value $optionValue
+ }
+ }
+ } else {
+ Write-Debug "[Set-WinUtilTheme] Theme '$customThemeName' was not found, using '$defaultThemeName' instead."
+ $themeToUse = $defaultThemeName
+ }
+
+ foreach ($property in $sync.configs.themes.$themeToUse.PSObject.Properties) {
+ $key = $property.Name
+ $value = $property.Value
+ # Add curly braces around the key
+ $formattedKey = "{$key}"
+ # Replace the key with the value in the input XML
+ $inputXML = $inputXML.Replace($formattedKey, $value)
+ }
+ }
+ catch {
+ Write-Host "[Set-WinUtilTheme] Unable to apply theme" -ForegroundColor Red
+ Write-Host "$($psitem.Exception.Message)" -ForegroundColor Red
+ $inputXML = "" # Make inputXML equal an empty string, indicating something went wrong to the function caller.
+ }
+
+ return @($inputXML, $themeToUse);
+}
diff --git a/functions/private/Set-WinUtilUiTheme.ps1 b/functions/private/Set-WinUtilUiTheme.ps1
deleted file mode 100644
index 45db1ab4..00000000
--- a/functions/private/Set-WinUtilUiTheme.ps1
+++ /dev/null
@@ -1,50 +0,0 @@
-function Set-WinUtilUITheme {
- <#
- .SYNOPSIS
- Sets the theme of the XAML file
-
- .PARAMETER inputXML
- A string representing the XAML object to modify
-
- .PARAMETER themeName
- The name of the theme to set the XAML to. Defaults to 'matrix'
-
- .EXAMPLE
- Set-WinUtilUITheme -inputXAML $inputXAML
- #>
-
- param
- (
- [Parameter(Mandatory, position=0)]
- [string]$inputXML,
- [Parameter(position=1)]
- [string]$themeName = 'matrix'
- )
-
- try {
- # Convert the JSON to a PowerShell object
- $themes = $sync.configs.themes
- # Select the specified theme
- $selectedTheme = $themes.$themeName
-
- if ($selectedTheme) {
- # Loop through all key-value pairs in the selected theme
- foreach ($property in $selectedTheme.PSObject.Properties) {
- $key = $property.Name
- $value = $property.Value
- # Add curly braces around the key
- $formattedKey = "{$key}"
- # Replace the key with the value in the input XML
- $inputXML = $inputXML.Replace($formattedKey, $value)
- }
- } else {
- Write-Host "Theme '$themeName' not found."
- }
-
- } catch {
- Write-Warning "Unable to apply theme"
- Write-Warning $psitem.Exception.StackTrace
- }
-
- return $inputXML;
-}
diff --git a/functions/private/Update-WinUtilProgramWinget.ps1 b/functions/private/Update-WinUtilProgramWinget.ps1
index 0784657d..7f50d35e 100644
--- a/functions/private/Update-WinUtilProgramWinget.ps1
+++ b/functions/private/Update-WinUtilProgramWinget.ps1
@@ -11,7 +11,7 @@ Function Update-WinUtilProgramWinget {
$host.ui.RawUI.WindowTitle = """Winget Install"""
- Start-Transcript $ENV:TEMP\winget-update.log -Append
+ Start-Transcript "$logdir\winget-update_$dateTime.log" -Append
winget upgrade --all --accept-source-agreements --accept-package-agreements --scope=machine --silent
}
diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1
index ac5b2f8c..e3015f09 100644
--- a/functions/public/Invoke-WPFButton.ps1
+++ b/functions/public/Invoke-WPFButton.ps1
@@ -21,13 +21,13 @@ function Invoke-WPFButton {
Switch -Wildcard ($Button) {
"WPFTab?BT" {Invoke-WPFTab $Button}
- "WPFinstall" {Invoke-WPFInstall}
- "WPFuninstall" {Invoke-WPFUnInstall}
+ "WPFInstall" {Invoke-WPFInstall}
+ "WPFUninstall" {Invoke-WPFUnInstall}
"WPFInstallUpgrade" {Invoke-WPFInstallUpgrade}
- "WPFstandard" {Invoke-WPFPresets "Standard"}
- "WPFminimal" {Invoke-WPFPresets "Minimal"}
- "WPFclear" {Invoke-WPFPresets -preset $null -imported $true}
- "WPFclearWinget" {Invoke-WPFPresets -preset $null -imported $true -CheckBox "WPFInstall"}
+ "WPFStandard" {Invoke-WPFPresets "Standard" -checkboxfilterpattern "WPFTweak*"}
+ "WPFMinimal" {Invoke-WPFPresets "Minimal" -checkboxfilterpattern "WPFTweak*"}
+ "WPFClearTweaksSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFTweak*"}
+ "WPFClearInstallSelection" {Invoke-WPFPresets -imported $true -checkboxfilterpattern "WPFInstall*"}
"WPFtweaksbutton" {Invoke-WPFtweaksbutton}
"WPFOOSUbutton" {Invoke-WPFOOSU}
"WPFAddUltPerf" {Invoke-WPFUltimatePerformance -State "Enable"}
diff --git a/functions/public/Invoke-WPFImpex.ps1 b/functions/public/Invoke-WPFImpex.ps1
index 9b03cc13..43d835de 100644
--- a/functions/public/Invoke-WPFImpex.ps1
+++ b/functions/public/Invoke-WPFImpex.ps1
@@ -41,6 +41,8 @@ function Invoke-WPFImpex {
if ($type -eq "export") {
$jsonFile = Get-WinUtilCheckBoxes -unCheck $false
$jsonFile | ConvertTo-Json | Out-File $FileBrowser.FileName -Force
+ $runscript = "iex ""& { `$(irm christitus.com/win) } -Config '$($FileBrowser.FileName)'"""
+ $runscript | Set-Clipboard
}
if ($type -eq "import") {
$jsonFile = Get-Content $Config | ConvertFrom-Json
diff --git a/functions/public/Invoke-WPFPresets.ps1 b/functions/public/Invoke-WPFPresets.ps1
index 1595a8f1..90493899 100644
--- a/functions/public/Invoke-WPFPresets.ps1
+++ b/functions/public/Invoke-WPFPresets.ps1
@@ -10,29 +10,36 @@ function Invoke-WPFPresets {
.PARAMETER imported
If the preset is imported from a file, defaults to false
- .PARAMETER checkbox
- The checkbox to set the options to, defaults to 'WPFTweaks'
+ .PARAMETER checkboxfilterpattern
+ The Pattern to use when filtering through CheckBoxes, defaults to "**"
#>
- param(
- $preset,
- [bool]$imported = $false
+ param (
+ [Parameter(position=0)]
+ [string]$preset = "",
+
+ [Parameter(position=1)]
+ [bool]$imported = $false,
+
+ [Parameter(position=2)]
+ [string]$checkboxfilterpattern = "**"
)
- if($imported -eq $true) {
+ if ($imported -eq $true) {
$CheckBoxesToCheck = $preset
} else {
$CheckBoxesToCheck = $sync.configs.preset.$preset
}
- $CheckBoxes = $sync.GetEnumerator() | Where-Object { $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" }
- Write-Debug "Getting checkboxes to set $($CheckBoxes.Count)"
+ $CheckBoxes = ($sync.GetEnumerator()).where{ $_.Value -is [System.Windows.Controls.CheckBox] -and $_.Name -notlike "WPFToggle*" -and $_.Name -like "$checkboxfilterpattern"}
+ Write-Debug "Getting checkboxes to set, number of checkboxes: $($CheckBoxes.Count)"
- $CheckBoxesToCheck | ForEach-Object {
- if ($_ -ne $null) {
- Write-Debug $_
- }
+ if ($CheckBoxesToCheck -ne "") {
+ $debugMsg = "CheckBoxes to Check are: "
+ $CheckBoxesToCheck | ForEach-Object { $debugMsg += "$_, " }
+ $debugMsg = $debugMsg -replace (',\s*$', '')
+ Write-Debug "$debugMsg"
}
foreach ($CheckBox in $CheckBoxes) {
diff --git a/functions/public/Invoke-WPFToggle.ps1 b/functions/public/Invoke-WPFToggle.ps1
index d0b79da1..c4fbd105 100644
--- a/functions/public/Invoke-WPFToggle.ps1
+++ b/functions/public/Invoke-WPFToggle.ps1
@@ -15,23 +15,25 @@ function Invoke-WPFToggle {
# Use this to get the name of the button
#[System.Windows.MessageBox]::Show("$Button","Chris Titus Tech's Windows Utility","OK","Info")
+ $ToggleStatus = (Get-WinUtilToggleStatus $Button)
+
Switch -Wildcard ($Button) {
- "WPFToggleDarkMode" {Invoke-WinUtilDarkMode -DarkMoveEnabled $(Get-WinUtilToggleStatus WPFToggleDarkMode)}
- "WPFToggleBingSearch" {Invoke-WinUtilBingSearch $(Get-WinUtilToggleStatus WPFToggleBingSearch)}
- "WPFToggleNumLock" {Invoke-WinUtilNumLock $(Get-WinUtilToggleStatus WPFToggleNumLock)}
- "WPFToggleVerboseLogon" {Invoke-WinUtilVerboseLogon $(Get-WinUtilToggleStatus WPFToggleVerboseLogon)}
- "WPFToggleShowExt" {Invoke-WinUtilShowExt $(Get-WinUtilToggleStatus WPFToggleShowExt)}
- "WPFToggleSnapWindow" {Invoke-WinUtilSnapWindow $(Get-WinUtilToggleStatus WPFToggleSnapWindow)}
- "WPFToggleSnapFlyout" {Invoke-WinUtilSnapFlyout $(Get-WinUtilToggleStatus WPFToggleSnapFlyout)}
- "WPFToggleSnapSuggestion" {Invoke-WinUtilSnapSuggestion $(Get-WinUtilToggleStatus WPFToggleSnapSuggestion)}
- "WPFToggleMouseAcceleration" {Invoke-WinUtilMouseAcceleration $(Get-WinUtilToggleStatus WPFToggleMouseAcceleration)}
- "WPFToggleStickyKeys" {Invoke-WinUtilStickyKeys $(Get-WinUtilToggleStatus WPFToggleStickyKeys)}
- "WPFToggleTaskbarWidgets" {Invoke-WinUtilTaskbarWidgets $(Get-WinUtilToggleStatus WPFToggleTaskbarWidgets)}
- "WPFToggleTaskbarSearch" {Invoke-WinUtilTaskbarSearch $(Get-WinUtilToggleStatus WPFToggleTaskbarSearch)}
- "WPFToggleTaskView" {Invoke-WinUtilTaskView $(Get-WinUtilToggleStatus WPFToggleTaskView)}
- "WPFToggleHiddenFiles" {Invoke-WinUtilHiddenFiles $(Get-WinUtilToggleStatus WPFToggleHiddenFiles)}
- "WPFToggleTaskbarAlignment" {Invoke-WinUtilTaskbarAlignment $(Get-WinUtilToggleStatus WPFToggleTaskbarAlignment)}
- "WPFToggleDetailedBSoD" {Invoke-WinUtilDetailedBSoD $(Get-WinUtilToggleStatus WPFToggleDetailedBSoD)}
+ "WPFToggleDarkMode" {Invoke-WinUtilDarkMode $ToggleStatus}
+ "WPFToggleBingSearch" {Invoke-WinUtilBingSearch $ToggleStatus}
+ "WPFToggleNumLock" {Invoke-WinUtilNumLock $ToggleStatus}
+ "WPFToggleVerboseLogon" {Invoke-WinUtilVerboseLogon $ToggleStatus}
+ "WPFToggleShowExt" {Invoke-WinUtilShowExt $ToggleStatus}
+ "WPFToggleSnapWindow" {Invoke-WinUtilSnapWindow $ToggleStatus}
+ "WPFToggleSnapFlyout" {Invoke-WinUtilSnapFlyout $ToggleStatus}
+ "WPFToggleSnapSuggestion" {Invoke-WinUtilSnapSuggestion $ToggleStatus}
+ "WPFToggleMouseAcceleration" {Invoke-WinUtilMouseAcceleration $ToggleStatus}
+ "WPFToggleStickyKeys" {Invoke-WinUtilStickyKeys $ToggleStatus}
+ "WPFToggleTaskbarWidgets" {Invoke-WinUtilTaskbarWidgets $ToggleStatus}
+ "WPFToggleTaskbarSearch" {Invoke-WinUtilTaskbarSearch $ToggleStatus}
+ "WPFToggleTaskView" {Invoke-WinUtilTaskView $ToggleStatus}
+ "WPFToggleHiddenFiles" {Invoke-WinUtilHiddenFiles $ToggleStatus}
+ "WPFToggleTaskbarAlignment" {Invoke-WinUtilTaskbarAlignment $ToggleStatus}
+ "WPFToggleDetailedBSoD" {Invoke-WinUtilDetailedBSoD $ToggleStatus}
}
}
diff --git a/functions/public/Invoke-WPFUIElements.ps1 b/functions/public/Invoke-WPFUIElements.ps1
new file mode 100644
index 00000000..f8e9c7ba
--- /dev/null
+++ b/functions/public/Invoke-WPFUIElements.ps1
@@ -0,0 +1,326 @@
+function Invoke-WPFUIElements {
+ <#
+ .SYNOPSIS
+ Adds UI elements to a specified Grid in the WinUtil GUI based on a JSON configuration.
+ .PARAMETER configVariable
+ The variable/link containing the JSON configuration.
+ .PARAMETER targetGridName
+ The name of the grid to which the UI elements should be added.
+ .PARAMETER columncount
+ The number of columns to be used in the Grid. If not provided, a default value is used based on the panel.
+ .EXAMPLE
+ Invoke-WPFUIElements -configVariable $sync.configs.applications -targetGridName "install" -columncount 5
+ .NOTES
+ Future me/contributer: If possible please wrap this into a runspace to make it load all panels at the same time.
+ #>
+
+ param(
+ [Parameter(Mandatory, position=0)]
+ [PSCustomObject]$configVariable,
+
+ [Parameter(Mandatory, position=1)]
+ [string]$targetGridName,
+
+ [Parameter(Mandatory, position=2)]
+ [int]$columncount
+ )
+
+ $window = $sync["Form"]
+
+ $theme = $sync.configs.themes.$ctttheme
+ $borderstyle = $window.FindResource("BorderStyle")
+ $HoverTextBlockStyle = $window.FindResource("HoverTextBlockStyle")
+ $ColorfulToggleSwitchStyle = $window.FindResource("ColorfulToggleSwitchStyle")
+
+ if (!$borderstyle -or !$HoverTextBlockStyle -or !$ColorfulToggleSwitchStyle) {
+ throw "Failed to retrieve Styles using 'FindResource' from main window element."
+ }
+
+ $targetGrid = $window.FindName($targetGridName)
+
+ if (!$targetGrid) {
+ throw "Failed to retrieve Target Grid by name, provided name: $targetGrid"
+ }
+
+ # Clear existing ColumnDefinitions and Children
+ $targetGrid.ColumnDefinitions.Clear() | Out-Null
+ $targetGrid.Children.Clear() | Out-Null
+
+ # Add ColumnDefinitions to the target Grid
+ for ($i = 0; $i -lt $columncount; $i++) {
+ $colDef = New-Object Windows.Controls.ColumnDefinition
+ $colDef.Width = New-Object Windows.GridLength(1, [Windows.GridUnitType]::Star)
+ $targetGrid.ColumnDefinitions.Add($colDef) | Out-Null
+ }
+
+ # Convert PSCustomObject to Hashtable
+ $configHashtable = @{}
+ $configVariable.PSObject.Properties.Name | ForEach-Object {
+ $configHashtable[$_] = $configVariable.$_
+ }
+
+ $organizedData = @{}
+ # Iterate through JSON data and organize by panel and category
+ foreach ($entry in $configHashtable.Keys) {
+ $entryInfo = $configHashtable[$entry]
+
+ # Create an object for the application
+ $entryObject = [PSCustomObject]@{
+ Name = $entry
+ Order = $entryInfo.order
+ Category = $entryInfo.Category
+ Content = $entryInfo.Content
+ Choco = $entryInfo.choco
+ Winget = $entryInfo.winget
+ Panel = if ($entryInfo.Panel) { $entryInfo.Panel } else { "0" }
+ Link = $entryInfo.link
+ Description = $entryInfo.description
+ Type = $entryInfo.type
+ ComboItems = $entryInfo.ComboItems
+ Checked = $entryInfo.Checked
+ ButtonWidth = $entryInfo.ButtonWidth
+ }
+
+ if (-not $organizedData.ContainsKey($entryObject.Panel)) {
+ $organizedData[$entryObject.Panel] = @{}
+ }
+
+ if (-not $organizedData[$entryObject.Panel].ContainsKey($entryObject.Category)) {
+ $organizedData[$entryObject.Panel][$entryObject.Category] = @()
+ }
+
+ # Store application data in an array under the category
+ $organizedData[$entryObject.Panel][$entryObject.Category] += $entryObject
+
+ # Only apply the logic for distributing entries across columns if the targetGridName is "appspanel"
+ if ($targetGridName -eq "appspanel") {
+ $panelcount = 0
+ $entrycount = $configHashtable.Keys.Count + $organizedData["0"].Keys.Count
+ $maxcount = [Math]::Round($entrycount / $columncount + 0.5)
+ }
+ }
+
+ # Iterate through 'organizedData' by panel, category, and application
+ $count = 0
+ foreach ($panelKey in ($organizedData.Keys | Sort-Object)) {
+ # Create a Border for each column
+ $border = New-Object Windows.Controls.Border
+ $border.VerticalAlignment = "Stretch" # Ensure the border stretches vertically
+ [System.Windows.Controls.Grid]::SetColumn($border, $panelcount)
+ $border.style = $borderstyle
+ $targetGrid.Children.Add($border) | Out-Null
+
+ # Create a StackPanel inside the Border
+ $stackPanel = New-Object Windows.Controls.StackPanel
+ $stackPanel.Background = [Windows.Media.Brushes]::Transparent
+ $stackPanel.SnapsToDevicePixels = $true
+ $stackPanel.VerticalAlignment = "Stretch" # Ensure the stack panel stretches vertically
+ $border.Child = $stackPanel
+ $panelcount++
+
+ foreach ($category in ($organizedData[$panelKey].Keys | Sort-Object)) {
+ $count++
+ if ($targetGridName -eq "appspanel" -and $columncount -gt 0) {
+ $panelcount2 = [Int](($count) / $maxcount - 0.5)
+ if ($panelcount -eq $panelcount2) {
+ # Create a new Border for the new column
+ $border = New-Object Windows.Controls.Border
+ $border.VerticalAlignment = "Stretch" # Ensure the border stretches vertically
+ [System.Windows.Controls.Grid]::SetColumn($border, $panelcount)
+ $border.style = $borderstyle
+ $targetGrid.Children.Add($border) | Out-Null
+
+ # Create a new StackPanel inside the Border
+ $stackPanel = New-Object Windows.Controls.StackPanel
+ $stackPanel.Background = [Windows.Media.Brushes]::Transparent
+ $stackPanel.SnapsToDevicePixels = $true
+ $stackPanel.VerticalAlignment = "Stretch" # Ensure the stack panel stretches vertically
+ $border.Child = $stackPanel
+ $panelcount++
+ }
+ }
+
+ $label = New-Object Windows.Controls.Label
+ $label.Content = $category -replace ".*__", ""
+ $label.FontSize = $theme.FontSizeHeading
+ $label.FontFamily = $theme.HeaderFontFamily
+ $stackPanel.Children.Add($label) | Out-Null
+
+ $sync[$category] = $label
+
+ # Sort entries by Order and then by Name, but only display Name
+ $entries = $organizedData[$panelKey][$category] | Sort-Object Order, Name
+ foreach ($entryInfo in $entries) {
+ $count++
+ if ($targetGridName -eq "appspanel" -and $columncount -gt 0) {
+ $panelcount2 = [Int](($count) / $maxcount - 0.5)
+ if ($panelcount -eq $panelcount2) {
+ # Create a new Border for the new column
+ $border = New-Object Windows.Controls.Border
+ $border.VerticalAlignment = "Stretch" # Ensure the border stretches vertically
+ [System.Windows.Controls.Grid]::SetColumn($border, $panelcount)
+ $border.style = $borderstyle
+ $targetGrid.Children.Add($border) | Out-Null
+
+ # Create a new StackPanel inside the Border
+ $stackPanel = New-Object Windows.Controls.StackPanel
+ $stackPanel.Background = [Windows.Media.Brushes]::Transparent
+ $stackPanel.SnapsToDevicePixels = $true
+ $stackPanel.VerticalAlignment = "Stretch" # Ensure the stack panel stretches vertically
+ $border.Child = $stackPanel
+ $panelcount++
+ }
+ }
+
+ switch ($entryInfo.Type) {
+ "Toggle" {
+ $dockPanel = New-Object Windows.Controls.DockPanel
+ $checkBox = New-Object Windows.Controls.CheckBox
+ $checkBox.Name = $entryInfo.Name
+ $checkBox.HorizontalAlignment = "Right"
+ $dockPanel.Children.Add($checkBox) | Out-Null
+ $checkBox.Style = $ColorfulToggleSwitchStyle
+
+ $label = New-Object Windows.Controls.Label
+ $label.Content = $entryInfo.Content
+ $label.ToolTip = $entryInfo.Description
+ $label.HorizontalAlignment = "Left"
+ $label.FontSize = $theme.FontSize
+ $label.Foreground = $theme.MainForegroundColor
+ $dockPanel.Children.Add($label) | Out-Null
+ $stackPanel.Children.Add($dockPanel) | Out-Null
+
+ $sync[$entryInfo.Name] = $checkBox
+
+ $sync[$entryInfo.Name].IsChecked = Get-WinUtilToggleStatus $sync[$entryInfo.Name].Name
+
+ $sync[$entryInfo.Name].Add_Click({
+ [System.Object]$Sender = $args[0]
+ Invoke-WPFToggle $Sender.name
+ })
+ }
+
+ "ToggleButton" {
+ $toggleButton = New-Object Windows.Controls.ToggleButton
+ $toggleButton.Name = $entryInfo.Name
+ $toggleButton.Name = "WPFTab" + ($stackPanel.Children.Count + 1) + "BT"
+ $toggleButton.HorizontalAlignment = "Left"
+ $toggleButton.Height = $theme.TabButtonHeight
+ $toggleButton.Width = $theme.TabButtonWidth
+ $toggleButton.Background = $theme.ButtonInstallBackgroundColor
+ $toggleButton.Foreground = [Windows.Media.Brushes]::White
+ $toggleButton.FontWeight = [Windows.FontWeights]::Bold
+
+ $textBlock = New-Object Windows.Controls.TextBlock
+ $textBlock.FontSize = $theme.TabButtonFontSize
+ $textBlock.Background = [Windows.Media.Brushes]::Transparent
+ $textBlock.Foreground = $theme.ButtonInstallForegroundColor
+
+ $underline = New-Object Windows.Documents.Underline
+ $underline.Inlines.Add($entryInfo.name -replace "(.).*", "`$1")
+
+ $run = New-Object Windows.Documents.Run
+ $run.Text = $entryInfo.name -replace "^.", ""
+
+ $textBlock.Inlines.Add($underline)
+ $textBlock.Inlines.Add($run)
+
+ $toggleButton.Content = $textBlock
+
+ $stackPanel.Children.Add($toggleButton) | Out-Null
+
+ $sync[$entryInfo.Name] = $toggleButton
+ }
+
+ "Combobox" {
+ $horizontalStackPanel = New-Object Windows.Controls.StackPanel
+ $horizontalStackPanel.Orientation = "Horizontal"
+ $horizontalStackPanel.Margin = "0,5,0,0"
+
+ $label = New-Object Windows.Controls.Label
+ $label.Content = $entryInfo.Content
+ $label.HorizontalAlignment = "Left"
+ $label.VerticalAlignment = "Center"
+ $label.FontSize = $theme.ButtonFontSize
+ $horizontalStackPanel.Children.Add($label) | Out-Null
+
+ $comboBox = New-Object Windows.Controls.ComboBox
+ $comboBox.Name = $entryInfo.Name
+ $comboBox.Height = $theme.ButtonHeight
+ $comboBox.Width = $theme.ButtonWidth
+ $comboBox.HorizontalAlignment = "Left"
+ $comboBox.VerticalAlignment = "Center"
+ $comboBox.Margin = $theme.ButtonMargin
+
+ foreach ($comboitem in ($entryInfo.ComboItems -split " ")) {
+ $comboBoxItem = New-Object Windows.Controls.ComboBoxItem
+ $comboBoxItem.Content = $comboitem
+ $comboBoxItem.FontSize = $theme.ButtonFontSize
+ $comboBox.Items.Add($comboBoxItem) | Out-Null
+ }
+
+ $horizontalStackPanel.Children.Add($comboBox) | Out-Null
+ $stackPanel.Children.Add($horizontalStackPanel) | Out-Null
+
+ $comboBox.SelectedIndex = 0
+
+ $sync[$entryInfo.Name] = $comboBox
+ }
+
+ "Button" {
+ $button = New-Object Windows.Controls.Button
+ $button.Name = $entryInfo.Name
+ $button.Content = $entryInfo.Content
+ $button.HorizontalAlignment = "Left"
+ $button.Margin = $theme.ButtonMargin
+ $button.FontSize = $theme.ButtonFontSize
+ if ($entryInfo.ButtonWidth) {
+ $button.Width = $entryInfo.ButtonWidth
+ }
+ $stackPanel.Children.Add($button) | Out-Null
+
+ $sync[$entryInfo.Name] = $button
+ }
+
+ default {
+ $horizontalStackPanel = New-Object Windows.Controls.StackPanel
+ $horizontalStackPanel.Orientation = "Horizontal"
+
+ $checkBox = New-Object Windows.Controls.CheckBox
+ $checkBox.Name = $entryInfo.Name
+ $checkBox.Content = $entryInfo.Content
+ $checkBox.FontSize = $theme.FontSize
+ $checkBox.ToolTip = $entryInfo.Description
+ $checkBox.Margin = $theme.CheckBoxMargin
+ if ($entryInfo.Checked) {
+ $checkBox.IsChecked = $entryInfo.Checked
+ }
+ $horizontalStackPanel.Children.Add($checkBox) | Out-Null
+
+ if ($entryInfo.Link) {
+ $textBlock = New-Object Windows.Controls.TextBlock
+ $textBlock.Name = $checkBox.Name + "Link"
+ $textBlock.Text = "(?)"
+ $textBlock.ToolTip = $entryInfo.Link
+ $textBlock.Style = $HoverTextBlockStyle
+
+ # Add event handler for click to open link
+ $handler = [System.Windows.Input.MouseButtonEventHandler]{
+ param($sender, $e)
+ Start-Process $sender.ToolTip.ToString()
+ }
+ $textBlock.AddHandler([Windows.Controls.TextBlock]::MouseLeftButtonUpEvent, $handler)
+
+ $horizontalStackPanel.Children.Add($textBlock) | Out-Null
+
+ $sync[$textBlock.Name] = $textBlock
+ }
+
+ $stackPanel.Children.Add($horizontalStackPanel) | Out-Null
+ $sync[$entryInfo.Name] = $checkBox
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/overrides/main.html b/overrides/main.html
index f5f0eb39..bf27a2c6 100644
--- a/overrides/main.html
+++ b/overrides/main.html
@@ -1,5 +1,12 @@
{% extends "base.html" %}
+{% block header %}
+ {{ super() }}
+
+ Announcement: We are currently not adding any applications to WinUtil and any apps that will be added through a PR will be declined by the maintainer.
+
+{% endblock %}
+
{% block footer %}
{# Empty block to override the footer #}
{% endblock %}
diff --git a/pester/configs.Tests.ps1 b/pester/configs.Tests.ps1
index dd2db7f3..f8e55c44 100644
--- a/pester/configs.Tests.ps1
+++ b/pester/configs.Tests.ps1
@@ -37,6 +37,12 @@ Describe "Config Files" -ForEach @(
$result = New-Object System.Collections.Generic.List[System.Object]
Foreach ($application in $applications) {
$compare = $global:importedconfigs.$name.$application | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name
+ if (-not $compare) {
+ throw "Comparison object for application '$application' is null."
+ }
+ if (-not $template) {
+ throw "Template object for application '$application' is null."
+ }
if ($(Compare-Object $compare $template) -ne $null) {
$result.Add($application)
}
diff --git a/scripts/main.ps1 b/scripts/main.ps1
index 1b299456..5bbff0ee 100644
--- a/scripts/main.ps1
+++ b/scripts/main.ps1
@@ -52,6 +52,7 @@ $sync.runspace.Open()
$inputXML = $inputXML -replace 'mc:Ignorable="d"', '' -replace "x:N", 'N' -replace '^
+
param (
[switch]$Debug,
[string]$Config,
@@ -27,12 +28,6 @@ if ($Run) {
$PARAM_RUN = $true
}
-if (!(Test-Path -Path $ENV:TEMP)) {
- New-Item -ItemType Directory -Force -Path $ENV:TEMP
-}
-
-Start-Transcript $ENV:TEMP\Winutil.log -Append
-
# Load DLLs
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms
@@ -44,15 +39,38 @@ $sync.version = "#{replaceme}"
$sync.configs = @{}
$sync.ProcessRunning = $false
-# If script isn't running as admin, show error message and quit
-If (([Security.Principal.WindowsIdentity]::GetCurrent()).Owner.Value -ne "S-1-5-32-544") {
- Write-Host "===========================================" -Foregroundcolor Red
- Write-Host "-- Scripts must be run as Administrator ---" -Foregroundcolor Red
- Write-Host "-- Right-Click Start -> Terminal(Admin) ---" -Foregroundcolor Red
- Write-Host "===========================================" -Foregroundcolor Red
+if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
+ Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch."
+ $argList = @()
+
+ $PSBoundParameters.GetEnumerator() | ForEach-Object {
+ $argList += if ($_.Value -is [switch] -and $_.Value) {
+ "-$($_.Key)"
+ } elseif ($_.Value) {
+ "-$($_.Key) `"$($_.Value)`""
+ }
+ }
+
+ $script = if ($MyInvocation.MyCommand.Path) {
+ "& { & '$($MyInvocation.MyCommand.Path)' $argList }"
+ } else {
+ "iex '& { $(irm https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1) } $argList'"
+ }
+
+ $powershellcmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" }
+ $processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $powershellcmd }
+
+ Start-Process $processCmd -ArgumentList "$powershellcmd -ExecutionPolicy Bypass -NoProfile -Command $script" -Verb RunAs
+
break
}
+$dateTime = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
+
+$logdir = "$env:localappdata\winutil\logs"
+[System.IO.Directory]::CreateDirectory("$logdir") | Out-Null
+Start-Transcript -Path "$logdir\winutil_$dateTime.log" -Append -NoClobber | Out-Null
+
# Set PowerShell window title
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Admin)"
clear-host
diff --git a/windev.ps1 b/windev.ps1
index 3e1805f0..b735efca 100644
--- a/windev.ps1
+++ b/windev.ps1
@@ -12,6 +12,25 @@
Run in Admin Powershell > ./windev.ps1
#>
+if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
+ Write-Output "Winutil needs to be run as Administrator. Attempting to relaunch."
+ # Capture all the arguments passed to the script
+ $argList = $args -join ' '
+
+ $script = if ($MyInvocation.MyCommand.Path) {
+ "& { & '$($MyInvocation.MyCommand.Path)' $argList }"
+ } else {
+ "iex '& { $(irm https://github.com/ChrisTitusTech/winutil/raw/main/windev.ps1) } $argList'"
+ }
+
+ $powershellcmd = if (Get-Command pwsh -ErrorAction SilentlyContinue) { "pwsh" } else { "powershell" }
+ $processCmd = if (Get-Command wt.exe -ErrorAction SilentlyContinue) { "wt.exe" } else { $powershellcmd }
+
+ Start-Process $processCmd -ArgumentList "$powershellcmd -ExecutionPolicy Bypass -NoProfile -Command $script" -Verb RunAs
+
+ break
+}
+
# Function to fetch the latest release tag from the GitHub API
function Get-LatestRelease {
try {
@@ -34,9 +53,9 @@ function RedirectToLatestPreRelease {
Write-Host "Using latest Full Release"
$url = "https://github.com/ChrisTitusTech/winutil/releases/latest/download/winutil.ps1"
}
- Invoke-RestMethod $url | Invoke-Expression
+
+ iex "& { $(irm $url) } $argList"
}
# Call the redirect function
-
RedirectToLatestPreRelease
diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml
index cdbf4ccc..17115614 100644
--- a/xaml/inputXML.xaml
+++ b/xaml/inputXML.xaml
@@ -9,7 +9,11 @@
WindowStartupLocation="CenterScreen"
UseLayoutRounding="True"
WindowStyle="None"
- Title="Chris Titus Tech's Windows Utility" Height="800" Width="1280">
+ Width="Auto"
+ Height="Auto"
+ MaxWidth="1280"
+ MaxHeight="800"
+ Title="Chris Titus Tech's Windows Utility">
@@ -284,12 +288,12 @@
-
+
@@ -305,17 +309,17 @@
-
+
@@ -468,7 +472,11 @@
Width="34" Height="17">
+ HorizontalAlignment="Left" Width="10.8"
+ RenderTransformOrigin="0.5, 0.5">
+
+
+
@@ -479,12 +487,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
@@ -543,7 +574,7 @@
-