diff --git a/README.md b/README.md index 2040acde..c93e6a43 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,16 @@ Winutil must be run in Admin mode because it performs system-wide tweaks. To ach #### Simple way -``` -iwr -useb https://christitus.com/win | iex -``` -or by executing: ``` irm https://christitus.com/win | iex ``` Courtesy of the issue raised at: [#144](/../../issues/144) +or by executing: +``` +iwr -useb https://christitus.com/win | iex +``` + if for some reason this site is not reachable from your country please try running it directly from github ``` @@ -97,7 +98,9 @@ If you are still having issues try changing your DNS provider to 1.1.1.1 || 1.0. - Essential Tweaks: Offers a collection of essential tweaks aimed at improving system performance, privacy, and resource utilization. These tweaks include creating a system restore point, disabling telemetry, Wi-Fi Sense, setting services to manual, disabling location tracking, and HomeGroup, among others. - - Misc. Tweaks: Encompasses a range of various tweaks to further optimize the system. These tweaks include enabling/disabling power throttling, enabling num lock on startup, removing Cortana and Edge, disabling User Account Control (UAC), notification panel, and configuring TPM during updates, among others. + - Advanced Tweaks: Encompasses a range of various advanced power user tweaks to further optimize the system. These tweaks include removing OneDrive and Edge, disabling User Account Control (UAC), notification panel, among others. + + - Toggles: Adds easy to use, one click shortcuts for toggling dark mode, NumLock on startup, file extensions, sticky keys, among others. - Additional Tweaks: Introduces various other tweaks such as enabling dark mode, changing DNS settings, adding an Ultimate Performance mode, and creating shortcuts for WinUtil tools. These tweaks provide users with additional customization options to tailor their system to their preferences. diff --git a/config/tweaks.json b/config/tweaks.json index dcf08885..68dea51b 100644 --- a/config/tweaks.json +++ b/config/tweaks.json @@ -2527,6 +2527,15 @@ "category": "z__Advanced Tweaks - CAUTION", "panel": "1", "Order": "a031_", + "registry": [ + { + "Path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", + "Name": "DisabledComponents", + "Value": "255", + "OriginalValue": "0", + "Type": "DWord" + } + ], "InvokeScript": [ "Disable-NetAdapterBinding -Name \"*\" -ComponentID ms_tcpip6" ], @@ -2540,6 +2549,15 @@ "category": "z__Advanced Tweaks - CAUTION", "panel": "1", "Order": "a030_", + "registry": [ + { + "Path": "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters", + "Name": "DisabledComponents", + "Value": "0", + "OriginalValue": "0", + "Type": "DWord" + } + ], "InvokeScript": [ "Enable-NetAdapterBinding -Name \"*\" -ComponentID ms_tcpip6" ], diff --git a/functions/private/Get-Oscdimg.ps1 b/functions/private/Get-Oscdimg.ps1 index 29634955..47a2eadd 100644 --- a/functions/private/Get-Oscdimg.ps1 +++ b/functions/private/Get-Oscdimg.ps1 @@ -2,7 +2,7 @@ function Get-Oscdimg { <# .DESCRIPTION - This function will get oscdimg file for from github Release foldersand put it into env:temp + This function will download oscdimg file from github Release folders and put it into env:temp folder .EXAMPLE Get-Oscdimg diff --git a/functions/private/Get-WinUtilWingetLatest.ps1 b/functions/private/Get-WinUtilWingetLatest.ps1 new file mode 100644 index 00000000..5a18a536 --- /dev/null +++ b/functions/private/Get-WinUtilWingetLatest.ps1 @@ -0,0 +1,23 @@ +function Get-WinUtilWingetLatest { + <# + .SYNOPSIS + Uses GitHub API to check for the latest release of Winget. + .DESCRIPTION + This function grabs the latest version of Winget and returns the download path to Install-WinUtilWinget for installation. + #> + + Try{ + # Grabs the latest release of Winget from the Github API for the install process. + $response = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/Winget-cli/releases/latest" -Method Get -ErrorAction Stop + $latestVersion = $response.tag_name #Stores version number of latest release. + $licenseWingetUrl = $response.assets.browser_download_url[0] #Index value for License file. + Write-Host "Latest Version:`t$($latestVersion)`n" + $assetUrl = $response.assets.browser_download_url[2] #Index value for download URL. + Invoke-WebRequest -Uri $licenseWingetUrl -OutFile $ENV:TEMP\License1.xml + # The only pain is that the msixbundle for winget-cli is 246MB. In some situations this can take a bit, with slower connections. + Invoke-WebRequest -Uri $assetUrl -OutFile $ENV:TEMP\Microsoft.DesktopAppInstaller.msixbundle + } + Catch{ + throw [WingetFailedInstall]::new('Failed to get latest Winget release and license') + } +} diff --git a/functions/private/Get-WinUtilWingetPrerequisites.ps1 b/functions/private/Get-WinUtilWingetPrerequisites.ps1 new file mode 100644 index 00000000..4949d191 --- /dev/null +++ b/functions/private/Get-WinUtilWingetPrerequisites.ps1 @@ -0,0 +1,29 @@ +function Get-WinUtilWingetPrerequisites { + <# + .SYNOPSIS + Downloads the Winget Prereqs. + .DESCRIPTION + Downloads Prereqs for Winget. Version numbers are coded as variables and can be updated as uncommonly as Microsoft updates the prereqs. + #> + + # I don't know of a way to detect the prereqs automatically, so if someone has a better way of defining these, that would be great. + # Microsoft.VCLibs version rarely changes, but for future compatibility I made it a variable. + $versionVCLibs = "14.00" + $fileVCLibs = "https://aka.ms/Microsoft.VCLibs.x64.${versionVCLibs}.Desktop.appx" + # Write-Host "$fileVCLibs" + # Microsoft.UI.Xaml version changed recently, so I made the version numbers variables. + $versionUIXamlMinor = "2.8" + $versionUIXamlPatch = "2.8.6" + $fileUIXaml = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v${versionUIXamlPatch}/Microsoft.UI.Xaml.${versionUIXamlMinor}.x64.appx" + # Write-Host "$fileUIXaml" + + Try{ + Write-Host "Downloading Microsoft.VCLibs Dependency..." + Invoke-WebRequest -Uri $fileVCLibs -OutFile $ENV:TEMP\Microsoft.VCLibs.x64.Desktop.appx + Write-Host "Downloading Microsoft.UI.Xaml Dependency...`n" + Invoke-WebRequest -Uri $fileUIXaml -OutFile $ENV:TEMP\Microsoft.UI.Xaml.x64.appx + } + Catch{ + throw [WingetFailedInstall]::new('Failed to install prerequsites') + } +} diff --git a/functions/private/Install-WinUtilProgramWinget.ps1 b/functions/private/Install-WinUtilProgramWinget.ps1 index 24d3b266..7bfedd93 100644 --- a/functions/private/Install-WinUtilProgramWinget.ps1 +++ b/functions/private/Install-WinUtilProgramWinget.ps1 @@ -30,7 +30,10 @@ Function Install-WinUtilProgramWinget { Write-Progress -Activity "$manage Applications" -Status "$manage $Program $($x + 1) of $count" -PercentComplete $($x/$count*100) if($manage -eq "Installing"){ - Start-Process -FilePath winget -ArgumentList "install -e --accept-source-agreements --accept-package-agreements --scope=machine --silent $Program" -NoNewWindow -Wait + # --scope=machine when installing non-UWP apps with winget fails with error code 0x80070005. + # Removed argument while testing new Winget install method. + # Open issue on winget-cli github repo: https://github.com/microsoft/winget-cli/issues/3936 + Start-Process -FilePath winget -ArgumentList "install -e --accept-source-agreements --accept-package-agreements --silent $Program" -NoNewWindow -Wait } if($manage -eq "Uninstalling"){ Start-Process -FilePath winget -ArgumentList "uninstall -e --accept-source-agreements --purge --force --silent $Program" -NoNewWindow -Wait diff --git a/functions/private/Install-WinUtilWinget.ps1 b/functions/private/Install-WinUtilWinget.ps1 index a334bb61..a1da1f48 100644 --- a/functions/private/Install-WinUtilWinget.ps1 +++ b/functions/private/Install-WinUtilWinget.ps1 @@ -1,26 +1,16 @@ -function Get-LatestHash { - $shaUrl = ((Invoke-WebRequest $apiLatestUrl -UseBasicParsing | ConvertFrom-Json).assets | Where-Object { $_.name -match '^Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt$' }).browser_download_url - - $shaFile = Join-Path -Path $tempFolder -ChildPath 'Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.txt' - $WebClient.DownloadFile($shaUrl, $shaFile) - - Get-Content $shaFile -} - function Install-WinUtilWinget { - <# .SYNOPSIS - Installs Winget if it is not already installed + Installs Winget if it is not already installed. .DESCRIPTION - This function will download the latest version of winget and install it. If winget is already installed, it will do nothing. + This function will download the latest version of Winget and install it. If Winget is already installed, it will do nothing. #> Try{ Write-Host "Checking if Winget is Installed..." - if (Test-WinUtilPackageManager -winget) { - # Checks if winget executable exists and if the Windows Version is 1809 or higher + if (Test-WinUtilPackageManager -Winget) { + # Checks if Winget executable exists and if the Windows Version is 1809 or higher Write-Host "Winget Already Installed" return } @@ -34,17 +24,35 @@ function Install-WinUtilWinget { } if (($ComputerInfo.WindowsVersion) -lt "1809") { - # Checks if Windows Version is too old for winget + # Checks if Windows Version is too old for Winget Write-Host "Winget is not supported on this version of Windows (Pre-1809)" return } - Write-Host "Running Alternative Installer and Direct Installing" - Start-Process -Verb runas -FilePath powershell.exe -ArgumentList "choco install winget" - - Write-Host "Winget Installed" + if((Get-Command -Name choco -ErrorAction Ignore)) { + # Checks if Chocolatey is present (In case it didn't install properly), and installs Winget with choco, if so. + Write-Host "Chocolatey detected. Installing Winget via Chocolatey" + Start-Process -Verb runas -FilePath powershell.exe -ArgumentList "choco install winget-cli" + Write-Host "Winget Installed" + Write-Output "Refreshing Environment Variables...`n" + $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + } + Else { + # If Chocolatey doesn't exist, it will install Winget through more manual means. + # Used part of my own script with some modification: ruxunderscore/windows-initialization + Write-Host "Downloading Winget Prerequsites" + Get-WinUtilWingetPrerequisites + Write-Host "Downloading Winget and License File" + Get-WinUtilWingetLatest + Write-Host "Installing Winget w/ Prerequsites" + Add-AppxProvisionedPackage -Online -PackagePath $ENV:TEMP\Microsoft.DesktopAppInstaller.msixbundle -DependencyPackagePath $ENV:TEMP\Microsoft.VCLibs.x64.Desktop.appx, $ENV:TEMP\Microsoft.UI.Xaml.x64.appx -LicensePath $ENV:TEMP\License1.xml + Write-Host "Winget Installed" + # Winget only needs a refresh of the environment variables to be used. + Write-Output "Refreshing Environment Variables...`n" + $ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + } } Catch{ throw [WingetFailedInstall]::new('Failed to install') } -} \ No newline at end of file +} diff --git a/functions/private/Update-WinUtilProgramWinget.ps1 b/functions/private/Update-WinUtilProgramWinget.ps1 index 1fc0c2df..6fac6741 100644 --- a/functions/private/Update-WinUtilProgramWinget.ps1 +++ b/functions/private/Update-WinUtilProgramWinget.ps1 @@ -12,7 +12,7 @@ Function Update-WinUtilProgramWinget { $host.ui.RawUI.WindowTitle = """Winget Install""" Start-Transcript $ENV:TEMP\winget-update.log -Append - winget upgrade --all + winget upgrade --all --accept-source-agreements --accept-package-agreements --scope=machine --silent } diff --git a/winutil.ps1 b/winutil.ps1 index e4a02892..e69de29b 100644 --- a/winutil.ps1 +++ b/winutil.ps1 @@ -1,12518 +0,0 @@ - -################################################################################################################ -### ### -### WARNING: This file is automatically generated DO NOT modify this file directly as it will be overwritten ### -### ### -################################################################################################################ - -<# -.NOTES - Author : Chris Titus @christitustech - Runspace Author: @DeveloperDurp - GitHub : https://github.com/ChrisTitusTech - Version : 24.03.26 -#> -param ( - [switch]$Debug, - [string]$Config, - [switch]$Run -) - -# Set DebugPreference based on the -Debug switch -if ($Debug) { - $DebugPreference = "Continue" -} - -if ($Config) { - $PARAM_CONFIG = $Config -} - -$PARAM_RUN = $false -# Handle the -Run switch -if ($Run) { - Write-Host "Running config file tasks..." - $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 - -# Variable to sync between runspaces -$sync = [Hashtable]::Synchronized(@{}) -$sync.PSScriptRoot = $PSScriptRoot -$sync.version = "24.03.26" -$sync.configs = @{} -$sync.ProcessRunning = $false - -$currentPid = [System.Security.Principal.WindowsIdentity]::GetCurrent() -$principal = new-object System.Security.Principal.WindowsPrincipal($currentPid) -$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator - - -if ($principal.IsInRole($adminRole)) -{ - $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Admin)" - clear-host -} -else -{ - $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; - $newProcess.Arguments = $myInvocation.MyCommand.Definition; - $newProcess.Verb = "runas"; - [System.Diagnostics.Process]::Start($newProcess); - break -} -function ConvertTo-Icon { - <# - - .DESCRIPTION - This function will convert PNG to ICO file - - .EXAMPLE - ConvertTo-Icon -bitmapPath "$env:TEMP\cttlogo.png" -iconPath $iconPath - #> - param( [Parameter(Mandatory=$true)] - $bitmapPath, - $iconPath = "$env:temp\newicon.ico" - ) - - Add-Type -AssemblyName System.Drawing - - if (Test-Path $bitmapPath) { - $b = [System.Drawing.Bitmap]::FromFile($bitmapPath) - $icon = [System.Drawing.Icon]::FromHandle($b.GetHicon()) - $file = New-Object System.IO.FileStream($iconPath, 'OpenOrCreate') - $icon.Save($file) - $file.Close() - $icon.Dispose() - #explorer "/SELECT,$iconpath" - } - else { Write-Warning "$BitmapPath does not exist" } -} -function Copy-Files { - <# - - .DESCRIPTION - This function will make all modifications to the registry - - .EXAMPLE - - Set-WinUtilRegistry -Name "PublishUserActivities" -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System" -Type "DWord" -Value "0" - - #> - param ( - [string] $Path, - [string] $Destination, - [switch] $Recurse = $false, - [switch] $Force = $false - ) - - try { - - $files = Get-ChildItem -Path $path -Recurse:$recurse - Write-Host "Copy $($files.Count)(s) from $path to $destination" - - foreach($file in $files) - { - $status = "Copy files {0} on {1}: {2}" -f $counter, $files.Count, $file.Name - Write-Progress -Activity "Copy Windows files" -Status $status -PercentComplete ($counter++/$files.count*100) - $restpath = $file.FullName -Replace $path, '' - - if($file.PSIsContainer -eq $true) - { - Write-Debug "Creating $($destination + $restpath)" - New-Item ($destination+$restpath) -Force:$force -Type Directory -ErrorAction SilentlyContinue - } - else - { - Write-Debug "Copy from $($file.FullName) to $($destination+$restpath)" - Copy-Item $file.FullName ($destination+$restpath) -ErrorAction SilentlyContinue -Force:$force - Set-ItemProperty -Path ($destination+$restpath) -Name IsReadOnly -Value $false - } - } - Write-Progress -Activity "Copy Windows files" -Status "Ready" -Completed - } - Catch{ - Write-Warning "Unable to Copy all the files due to unhandled exception" - Write-Warning $psitem.Exception.StackTrace - } -} -function Get-LocalizedYesNo { - <# - .SYNOPSIS - This function runs choice.exe and captures its output to extract yes no in a localized Windows - - .DESCRIPTION - The function retrieves the output of the command 'cmd /c "choice nul"' and converts the default output for Yes and No - in the localized format, such as "Yes=, No=". - - .EXAMPLE - $yesNoArray = Get-LocalizedYesNo - Write-Host "Yes=$($yesNoArray[0]), No=$($yesNoArray[1])" - #> - - # Run choice and capture its options as output - # The output shows the options for Yes and No as "[Y,N]?" in the (partitially) localized format. - # eg. English: [Y,N]? - # Dutch: [Y,N]? - # German: [J,N]? - # French: [O,N]? - # Spanish: [S,N]? - # Italian: [S,N]? - # Russian: [Y,N]? - - $line = cmd /c "choice nul" - $charactersArray = @() - $regexPattern = '([a-zA-Z])' - $charactersArray = [regex]::Matches($line, $regexPattern) | ForEach-Object { $_.Groups[1].Value } - - Write-Debug "According to takeown.exe local Yes is $charactersArray[0]" - # Return the array of characters - return $charactersArray - } - - -function Get-LocalizedYesNoTakeown { - <# - .SYNOPSIS - This function runs takeown.exe and captures its output to extract yes no in a localized Windows - - .DESCRIPTION - The function retrieves lines from the output of takeown.exe until there are at least 2 characters - captured in a specific format, such as "Yes=, No=". - - .EXAMPLE - $yesNoArray = Get-LocalizedYesNo - Write-Host "Yes=$($yesNoArray[0]), No=$($yesNoArray[1])" - #> - - # Run takeown.exe and capture its output - $takeownOutput = & takeown.exe /? | Out-String - - # Parse the output and retrieve lines until there are at least 2 characters in the array - $found = $false - $charactersArray = @() - foreach ($line in $takeownOutput -split "`r`n") - { - # skip everything before /D flag help - if ($found) - { - # now that /D is found start looking for a single character in double quotes - # in help text there is another string in double quotes but it is not a single character - $regexPattern = '"([a-zA-Z])"' - - $charactersArray = [regex]::Matches($line, $regexPattern) | ForEach-Object { $_.Groups[1].Value } - - # if ($charactersArray.Count -gt 0) { - # Write-Output "Extracted symbols: $($matches -join ', ')" - # } else { - # Write-Output "No matches found." - # } - - if ($charactersArray.Count -ge 2) - { - break - } - } - elseif ($line -match "/D ") - { - $found = $true - } - } - - Write-Debug "According to takeown.exe local Yes is $charactersArray[0]" - # Return the array of characters - return $charactersArray - } -function Get-Oscdimg { - <# - - .DESCRIPTION - This function will get oscdimg file for from github Release foldersand put it into env:temp - - .EXAMPLE - Get-Oscdimg - #> - param( [Parameter(Mandatory=$true)] - [string]$oscdimgPath - ) - $oscdimgPath = "$env:TEMP\oscdimg.exe" - $downloadUrl = "https://github.com/ChrisTitusTech/winutil/raw/main/releases/oscdimg.exe" - Invoke-RestMethod -Uri $downloadUrl -OutFile $oscdimgPath - $hashResult = Get-FileHash -Path $oscdimgPath -Algorithm SHA256 - $sha256Hash = $hashResult.Hash - - Write-Host "[INFO] oscdimg.exe SHA-256 Hash: $sha256Hash" - - $expectedHash = "AB9E161049D293B544961BFDF2D61244ADE79376D6423DF4F60BF9B147D3C78D" # Replace with the actual expected hash - if ($sha256Hash -eq $expectedHash) { - Write-Host "Hashes match. File is verified." - } else { - Write-Host "Hashes do not match. File may be corrupted or tampered with." - } -} -function Get-TabXaml { - <# - .SYNOPSIS - Generates XAML for a tab in the WinUtil GUI - This function is used to generate the XAML for the applications tab in the WinUtil GUI - It takes the tabname and the number of columns to display the applications in as input and returns the XAML for the tab as output - .PARAMETER tabname - The name of the tab to generate XAML for - .PARAMETER columncount - The number of columns to display the applications in - .OUTPUTS - The XAML for the tab - .EXAMPLE - Get-TabXaml "applications" 3 - #> - - - param( [Parameter(Mandatory=$true)] - $tabname, - $columncount = 0 - ) - $organizedData = @{} - # Iterate through JSON data and organize by panel and category - foreach ($appName in $sync.configs.$tabname.PSObject.Properties.Name) { - $appInfo = $sync.configs.$tabname.$appName - - # Create an object for the application - $appObject = [PSCustomObject]@{ - Name = $appName - Category = $appInfo.Category - Content = $appInfo.Content - Choco = $appInfo.choco - Winget = $appInfo.winget - Panel = if ($columncount -gt 0 ) { "0" } else {$appInfo.panel} - Link = $appInfo.link - Description = $appInfo.description - # Type is (Checkbox,Toggle,Button,Combobox ) (Default is Checkbox) - Type = $appInfo.type - ComboItems = $appInfo.ComboItems - # Checked is the property to set startup checked status of checkbox (Default is false) - Checked = $appInfo.Checked - } - - if (-not $organizedData.ContainsKey($appObject.panel)) { - $organizedData[$appObject.panel] = @{} - } - - if (-not $organizedData[$appObject.panel].ContainsKey($appObject.Category)) { - $organizedData[$appObject.panel][$appObject.Category] = @{} - } - - # Store application data in a sub-array under the category - # Add Order property to keep the original order of tweaks and features - $organizedData[$appObject.panel][$appInfo.Category]["$($appInfo.order)$appName"] = $appObject - } - $panelcount=0 - $paneltotal = $organizedData.Keys.Count - if ($columncount -gt 0) { - $appcount = $sync.configs.$tabname.PSObject.Properties.Name.count + $organizedData["0"].Keys.count - $maxcount = [Math]::Round( $appcount / $columncount + 0.5) - $paneltotal = $columncount - } - # add ColumnDefinitions to evenly draw colums - $blockXml="`n"+("`n"*($paneltotal))+"`n" - # Iterate through organizedData by panel, category, and application - $count = 0 - foreach ($panel in ($organizedData.Keys | Sort-Object)) { - $blockXml += "`n`n" - $panelcount++ - foreach ($category in ($organizedData[$panel].Keys | Sort-Object)) { - $count++ - if ($columncount -gt 0) { - $panelcount2 = [Int](($count)/$maxcount-0.5) - if ($panelcount -eq $panelcount2 ) { - $blockXml +="`n`n`n" - $blockXml += "`n`n" - $panelcount++ - } - } - $blockXml += "`n`n" - $blockXml += "`n`n" - $panelcount++ - } - } - $appInfo = $organizedData[$panel][$category][$appName] - if ("Toggle" -eq $appInfo.Type) { - $blockXml += "`n`n" - } elseif ("Combobox" -eq $appInfo.Type) { - $blockXml += "`n" - # If it is a digit, type is button and button length is digits - } elseif ($appInfo.Type -match "^[\d\.]+$") { - $blockXml += " - - - - - - Choose Windows SKU - - Choose Windows features you want to remove from the ISO - - - - - - - - - - -