mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-07-03 07:08:27 +00:00
Compare commits
3 Commits
2026.06.24
...
allow-mult
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a8999f635 | ||
|
|
5bd8c9957b | ||
|
|
88b5f1b629 |
4
Run.bat
4
Run.bat
@@ -4,10 +4,10 @@ setlocal EnableDelayedExpansion
|
|||||||
:: Set Windows Terminal installation paths. (Default and Scoop installation)
|
:: Set Windows Terminal installation paths. (Default and Scoop installation)
|
||||||
set "wtDefaultPath=%LOCALAPPDATA%\Microsoft\WindowsApps\wt.exe"
|
set "wtDefaultPath=%LOCALAPPDATA%\Microsoft\WindowsApps\wt.exe"
|
||||||
set "wtScoopPath=%USERPROFILE%\scoop\apps\windows-terminal\current\wt.exe"
|
set "wtScoopPath=%USERPROFILE%\scoop\apps\windows-terminal\current\wt.exe"
|
||||||
set "logFile=%~dp0Logs\Win11Debloat-Run.log"
|
set "logFile=%LOCALAPPDATA%\Win11Debloat\Logs\Win11Debloat-Run.log"
|
||||||
|
|
||||||
:: Ensure Logs folder exists
|
:: Ensure Logs folder exists
|
||||||
if not exist "%~dp0Logs" mkdir "%~dp0Logs"
|
if not exist "%LOCALAPPDATA%\Win11Debloat\Logs" mkdir "%LOCALAPPDATA%\Win11Debloat\Logs"
|
||||||
|
|
||||||
:: Determine which terminal exists
|
:: Determine which terminal exists
|
||||||
if exist "%wtDefaultPath%" (
|
if exist "%wtDefaultPath%" (
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
|
||||||
Title="Win11Debloat"
|
Title="Win11Debloat"
|
||||||
MinWidth="860" MinHeight="640"
|
MinWidth="860" MinHeight="600"
|
||||||
ResizeMode="CanResize"
|
ResizeMode="CanResize"
|
||||||
SnapsToDevicePixels="True"
|
SnapsToDevicePixels="True"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
WindowStyle="None"
|
WindowStyle="None"
|
||||||
AllowsTransparency="False"
|
AllowsTransparency="True"
|
||||||
Background="{DynamicResource AppBorderColor}"
|
Background="Transparent"
|
||||||
Foreground="{DynamicResource AppFgColor}">
|
Foreground="{DynamicResource AppFgColor}">
|
||||||
<shell:WindowChrome.WindowChrome>
|
<shell:WindowChrome.WindowChrome>
|
||||||
<shell:WindowChrome ResizeBorderThickness="5"
|
<shell:WindowChrome ResizeBorderThickness="5"
|
||||||
@@ -464,7 +464,7 @@
|
|||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel x:Name="HomeContentPanel" HorizontalAlignment="Center" VerticalAlignment="Top">
|
<StackPanel x:Name="HomeContentPanel" HorizontalAlignment="Center" VerticalAlignment="Top">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<Viewbox Width="250" Height="250" Margin="0,0,0,16" HorizontalAlignment="Center">
|
<Viewbox Width="250" Height="250" Margin="0,0,0,24" HorizontalAlignment="Center">
|
||||||
<Grid Width="250" Height="250">
|
<Grid Width="250" Height="250">
|
||||||
<!-- Windows logo style icon -->
|
<!-- Windows logo style icon -->
|
||||||
<Path x:Name="LogoFallback" Data="M0,0 L80,0 L80,80 L0,80 Z M90,0 L170,0 L170,80 L90,80 Z M0,90 L80,90 L80,170 L0,170 Z M90,90 L170,90 L170,170 L90,170 Z"
|
<Path x:Name="LogoFallback" Data="M0,0 L80,0 L80,80 L0,80 Z M90,0 L170,0 L170,80 L90,80 Z M0,90 L80,90 L80,170 L0,170 Z M90,90 L170,90 L170,170 L90,170 Z"
|
||||||
@@ -483,7 +483,7 @@
|
|||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<TextBlock Text="Welcome to Win11Debloat" FontSize="40" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
<TextBlock Text="Welcome to Win11Debloat" FontSize="40" FontWeight="SemiBold" Foreground="{DynamicResource AppFgColor}" HorizontalAlignment="Center"/>
|
||||||
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource AppFgColor}" FontSize="20" HorizontalAlignment="Center" Margin="0,4,0,48">
|
<TextBlock TextWrapping="Wrap" Foreground="{DynamicResource AppFgColor}" FontSize="20" HorizontalAlignment="Center" Margin="0,8,0,64">
|
||||||
<Run Text="Your clean Windows experience is just a few clicks away!"/>
|
<Run Text="Your clean Windows experience is just a few clicks away!"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
|
|||||||
@@ -113,15 +113,16 @@ function ReplaceStartMenu {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
$startMenuBackupsDir = Join-Path $script:AppDataPath 'Backups'
|
||||||
$backupFileName = "Win11Debloat-StartBackup-$timestamp.bak"
|
if (-not (Test-Path $startMenuBackupsDir)) {
|
||||||
$startMenuDir = Split-Path $startMenuBinFile -Parent
|
New-Item -ItemType Directory -Path $startMenuBackupsDir -Force | Out-Null
|
||||||
$backupBinFile = Join-Path $startMenuDir $backupFileName
|
}
|
||||||
|
$backupTimestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
|
||||||
|
$backupBinFile = Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinBackup-$userName-$backupTimestamp.bak"
|
||||||
|
|
||||||
if (Test-Path $startMenuBinFile) {
|
if (Test-Path $startMenuBinFile) {
|
||||||
# Backup current start menu file
|
# Backup current start menu file
|
||||||
Copy-Item -Path $startMenuBinFile -Destination $backupBinFile -Force
|
Move-Item -Path $startMenuBinFile -Destination $backupBinFile -Force
|
||||||
Write-Verbose "Start menu backup for user $userName saved to $backupFileName"
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow
|
Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow
|
||||||
@@ -193,55 +194,6 @@ function GetStartMenuUserNameFromPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Returns the path to the latest start menu backup file for the given scope.
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
Resolves the LocalState folder for the specified scope and returns the
|
|
||||||
full path to the most recent Win11Debloat-StartBackup-*.bak file, or
|
|
||||||
$null if no backup exists.
|
|
||||||
|
|
||||||
For CurrentUser, uses $env:LOCALAPPDATA directly. For AllUsers, scans
|
|
||||||
every user profile.
|
|
||||||
|
|
||||||
.PARAMETER Scope
|
|
||||||
The scope to check: CurrentUser or AllUsers.
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
$backupPath = Get-StartMenuBackupPath -Scope 'CurrentUser'
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
$backupPath = Get-StartMenuBackupPath -Scope 'AllUsers'
|
|
||||||
#>
|
|
||||||
function Get-StartMenuBackupPath {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory)]
|
|
||||||
[ValidateSet('CurrentUser', 'AllUsers')]
|
|
||||||
[string]$Scope
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($Scope -eq 'CurrentUser') {
|
|
||||||
$localStateDir = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
|
||||||
$latestBackup = Get-ChildItem -Path (Join-Path $localStateDir 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
|
|
||||||
Sort-Object Name -Descending |
|
|
||||||
Select-Object -First 1
|
|
||||||
if ($latestBackup) { return $latestBackup.FullName }
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
|
||||||
$usersStartMenuPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
|
|
||||||
foreach ($startMenuPath in $usersStartMenuPaths) {
|
|
||||||
$latestBackup = Get-ChildItem -Path (Join-Path $startMenuPath.FullName 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
|
|
||||||
Sort-Object Name -Descending |
|
|
||||||
Select-Object -First 1
|
|
||||||
if ($latestBackup) { return $latestBackup.FullName }
|
|
||||||
}
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
@@ -257,14 +209,14 @@ function Get-StartMenuBackupPath {
|
|||||||
The full path to the user's start2.bin file to restore.
|
The full path to the user's start2.bin file to restore.
|
||||||
|
|
||||||
.PARAMETER BackupFilePath
|
.PARAMETER BackupFilePath
|
||||||
Path to the backup file to restore from. If omitted, automatically
|
Path to the backup file to restore from. If omitted, defaults to
|
||||||
finds the latest Win11Debloat-StartBackup-*.bak file.
|
StartMenuBinFile with a .bak extension.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin"
|
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin"
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
|
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -BackupFilePath "C:\Backups\start2.bin"
|
||||||
#>
|
#>
|
||||||
function RestoreStartMenuFromBackup {
|
function RestoreStartMenuFromBackup {
|
||||||
param(
|
param(
|
||||||
@@ -274,32 +226,20 @@ function RestoreStartMenuFromBackup {
|
|||||||
)
|
)
|
||||||
|
|
||||||
$userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile
|
$userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile
|
||||||
$backupBinFile = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) {
|
$backupTimestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
|
||||||
# Auto-detect latest backup in the same folder as the start2.bin
|
$startMenuBackupsDir = Join-Path $script:AppDataPath 'Backups'
|
||||||
$startMenuDir = Split-Path $StartMenuBinFile -Parent
|
if (-not (Test-Path $startMenuBackupsDir)) { New-Item -ItemType Directory -Path $startMenuBackupsDir -Force | Out-Null }
|
||||||
$latestBackup = Get-ChildItem -Path (Join-Path $startMenuDir 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
|
|
||||||
Sort-Object Name -Descending |
|
|
||||||
Select-Object -First 1
|
|
||||||
|
|
||||||
if ($latestBackup) { $latestBackup.FullName } else { $null }
|
$resolvedBackupPath = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) {
|
||||||
|
Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinBackup-$userName-$backupTimestamp.bak"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$BackupFilePath
|
$BackupFilePath
|
||||||
}
|
}
|
||||||
$restoreTimestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
$currentBinBackup = Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinRestore-$userName-$backupTimestamp.bak"
|
||||||
$restoreBackupFileName = "Win11Debloat-StartRestore-$restoreTimestamp.bak"
|
|
||||||
$currentBinBackup = Join-Path (Split-Path $StartMenuBinFile -Parent) $restoreBackupFileName
|
|
||||||
|
|
||||||
if ([string]::IsNullOrWhiteSpace($backupBinFile)) {
|
|
||||||
return [PSCustomObject]@{
|
|
||||||
UserName = $userName
|
|
||||||
Result = $false
|
|
||||||
Message = "No start menu backup file found for user $userName."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($script:Params.ContainsKey("WhatIf")) {
|
if ($script:Params.ContainsKey("WhatIf")) {
|
||||||
Write-Host "[WhatIf] Restore start menu for user $userName from backup $backupBinFile" -ForegroundColor Cyan
|
Write-Host "[WhatIf] Restore start menu for user $userName from backup $resolvedBackupPath" -ForegroundColor Cyan
|
||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
UserName = $userName
|
UserName = $userName
|
||||||
Result = $true
|
Result = $true
|
||||||
@@ -307,11 +247,11 @@ function RestoreStartMenuFromBackup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $backupBinFile)) {
|
if (-not (Test-Path -LiteralPath $resolvedBackupPath)) {
|
||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
UserName = $userName
|
UserName = $userName
|
||||||
Result = $false
|
Result = $false
|
||||||
Message = "No start menu backup file found for user $userName."
|
Message = "Start menu backup file not found: $resolvedBackupPath"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +260,7 @@ function RestoreStartMenuFromBackup {
|
|||||||
Move-Item -Path $StartMenuBinFile -Destination $currentBinBackup -Force
|
Move-Item -Path $StartMenuBinFile -Destination $currentBinBackup -Force
|
||||||
}
|
}
|
||||||
|
|
||||||
Copy-Item -Path $backupBinFile -Destination $StartMenuBinFile -Force
|
Copy-Item -Path $resolvedBackupPath -Destination $StartMenuBinFile -Force
|
||||||
return [PSCustomObject]@{
|
return [PSCustomObject]@{
|
||||||
UserName = $userName
|
UserName = $userName
|
||||||
Result = $true
|
Result = $true
|
||||||
@@ -341,26 +281,34 @@ function RestoreStartMenuFromBackup {
|
|||||||
Restores the start menu for the current target user from a backup.
|
Restores the start menu for the current target user from a backup.
|
||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Resolves the start2.bin path for the currently logged-in user, then
|
Resolves the start2.bin path for the current user (or the user specified
|
||||||
delegates to RestoreStartMenuFromBackup.
|
via the -User parameter), then delegates to RestoreStartMenuFromBackup.
|
||||||
|
Returns early with a warning if the user's start menu path cannot
|
||||||
|
be resolved.
|
||||||
|
|
||||||
.PARAMETER BackupFilePath
|
.PARAMETER BackupFilePath
|
||||||
Path to the backup file to restore from. If omitted, automatically
|
Path to the backup file to restore from.
|
||||||
finds the latest Win11Debloat-StartBackup-*.bak file.
|
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
RestoreStartMenu
|
RestoreStartMenu -BackupFilePath "$env:LOCALAPPDATA\Win11Debloat\Backups\Win11Debloat-Start2BinBackup-Jeff-20260623_143000.bak"
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
RestoreStartMenu -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
|
|
||||||
#>
|
#>
|
||||||
function RestoreStartMenu {
|
function RestoreStartMenu {
|
||||||
param(
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
[string]$BackupFilePath
|
[string]$BackupFilePath
|
||||||
)
|
)
|
||||||
|
|
||||||
$targetUserName = $env:USERNAME
|
$targetUserName = GetUserName
|
||||||
$startMenuBinFile = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin"
|
$startMenuBinFile = GetStartMenuBinPathForUser -UserName $targetUserName
|
||||||
|
|
||||||
|
if ([string]::IsNullOrWhiteSpace($startMenuBinFile)) {
|
||||||
|
Write-Host "Unable to resolve start menu path for user $targetUserName, nothing to restore" -ForegroundColor Yellow
|
||||||
|
return [PSCustomObject]@{
|
||||||
|
UserName = $targetUserName
|
||||||
|
Result = $false
|
||||||
|
Message = "Could not resolve start menu path for user $targetUserName."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Restoring start menu for user $targetUserName from backup..."
|
Write-Host "Restoring start menu for user $targetUserName from backup..."
|
||||||
|
|
||||||
@@ -373,24 +321,19 @@ function RestoreStartMenu {
|
|||||||
|
|
||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
Iterates over every existing user profile and restores each user's
|
Iterates over every existing user profile and restores each user's
|
||||||
start2.bin from the latest backup in their LocalState folder. For the
|
start2.bin from the specified backup file. For the Default user profile,
|
||||||
Default user profile, removes the start2.bin file (which was previously
|
removes the start2.bin file (which was previously copied from a template)
|
||||||
copied from a template) so that new profiles revert to the system
|
so that new profiles revert to the system default start menu.
|
||||||
default start menu.
|
|
||||||
|
|
||||||
.PARAMETER BackupFilePath
|
.PARAMETER BackupFilePath
|
||||||
Path to the backup file to restore from. If omitted, automatically
|
Path to the backup file to restore from.
|
||||||
finds the latest Win11Debloat-StartBackup-*.bak in each user's
|
|
||||||
LocalState folder.
|
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
RestoreStartMenuForAllUsers
|
RestoreStartMenuForAllUsers -BackupFilePath "$env:LOCALAPPDATA\Win11Debloat\Backups\Win11Debloat-Start2BinBackup-Jeff-20260623_143000.bak"
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
RestoreStartMenuForAllUsers -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
|
|
||||||
#>
|
#>
|
||||||
function RestoreStartMenuForAllUsers {
|
function RestoreStartMenuForAllUsers {
|
||||||
param(
|
param(
|
||||||
|
[Parameter(Mandatory)]
|
||||||
[string]$BackupFilePath
|
[string]$BackupFilePath
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,71 @@
|
|||||||
# MainWindow-WindowChrome.ps1
|
# MainWindow-WindowChrome.ps1
|
||||||
# Window sizing, DPI-aware coordinate conversion, and UI animations.
|
# Window sizing, DPI-aware coordinate conversion, maximized-window taskbar-constraint helpers, and UI animations.
|
||||||
|
|
||||||
|
function Register-MaximizedWindowHelper {
|
||||||
|
if (-not ([System.Management.Automation.PSTypeName]'Win11Debloat.MaximizedWindowHelper').Type) {
|
||||||
|
Add-Type -Namespace Win11Debloat -Name MaximizedWindowHelper `
|
||||||
|
-ReferencedAssemblies 'PresentationFramework','System.Windows.Forms','System.Drawing' `
|
||||||
|
-MemberDefinition @'
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct MINMAXINFO {
|
||||||
|
public POINT ptReserved, ptMaxSize, ptMaxPosition, ptMinTrackSize, ptMaxTrackSize;
|
||||||
|
}
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct POINT { public int x, y; }
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||||||
|
private static extern System.IntPtr MonitorFromWindow(System.IntPtr hwnd, uint dwFlags);
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
|
||||||
|
private static extern bool GetMonitorInfo(System.IntPtr hMonitor, ref MONITORINFO lpmi);
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct RECT {
|
||||||
|
public int Left, Top, Right, Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
|
||||||
|
private struct MONITORINFO {
|
||||||
|
public int cbSize;
|
||||||
|
public RECT rcMonitor;
|
||||||
|
public RECT rcWork;
|
||||||
|
public uint dwFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.IntPtr WmGetMinMaxInfoHook(
|
||||||
|
System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) {
|
||||||
|
if (msg == 0x0024) { // WM_GETMINMAXINFO
|
||||||
|
var mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(
|
||||||
|
lParam, typeof(MINMAXINFO));
|
||||||
|
|
||||||
|
const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
|
||||||
|
var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
var monitorInfo = new MONITORINFO();
|
||||||
|
monitorInfo.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(MONITORINFO));
|
||||||
|
|
||||||
|
if (monitor != System.IntPtr.Zero && GetMonitorInfo(monitor, ref monitorInfo)) {
|
||||||
|
mmi.ptMaxPosition.x = monitorInfo.rcWork.Left - monitorInfo.rcMonitor.Left;
|
||||||
|
mmi.ptMaxPosition.y = monitorInfo.rcWork.Top - monitorInfo.rcMonitor.Top;
|
||||||
|
mmi.ptMaxSize.x = monitorInfo.rcWork.Right - monitorInfo.rcWork.Left;
|
||||||
|
mmi.ptMaxSize.y = monitorInfo.rcWork.Bottom - monitorInfo.rcWork.Top;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var screen = System.Windows.Forms.Screen.FromHandle(hwnd);
|
||||||
|
var wa = screen.WorkingArea;
|
||||||
|
var bounds = screen.Bounds;
|
||||||
|
mmi.ptMaxPosition.x = wa.Left - bounds.Left;
|
||||||
|
mmi.ptMaxPosition.y = wa.Top - bounds.Top;
|
||||||
|
mmi.ptMaxSize.x = wa.Width;
|
||||||
|
mmi.ptMaxSize.y = wa.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, lParam, true);
|
||||||
|
}
|
||||||
|
return System.IntPtr.Zero;
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Convert screen-pixel coordinates to WPF device-independent pixels (DIP)
|
# Convert screen-pixel coordinates to WPF device-independent pixels (DIP)
|
||||||
function ConvertTo-ScreenPointToDip {
|
function ConvertTo-ScreenPointToDip {
|
||||||
@@ -52,35 +118,16 @@ function Update-MainWindowChrome {
|
|||||||
)
|
)
|
||||||
|
|
||||||
$windowStateMaximized = [System.Windows.WindowState]::Maximized
|
$windowStateMaximized = [System.Windows.WindowState]::Maximized
|
||||||
|
$chrome = [System.Windows.Shell.WindowChrome]::GetWindowChrome($Window)
|
||||||
|
|
||||||
if ($Window.WindowState -eq $windowStateMaximized) {
|
if ($Window.WindowState -eq $windowStateMaximized) {
|
||||||
$chrome = [System.Windows.Shell.WindowChrome]::GetWindowChrome($Window)
|
$MainBorder.Margin = [System.Windows.Thickness]::new(0)
|
||||||
$resizeBorder = if ($chrome) { $chrome.ResizeBorderThickness } else { [System.Windows.SystemParameters]::WindowResizeBorderThickness }
|
|
||||||
|
|
||||||
# Compute margins using screen bounds vs working area
|
|
||||||
$marginLeft = $resizeBorder.Left
|
|
||||||
$marginTop = $resizeBorder.Top
|
|
||||||
$marginRight = $resizeBorder.Right
|
|
||||||
$marginBottom = $resizeBorder.Bottom
|
|
||||||
|
|
||||||
$screen = Get-WindowScreen -Window $Window
|
|
||||||
if ($screen) {
|
|
||||||
$workTL = ConvertTo-ScreenPointToDip -Window $Window -X $screen.WorkingArea.Left -Y $screen.WorkingArea.Top
|
|
||||||
$workSize = ConvertTo-ScreenPixelsToDip -Window $Window -Width $screen.WorkingArea.Width -Height $screen.WorkingArea.Height
|
|
||||||
$screenTL = ConvertTo-ScreenPointToDip -Window $Window -X $screen.Bounds.Left -Y $screen.Bounds.Top
|
|
||||||
$screenSize = ConvertTo-ScreenPixelsToDip -Window $Window -Width $screen.Bounds.Width -Height $screen.Bounds.Height
|
|
||||||
|
|
||||||
$marginLeft += ($workTL.X - $screenTL.X)
|
|
||||||
$marginTop += ($workTL.Y - $screenTL.Y)
|
|
||||||
$marginRight += ($screenTL.X + $screenSize.Width) - ($workTL.X + $workSize.Width)
|
|
||||||
$marginBottom += ($screenTL.Y + $screenSize.Height) - ($workTL.Y + $workSize.Height)
|
|
||||||
}
|
|
||||||
|
|
||||||
$MainBorder.Margin = [System.Windows.Thickness]::new($marginLeft, $marginTop, $marginRight, $marginBottom)
|
|
||||||
$MainBorder.BorderThickness = [System.Windows.Thickness]::new(0)
|
$MainBorder.BorderThickness = [System.Windows.Thickness]::new(0)
|
||||||
$MainBorder.CornerRadius = [System.Windows.CornerRadius]::new(0)
|
$MainBorder.CornerRadius = [System.Windows.CornerRadius]::new(0)
|
||||||
$MainBorder.Effect = $null
|
$MainBorder.Effect = $null
|
||||||
$TitleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(0)
|
$TitleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(0)
|
||||||
|
# Zero out resize borders when maximized so the entire title bar row is draggable
|
||||||
|
if ($chrome) { $chrome.ResizeBorderThickness = [System.Windows.Thickness]::new(0) }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$MainBorder.Margin = [System.Windows.Thickness]::new(0)
|
$MainBorder.Margin = [System.Windows.Thickness]::new(0)
|
||||||
@@ -88,6 +135,7 @@ function Update-MainWindowChrome {
|
|||||||
$MainBorder.CornerRadius = [System.Windows.CornerRadius]::new(8)
|
$MainBorder.CornerRadius = [System.Windows.CornerRadius]::new(8)
|
||||||
$MainBorder.Effect = $NormalWindowShadow
|
$MainBorder.Effect = $NormalWindowShadow
|
||||||
$TitleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(8, 8, 0, 0)
|
$TitleBarBackground.CornerRadius = [System.Windows.CornerRadius]::new(8, 8, 0, 0)
|
||||||
|
if ($chrome) { $chrome.ResizeBorderThickness = [System.Windows.Thickness]::new(5) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,3 @@
|
|||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Hides the currently displayed bubble popup.
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
Closes the bubble popup with a smooth fade-out animation (220ms). If the
|
|
||||||
-Immediate switch is used, the popup is closed instantly without animation.
|
|
||||||
This function is called automatically by Show-Bubble's timer and can also
|
|
||||||
be invoked manually to dismiss the bubble early.
|
|
||||||
|
|
||||||
.PARAMETER Immediate
|
|
||||||
If specified, the bubble popup is closed instantly without a fade-out
|
|
||||||
animation. Any pending close timer is also stopped.
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Hide-Bubble
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Hide-Bubble -Immediate
|
|
||||||
#>
|
|
||||||
function Hide-Bubble {
|
function Hide-Bubble {
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
@@ -57,34 +37,6 @@ function Hide-Bubble {
|
|||||||
$bubblePanel.BeginAnimation([System.Windows.UIElement]::OpacityProperty, $fadeOut)
|
$bubblePanel.BeginAnimation([System.Windows.UIElement]::OpacityProperty, $fadeOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Displays a transient bubble popup hint anchored above a target control.
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
Shows a WPF popup styled as a speech bubble above the specified target
|
|
||||||
control. The bubble fades in with a animation, displays for a configurable
|
|
||||||
duration, then fades out. Any previously shown bubble is dismissed
|
|
||||||
immediately before showing the new one.
|
|
||||||
|
|
||||||
.PARAMETER TargetControl
|
|
||||||
The WPF Control above which the bubble popup will be placed. This
|
|
||||||
parameter is mandatory.
|
|
||||||
|
|
||||||
.PARAMETER Message
|
|
||||||
The text message to display inside the bubble. Defaults to
|
|
||||||
'View the selected changes here'.
|
|
||||||
|
|
||||||
.PARAMETER DurationSeconds
|
|
||||||
The number of seconds the bubble remains visible before auto-hiding.
|
|
||||||
The minimum value is 1 second. Defaults to 5 seconds.
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Show-Bubble -TargetControl $myButton
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Show-Bubble -TargetControl $myButton -Message 'Changes saved!' -DurationSeconds 3
|
|
||||||
#>
|
|
||||||
function Show-Bubble {
|
function Show-Bubble {
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory=$true)]
|
[Parameter(Mandatory=$true)]
|
||||||
|
|||||||
@@ -1,6 +1,71 @@
|
|||||||
function Show-MainWindow {
|
function Show-MainWindow {
|
||||||
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms | Out-Null
|
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase,System.Windows.Forms | Out-Null
|
||||||
|
|
||||||
|
# ---- Constrain maximized window to taskbar work area ----
|
||||||
|
if (-not ([System.Management.Automation.PSTypeName]'Win11Debloat.MaximizedWindowHelper').Type) {
|
||||||
|
Add-Type -Namespace Win11Debloat -Name MaximizedWindowHelper `
|
||||||
|
-ReferencedAssemblies 'PresentationFramework','System.Windows.Forms','System.Drawing' `
|
||||||
|
-MemberDefinition @'
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct MINMAXINFO {
|
||||||
|
public POINT ptReserved, ptMaxSize, ptMaxPosition, ptMinTrackSize, ptMaxTrackSize;
|
||||||
|
}
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct POINT { public int x, y; }
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll")]
|
||||||
|
private static extern System.IntPtr MonitorFromWindow(System.IntPtr hwnd, uint dwFlags);
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
|
||||||
|
private static extern bool GetMonitorInfo(System.IntPtr hMonitor, ref MONITORINFO lpmi);
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
|
private struct RECT {
|
||||||
|
public int Left, Top, Right, Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
|
||||||
|
private struct MONITORINFO {
|
||||||
|
public int cbSize;
|
||||||
|
public RECT rcMonitor;
|
||||||
|
public RECT rcWork;
|
||||||
|
public uint dwFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static System.IntPtr WmGetMinMaxInfoHook(
|
||||||
|
System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) {
|
||||||
|
if (msg == 0x0024) { // WM_GETMINMAXINFO
|
||||||
|
var mmi = (MINMAXINFO)System.Runtime.InteropServices.Marshal.PtrToStructure(
|
||||||
|
lParam, typeof(MINMAXINFO));
|
||||||
|
|
||||||
|
const uint MONITOR_DEFAULTTONEAREST = 0x00000002;
|
||||||
|
var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
|
var monitorInfo = new MONITORINFO();
|
||||||
|
monitorInfo.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(MONITORINFO));
|
||||||
|
|
||||||
|
if (monitor != System.IntPtr.Zero && GetMonitorInfo(monitor, ref monitorInfo)) {
|
||||||
|
mmi.ptMaxPosition.x = monitorInfo.rcWork.Left - monitorInfo.rcMonitor.Left;
|
||||||
|
mmi.ptMaxPosition.y = monitorInfo.rcWork.Top - monitorInfo.rcMonitor.Top;
|
||||||
|
mmi.ptMaxSize.x = monitorInfo.rcWork.Right - monitorInfo.rcWork.Left;
|
||||||
|
mmi.ptMaxSize.y = monitorInfo.rcWork.Bottom - monitorInfo.rcWork.Top;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var screen = System.Windows.Forms.Screen.FromHandle(hwnd);
|
||||||
|
var wa = screen.WorkingArea;
|
||||||
|
var bounds = screen.Bounds;
|
||||||
|
mmi.ptMaxPosition.x = wa.Left - bounds.Left;
|
||||||
|
mmi.ptMaxPosition.y = wa.Top - bounds.Top;
|
||||||
|
mmi.ptMaxSize.x = wa.Width;
|
||||||
|
mmi.ptMaxSize.y = wa.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Runtime.InteropServices.Marshal.StructureToPtr(mmi, lParam, true);
|
||||||
|
}
|
||||||
|
return System.IntPtr.Zero;
|
||||||
|
}
|
||||||
|
'@
|
||||||
|
}
|
||||||
|
|
||||||
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
|
$WinVersion = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' CurrentBuild
|
||||||
$usesDarkMode = GetSystemUsesDarkMode
|
$usesDarkMode = GetSystemUsesDarkMode
|
||||||
|
|
||||||
@@ -58,6 +123,12 @@
|
|||||||
$window.Add_SourceInitialized({
|
$window.Add_SourceInitialized({
|
||||||
& $applyInitialWindowSize
|
& $applyInitialWindowSize
|
||||||
& $updateWindowChrome
|
& $updateWindowChrome
|
||||||
|
|
||||||
|
$hwndHelper = New-Object System.Windows.Interop.WindowInteropHelper($window)
|
||||||
|
$hwndSource = [System.Windows.Interop.HwndSource]::FromHwnd($hwndHelper.Handle)
|
||||||
|
$hookMethod = [Win11Debloat.MaximizedWindowHelper].GetMethod('WmGetMinMaxInfoHook')
|
||||||
|
$hook = [System.Delegate]::CreateDelegate([System.Windows.Interop.HwndSourceHook], $hookMethod)
|
||||||
|
$hwndSource.AddHook($hook)
|
||||||
})
|
})
|
||||||
|
|
||||||
$window.Add_SizeChanged({
|
$window.Add_SizeChanged({
|
||||||
@@ -88,7 +159,7 @@
|
|||||||
$menuReportBug.Add_Click({ Start-Process "https://github.com/Raphire/Win11Debloat/issues" })
|
$menuReportBug.Add_Click({ Start-Process "https://github.com/Raphire/Win11Debloat/issues" })
|
||||||
|
|
||||||
$menuLogs.Add_Click({
|
$menuLogs.Add_Click({
|
||||||
$logsFolder = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) 'Logs'
|
$logsFolder = Split-Path $script:DefaultLogPath -Parent
|
||||||
if (Test-Path $logsFolder) {
|
if (Test-Path $logsFolder) {
|
||||||
Start-Process "explorer.exe" -ArgumentList $logsFolder
|
Start-Process "explorer.exe" -ArgumentList $logsFolder
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ function Show-RestoreBackupDialog {
|
|||||||
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
|
||||||
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
|
||||||
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
|
||||||
|
$startMenuAutoBackupCheck.Visibility = 'Collapsed'
|
||||||
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
$introInfoPanel = $window.FindName('IntroInfoPanel')
|
||||||
$overviewPanel = $window.FindName('OverviewPanel')
|
$overviewPanel = $window.FindName('OverviewPanel')
|
||||||
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
|
||||||
@@ -148,9 +149,8 @@ function Show-RestoreBackupDialog {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
$hasSelectedFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
||||||
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
|
if ($hasSelectedFile) {
|
||||||
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
|
|
||||||
$primaryActionBtn.Content = 'Restore backup'
|
$primaryActionBtn.Content = 'Restore backup'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -196,10 +196,6 @@ function Show-RestoreBackupDialog {
|
|||||||
$primaryActionBtn.Visibility = 'Visible'
|
$primaryActionBtn.Visibility = 'Visible'
|
||||||
$primaryActionBtn.IsDefault = $true
|
$primaryActionBtn.IsDefault = $true
|
||||||
$chooseRegistryBtn.IsDefault = $false
|
$chooseRegistryBtn.IsDefault = $false
|
||||||
|
|
||||||
# Show intro panel so user can configure scope & auto-detect
|
|
||||||
$startMenuAutoBackupCheck.IsChecked = $true
|
|
||||||
$state.SelectedStartMenuBackupFilePath = $null
|
|
||||||
& $refreshStartMenuUi
|
& $refreshStartMenuUi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,15 +302,11 @@ function Show-RestoreBackupDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$handleStartMenuPrimaryAction = {
|
$handleStartMenuPrimaryAction = {
|
||||||
$scope = (& $getStartMenuScopeInfo).Scope
|
if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
||||||
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
|
|
||||||
|
|
||||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
|
|
||||||
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
$openDialog = New-Object Microsoft.Win32.OpenFileDialog
|
||||||
$openDialog.Title = 'Select Start Menu Backup File'
|
$openDialog.Title = 'Select Start Menu Backup File'
|
||||||
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
|
||||||
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
|
$openDialog.InitialDirectory = (Join-Path $script:AppDataPath 'Backups')
|
||||||
$openDialog.DefaultExt = '.bak'
|
|
||||||
|
|
||||||
if ($openDialog.ShowDialog($window) -ne $true) {
|
if ($openDialog.ShowDialog($window) -ne $true) {
|
||||||
return
|
return
|
||||||
@@ -326,21 +318,10 @@ function Show-RestoreBackupDialog {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $useManualBackupFile) {
|
$scope = (& $getStartMenuScopeInfo).Scope
|
||||||
$scopeInfo = & $getStartMenuScopeInfo
|
|
||||||
$autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
|
|
||||||
if ($null -eq $autoBackupPath) {
|
|
||||||
$scopeText = $scopeInfo.SummaryText
|
|
||||||
Show-MessageBox -Owner $window -Title 'No Backup Found' -Message "No Start Menu backup file was found for $scopeText. Uncheck 'Automatically find Start Menu backup' to select a backup file manually." -Button 'OK' -Icon 'Warning' | Out-Null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
|
|
||||||
}
|
|
||||||
|
|
||||||
$window.Tag = @{
|
$window.Tag = @{
|
||||||
Result = 'RestoreStartMenu'
|
Result = 'RestoreStartMenu'
|
||||||
StartMenuScope = $scope
|
StartMenuScope = $scope
|
||||||
UseManualBackupFile = $useManualBackupFile
|
|
||||||
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
BackupFilePath = $state.SelectedStartMenuBackupFilePath
|
||||||
}
|
}
|
||||||
$window.DialogResult = $true
|
$window.DialogResult = $true
|
||||||
@@ -359,16 +340,7 @@ function Show-RestoreBackupDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$startMenuAutoBackupCheck.Add_Checked({
|
|
||||||
$state.SelectedStartMenuBackupFilePath = $null
|
|
||||||
& $refreshStartMenuUi
|
|
||||||
})
|
|
||||||
$startMenuAutoBackupCheck.Add_Unchecked({
|
|
||||||
& $refreshStartMenuUi
|
|
||||||
})
|
|
||||||
|
|
||||||
$startMenuScopeCombo.Add_SelectionChanged({
|
$startMenuScopeCombo.Add_SelectionChanged({
|
||||||
$state.SelectedStartMenuBackupFilePath = $null
|
|
||||||
& $refreshStartMenuUi
|
& $refreshStartMenuUi
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -402,7 +374,6 @@ function Show-RestoreBackupDialog {
|
|||||||
|
|
||||||
if ($state.WizardStep -eq 'StartMenu') {
|
if ($state.WizardStep -eq 'StartMenu') {
|
||||||
$state.SelectedStartMenuBackupFilePath = $null
|
$state.SelectedStartMenuBackupFilePath = $null
|
||||||
$startMenuAutoBackupCheck.IsChecked = $true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
& $setWizardStep 'SelectType'
|
& $setWizardStep 'SelectType'
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ function Show-RestoreBackupWindow {
|
|||||||
}
|
}
|
||||||
elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
|
elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
|
||||||
$scope = $dialogResult.StartMenuScope
|
$scope = $dialogResult.StartMenuScope
|
||||||
$useManualBackupFile = ($dialogResult.UseManualBackupFile -eq $true)
|
|
||||||
$backupFilePath = $null
|
$backupFilePath = $null
|
||||||
if ($dialogResult -is [hashtable] -and $dialogResult.ContainsKey('BackupFilePath')) {
|
if ($dialogResult -is [hashtable] -and $dialogResult.ContainsKey('BackupFilePath')) {
|
||||||
$backupFilePath = $dialogResult['BackupFilePath']
|
$backupFilePath = $dialogResult['BackupFilePath']
|
||||||
@@ -49,7 +48,7 @@ function Show-RestoreBackupWindow {
|
|||||||
$backupFilePath = $dialogResult.BackupFilePath
|
$backupFilePath = $dialogResult.BackupFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($backupFilePath)) {
|
if ([string]::IsNullOrWhiteSpace($backupFilePath)) {
|
||||||
throw 'Start Menu restore canceled: no backup file selected.'
|
throw 'Start Menu restore canceled: no backup file selected.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,31 +131,33 @@ catch {
|
|||||||
Exit
|
Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove old script folder if it exists, but keep configs, logs and backups
|
# Migrate old user data from previous runs to AppData before cleanup
|
||||||
if (Test-Path $tempWorkPath) {
|
if (Test-Path $tempWorkPath) {
|
||||||
|
$appDataPath = Join-Path $env:LOCALAPPDATA 'Win11Debloat'
|
||||||
|
if (-not (Test-Path $appDataPath)) { New-Item -ItemType Directory -Path $appDataPath -Force | Out-Null }
|
||||||
|
|
||||||
|
$oldBackupsDir = Join-Path $tempWorkPath 'Backups'
|
||||||
|
$oldLogsDir = Join-Path $tempWorkPath 'Logs'
|
||||||
|
$oldSettingsFile = Join-Path $tempWorkPath 'Config\LastUsedSettings.json'
|
||||||
|
|
||||||
|
if ((Test-Path $oldBackupsDir) -and (Get-ChildItem -Path $oldBackupsDir -ErrorAction SilentlyContinue)) {
|
||||||
|
$newBackupsDir = Join-Path $appDataPath 'Backups'
|
||||||
|
if (-not (Test-Path $newBackupsDir)) { New-Item -ItemType Directory -Path $newBackupsDir -Force | Out-Null }
|
||||||
|
Get-ChildItem -Path $oldBackupsDir | Move-Item -Destination $newBackupsDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if ((Test-Path $oldLogsDir) -and (Get-ChildItem -Path $oldLogsDir -ErrorAction SilentlyContinue)) {
|
||||||
|
$newLogsDir = Join-Path $appDataPath 'Logs'
|
||||||
|
if (-not (Test-Path $newLogsDir)) { New-Item -ItemType Directory -Path $newLogsDir -Force | Out-Null }
|
||||||
|
Get-ChildItem -Path $oldLogsDir | Move-Item -Destination $newLogsDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if (Test-Path $oldSettingsFile) {
|
||||||
|
Move-Item -Path $oldSettingsFile -Destination $appDataPath -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
Write-Output "> Cleaning up old Win11Debloat folder..."
|
Write-Output "> Cleaning up old Win11Debloat folder..."
|
||||||
|
|
||||||
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
|
Remove-Item $tempWorkPath -Recurse -Force
|
||||||
}
|
|
||||||
|
|
||||||
$configDir = Join-Path $tempWorkPath 'Config'
|
|
||||||
$backupDir = Join-Path $tempWorkPath 'ConfigOld'
|
|
||||||
|
|
||||||
# Temporarily move existing config files if they exist to prevent them from being overwritten by the new script files, will be moved back after the new script is unpacked
|
|
||||||
if (Test-Path "$configDir") {
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "> Backing up existing config files..."
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path "$backupDir" -Force | Out-Null
|
|
||||||
|
|
||||||
$filesToKeep = @(
|
|
||||||
'LastUsedSettings.json'
|
|
||||||
)
|
|
||||||
|
|
||||||
Get-ChildItem -Path "$configDir" -Recurse | Where-Object { $_.Name -in $filesToKeep } | Move-Item -Destination "$backupDir"
|
|
||||||
|
|
||||||
Remove-Item "$configDir" -Recurse -Force
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
@@ -170,19 +172,6 @@ Remove-Item $tempArchivePath
|
|||||||
# Move files
|
# Move files
|
||||||
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
|
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
|
||||||
|
|
||||||
# Add existing config files back to Config folder
|
|
||||||
if (Test-Path "$backupDir") {
|
|
||||||
if (-not (Test-Path "$configDir")) {
|
|
||||||
New-Item -ItemType Directory -Path "$configDir" -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "> Restoring existing config files..."
|
|
||||||
|
|
||||||
Get-ChildItem -Path "$backupDir" -Recurse | Move-Item -Destination "$configDir"
|
|
||||||
Remove-Item "$backupDir" -Recurse -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make list of arguments to pass on to the script
|
# Make list of arguments to pass on to the script
|
||||||
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
|
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
|
||||||
if ($_.Value -eq $true) {
|
if ($_.Value -eq $true) {
|
||||||
@@ -219,13 +208,12 @@ if ($null -ne $debloatProcess) {
|
|||||||
$debloatProcess.WaitForExit()
|
$debloatProcess.WaitForExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove all remaining script files, except for configs, logs and backups
|
# Remove all remaining script files
|
||||||
if (Test-Path $tempWorkPath) {
|
if (Test-Path $tempWorkPath) {
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
Write-Output "> Cleaning up..."
|
Write-Output "> Cleaning up..."
|
||||||
|
|
||||||
# Cleanup, remove Win11Debloat directory
|
Remove-Item $tempWorkPath -Recurse -Force
|
||||||
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
|
|||||||
@@ -132,31 +132,33 @@ catch {
|
|||||||
Exit
|
Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove old script folder if it exists, but keep configs, logs and backups
|
# Migrate old user data from previous runs to AppData before cleanup
|
||||||
if (Test-Path $tempWorkPath) {
|
if (Test-Path $tempWorkPath) {
|
||||||
|
$appDataPath = Join-Path $env:LOCALAPPDATA 'Win11Debloat'
|
||||||
|
if (-not (Test-Path $appDataPath)) { New-Item -ItemType Directory -Path $appDataPath -Force | Out-Null }
|
||||||
|
|
||||||
|
$oldBackupsDir = Join-Path $tempWorkPath 'Backups'
|
||||||
|
$oldLogsDir = Join-Path $tempWorkPath 'Logs'
|
||||||
|
$oldSettingsFile = Join-Path $tempWorkPath 'Config\LastUsedSettings.json'
|
||||||
|
|
||||||
|
if ((Test-Path $oldBackupsDir) -and (Get-ChildItem -Path $oldBackupsDir -ErrorAction SilentlyContinue)) {
|
||||||
|
$newBackupsDir = Join-Path $appDataPath 'Backups'
|
||||||
|
if (-not (Test-Path $newBackupsDir)) { New-Item -ItemType Directory -Path $newBackupsDir -Force | Out-Null }
|
||||||
|
Get-ChildItem -Path $oldBackupsDir | Move-Item -Destination $newBackupsDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if ((Test-Path $oldLogsDir) -and (Get-ChildItem -Path $oldLogsDir -ErrorAction SilentlyContinue)) {
|
||||||
|
$newLogsDir = Join-Path $appDataPath 'Logs'
|
||||||
|
if (-not (Test-Path $newLogsDir)) { New-Item -ItemType Directory -Path $newLogsDir -Force | Out-Null }
|
||||||
|
Get-ChildItem -Path $oldLogsDir | Move-Item -Destination $newLogsDir -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
if (Test-Path $oldSettingsFile) {
|
||||||
|
Move-Item -Path $oldSettingsFile -Destination $appDataPath -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
Write-Output "> Cleaning up old Win11Debloat folder..."
|
Write-Output "> Cleaning up old Win11Debloat folder..."
|
||||||
|
|
||||||
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
|
Remove-Item $tempWorkPath -Recurse -Force
|
||||||
}
|
|
||||||
|
|
||||||
$configDir = Join-Path $tempWorkPath 'Config'
|
|
||||||
$backupDir = Join-Path $tempWorkPath 'ConfigOld'
|
|
||||||
|
|
||||||
# Temporarily move existing config files if they exist to prevent them from being overwritten by the new script files, will be moved back after the new script is unpacked
|
|
||||||
if (Test-Path "$configDir") {
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "> Backing up existing config files..."
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Path "$backupDir" -Force | Out-Null
|
|
||||||
|
|
||||||
$filesToKeep = @(
|
|
||||||
'LastUsedSettings.json'
|
|
||||||
)
|
|
||||||
|
|
||||||
Get-ChildItem -Path "$configDir" -Recurse | Where-Object { $_.Name -in $filesToKeep } | Move-Item -Destination "$backupDir"
|
|
||||||
|
|
||||||
Remove-Item "$configDir" -Recurse -Force
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
@@ -171,19 +173,6 @@ Remove-Item $tempArchivePath
|
|||||||
# Move files
|
# Move files
|
||||||
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
|
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
|
||||||
|
|
||||||
# Add existing config files back to Config folder
|
|
||||||
if (Test-Path "$backupDir") {
|
|
||||||
if (-not (Test-Path "$configDir")) {
|
|
||||||
New-Item -ItemType Directory -Path "$configDir" -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Output ""
|
|
||||||
Write-Output "> Restoring existing config files..."
|
|
||||||
|
|
||||||
Get-ChildItem -Path "$backupDir" -Recurse | Move-Item -Destination "$configDir"
|
|
||||||
Remove-Item "$backupDir" -Recurse -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make list of arguments to pass on to the script
|
# Make list of arguments to pass on to the script
|
||||||
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
|
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
|
||||||
if ($_.Value -eq $true) {
|
if ($_.Value -eq $true) {
|
||||||
@@ -220,13 +209,12 @@ if ($null -ne $debloatProcess) {
|
|||||||
$debloatProcess.WaitForExit()
|
$debloatProcess.WaitForExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove all remaining script files, except for configs, logs and backups
|
# Remove all remaining script files
|
||||||
if (Test-Path $tempWorkPath) {
|
if (Test-Path $tempWorkPath) {
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
Write-Output "> Cleaning up..."
|
Write-Output "> Cleaning up..."
|
||||||
|
|
||||||
# Cleanup, remove Win11Debloat directory
|
Remove-Item $tempWorkPath -Recurse -Force
|
||||||
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Output ""
|
Write-Output ""
|
||||||
|
|||||||
@@ -137,19 +137,19 @@ if (-not $isAdmin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Define script-level variables & paths
|
# Define script-level variables & paths
|
||||||
$script:Version = "2026.06.24"
|
$script:Version = "2026.06.14"
|
||||||
$configPath = Join-Path $PSScriptRoot 'Config'
|
$configPath = Join-Path $PSScriptRoot 'Config'
|
||||||
$logsPath = Join-Path $PSScriptRoot 'Logs'
|
|
||||||
$schemasPath = Join-Path $PSScriptRoot 'Schemas'
|
$schemasPath = Join-Path $PSScriptRoot 'Schemas'
|
||||||
$scriptsPath = Join-Path $PSScriptRoot 'Scripts'
|
$scriptsPath = Join-Path $PSScriptRoot 'Scripts'
|
||||||
|
|
||||||
|
$script:AppDataPath = Join-Path $env:LOCALAPPDATA 'Win11Debloat'
|
||||||
$script:AppsListFilePath = Join-Path $configPath 'Apps.json'
|
$script:AppsListFilePath = Join-Path $configPath 'Apps.json'
|
||||||
$script:DefaultSettingsFilePath = Join-Path $configPath 'DefaultSettings.json'
|
$script:DefaultSettingsFilePath = Join-Path $configPath 'DefaultSettings.json'
|
||||||
$script:FeaturesFilePath = Join-Path $configPath 'Features.json'
|
$script:FeaturesFilePath = Join-Path $configPath 'Features.json'
|
||||||
$script:SavedSettingsFilePath = Join-Path $configPath 'LastUsedSettings.json'
|
$script:SavedSettingsFilePath = Join-Path $script:AppDataPath 'LastUsedSettings.json'
|
||||||
$script:DefaultLogPath = Join-Path $logsPath 'Win11Debloat.log'
|
$script:DefaultLogPath = Join-Path (Join-Path $script:AppDataPath 'Logs') 'Win11Debloat.log'
|
||||||
$script:RegfilesPath = Join-Path $PSScriptRoot 'Regfiles'
|
$script:RegfilesPath = Join-Path $PSScriptRoot 'Regfiles'
|
||||||
$script:RegistryBackupsPath = Join-Path $PSScriptRoot 'Backups'
|
$script:RegistryBackupsPath = Join-Path $script:AppDataPath 'Backups'
|
||||||
$script:AssetsPath = Join-Path $PSScriptRoot 'Assets'
|
$script:AssetsPath = Join-Path $PSScriptRoot 'Assets'
|
||||||
$script:AppSelectionSchema = Join-Path $schemasPath 'AppSelectionWindow.xaml'
|
$script:AppSelectionSchema = Join-Path $schemasPath 'AppSelectionWindow.xaml'
|
||||||
$script:MainWindowSchema = Join-Path $schemasPath 'MainWindow.xaml'
|
$script:MainWindowSchema = Join-Path $schemasPath 'MainWindow.xaml'
|
||||||
@@ -212,6 +212,9 @@ Write-Host ""
|
|||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Log script output to 'Win11Debloat.log' at the specified path
|
# Log script output to 'Win11Debloat.log' at the specified path
|
||||||
|
$logDir = if ($LogPath) { $LogPath } else { Split-Path $script:DefaultLogPath -Parent }
|
||||||
|
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
|
||||||
|
|
||||||
if ($LogPath -and (Test-Path $LogPath)) {
|
if ($LogPath -and (Test-Path $LogPath)) {
|
||||||
Start-Transcript -Path (Join-Path $LogPath 'Win11Debloat.log') -Append -IncludeInvocationHeader -Force | Out-Null
|
Start-Transcript -Path (Join-Path $LogPath 'Win11Debloat.log') -Append -IncludeInvocationHeader -Force | Out-Null
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user