From aa719b49bf67d693233b8c677b80e4000b51da4f Mon Sep 17 00:00:00 2001 From: CodingWonders <101426328+CodingWonders@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:48:11 +0100 Subject: [PATCH] [MicroWin] 2026 Update (#3837) * New reporting tool * Why did we EVER need to load the component store? * Up the execution policy for default PWSH sessions This is required to run the diagnostics script, and any script that either we create here, or the user creates later. At least we don't go too wild and use unrestricted... we use something more "safe" * Force Panther Setup instead of MoSetup This will increase reliability and performance. Screw the Windows 8 installer, we're back with what was used on Windows 7! Anyway, there are problems with the new installer: 1. It is slow at everything. Some people did benchmarks of both installers. MoSetup lost to Panther (ofc). Source: https://www.reddit.com/r/Windows11/comments/1kmjavl/i_made_a_small_test_comparing_which_one_installs/ 2. It is unreliable. The "Install driver to show hardware" screen that appears in some systems AND in PXE/WDS for Server 2025 is possibly, and simply, due to Setup not being able to find the install image. From BlueBox.log in MoSetup: ... 2025-12-21 10:05:43: Creating path (with ACL): [X:\$WINDOWS.~BT]... 2025-12-21 10:05:43: Creating path: [X:\$WINDOWS.~BT\Sources]... 2025-12-21 10:05:43: CBootScenarioCtrl::PopulateWorkingDir: Install.wim/swm/esd was not found! ... Other contributors in the CTT community stated similar unreliability with borked installs. I'm pretty sure this change will benefit everyone. We'll see how long it lasts before Microsoft f's it up. * Improve reporting tool Display appx package dependency full names * Rework ADK detection * Add revamped detection for ISO creation * File Explorer ain't broken anymore on 26200.7462 Those incompetent developers changed WindowsAppRuntime.CBS to WindowsAppRuntime.CBS.1.6 circa 10.0.26200.7462. No wonder why direct downloads were not causing this issue; they are still based on 10.0.26200.6584. The MCT, on the other hand... * Patch maximum version boundary Apparently they fixed that in November. Expanding the amd64_winappsdk-cbs-stable component manifest to a XML file reveals the dark secret: This is not a problem in .6899 because we don't have amd64_winappsdk-cbs-stable, but a vnext counterpart. But, at this point, why bother checking with that? MCT will not bundle this update with the image! * Default to showing file extensions Apart from a security improvement (let's remember ILOVEYOU from 2000), it's also a QoL. * Fix key for color modes Color mode settings are saved per-user, not system-wide. So HKLM\SOFTWARE is not our solution. HKCU is, and is mapped to \Users\Default\NTUSER.DAT * Move color mode from offline servicing to firstrun * Drop build number by 100 * Update Invoke-Microwin.ps1 (#15) * Revert "Update Invoke-Microwin.ps1 (#15)" This reverts commit 88f8a3031b2eb350b150eb04c7c4ffee428b0414. * Keep trying to commit and unmount images * Consistent REG ADD /F /V... Some issues happen with REG and strings if you put /f at the end... move it to the beginning, where it doesn't conflict with anything. * Rework stub path reg value set This works on both Windows 11 and Windows 10. It didn't work on Win10 before; it should work now * Remove copy link step We no longer do this * Move Copy-Files to microwin folder Only microwin calls this function; it's better if we place this script in that folder * Add Create Bootable USB (#18) * Add Create Bootable USB * Apply suggestion from @CodingWonders --------- Co-authored-by: CodingWonders <101426328+CodingWonders@users.noreply.github.com> * Download ADK to get oscdimg if not to download on GitHub If we don't download OSCDIMG from GitHub, because we now use ADK kits roots detection to get installed copies of the ADK and oscdimg, we just download the Deployment Tools. This is only 100 MB once fully installed, and removes the need to install Chocolatey. One caveat is that, on 64-bit systems, the script will output "Could not find ADK" once. This is normal as it does checks on both regular SOFTWARE and WOW6432Node, and it's able to pick it on the latter. If it isn't found anywhere, that message will appear twice. * Make indentation more consitent in bootable USB func. * Initialize petoolspath variable * Remove temporary adksetup on completion * Fix whitespace * Remove unnecessary comments and revamp admin check * Revert "Add Create Bootable USB (#18)" This reverts commit 64babfe986755c977e3a0706266d6712a4b11632. * Remove reporting tool creation The reporting tool is now hosted on a separate repo. The script itself can be run anywhere, not just in microwin, so... * Some formatting change here * Remove copy to ventoy option * Fix some more indents * Add Description that ISO automatically removes Win11 Requirements * Additional things - WPBT done by defaults - Skip Logon Animation - Remove convert to ESD and Upgrade to Win11 as Win10 is no longer supported * First attempt at OSCDIMG autodownload * No more custom ScratchPaths! Time to get rid of that mess and just use %TEMP%. All of these minor changes were made on a Server 2K8R2 VM. * Redetect ADK/OSCDIMG after autodownload * Leave ADK reg query error to debug only --------- Co-authored-by: Real-MullaC --- .../{private => microwin}/Copy-Files.ps1 | 0 functions/microwin/Invoke-Microwin.ps1 | 203 ++++++++++++------ functions/microwin/Invoke-MicrowinGetIso.ps1 | 104 +++++---- functions/microwin/Microwin-CopyToUSB.ps1 | 71 ------ .../Microwin-GetAdkDeploymentTools.ps1 | 25 +++ functions/microwin/Microwin-GetKitsRoot.ps1 | 40 ++++ functions/microwin/Microwin-GetOscdimg.ps1 | 6 + functions/microwin/Microwin-NewFirstRun.ps1 | 29 +++ .../microwin/Microwin-TestKitsRootPaths.ps1 | 11 + functions/public/Invoke-ScratchDialog.ps1 | 28 --- functions/public/Invoke-WPFButton.ps1 | 1 - xaml/inputXML.xaml | 38 +--- 12 files changed, 294 insertions(+), 262 deletions(-) rename functions/{private => microwin}/Copy-Files.ps1 (100%) delete mode 100644 functions/microwin/Microwin-CopyToUSB.ps1 create mode 100644 functions/microwin/Microwin-GetAdkDeploymentTools.ps1 create mode 100644 functions/microwin/Microwin-GetKitsRoot.ps1 create mode 100644 functions/microwin/Microwin-TestKitsRootPaths.ps1 delete mode 100644 functions/public/Invoke-ScratchDialog.ps1 diff --git a/functions/private/Copy-Files.ps1 b/functions/microwin/Copy-Files.ps1 similarity index 100% rename from functions/private/Copy-Files.ps1 rename to functions/microwin/Copy-Files.ps1 diff --git a/functions/microwin/Invoke-Microwin.ps1 b/functions/microwin/Invoke-Microwin.ps1 index c9e7b8ff..75a69009 100644 --- a/functions/microwin/Invoke-Microwin.ps1 +++ b/functions/microwin/Invoke-Microwin.ps1 @@ -54,14 +54,9 @@ public class PowerManagement { $index = $sync.MicrowinWindowsFlavors.SelectedValue.Split(":")[0].Trim() Write-Host "Index chosen: '$index' from $($sync.MicrowinWindowsFlavors.SelectedValue)" - $copyToUSB = $sync.WPFMicrowinCopyToUsb.IsChecked $injectDrivers = $sync.MicrowinInjectDrivers.IsChecked $importDrivers = $sync.MicrowinImportDrivers.IsChecked - $WPBT = $sync.MicroWinWPBT.IsChecked - $unsupported = $sync.MicroWinUnsupported.IsChecked - $skipFla = $sync.MicroWinNoFLA.IsChecked - $importVirtIO = $sync.MicrowinCopyVirtIO.IsChecked $mountDir = $sync.MicrowinMountDir.Text @@ -90,8 +85,11 @@ public class PowerManagement { } } - $imgVersion = (Get-WindowsImage -ImagePath $mountDir\sources\install.wim -Index $index).Version + $imgVersion = (Get-WindowsImage -ImagePath "$mountDir\sources\install.wim" -Index $index).Version + # Windows Setup is the second index in the boot image. + $bootVersion = (Get-WindowsImage -ImagePath "$mountDir\sources\boot.wim" -Index 2).Version Write-Host "The Windows Image Build Version is: $imgVersion" + Write-Host "The WinPE boot image Build Version is: $bootVersion" # Detect image version to avoid performing MicroWin processing on Windows 8 and earlier if ((Microwin-TestCompatibleImage $imgVersion $([System.Version]::new(10,0,10240,0))) -eq $false) { @@ -175,33 +173,31 @@ public class PowerManagement { } } - if ($WPBT) { - Write-Host "Disabling WPBT Execution" - reg load HKLM\zSYSTEM "$($scratchDir)\Windows\System32\config\SYSTEM" - reg add "HKLM\zSYSTEM\ControlSet001\Control\Session Manager" /v DisableWpbtExecution /t REG_DWORD /d 1 /f - reg unload HKLM\zSYSTEM - } + Write-Host "Disabling WPBT Execution" + reg load HKLM\zSYSTEM "$($scratchDir)\Windows\System32\config\SYSTEM" + reg add "HKLM\zSYSTEM\ControlSet001\Control\Session Manager" /v DisableWpbtExecution /t REG_DWORD /d 1 /f + reg unload HKLM\zSYSTEM - if ($skipFla) { - Write-Host "Skipping first logon animation..." - reg load HKLM\zSOFTWARE "$($scratchDir)\Windows\System32\config\SOFTWARE" - reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /f - reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /ve /t REG_SZ /d "Stop First Logon Animation Process" /f - reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /v StubPath /t REG_EXPAND_SZ /d '""%WINDIR%\System32\cmd.exe"" /C ""taskkill /f /im firstlogonanim.exe""' /f - reg unload HKLM\zSOFTWARE - } + Write-Host "Skipping first logon animation..." + reg load HKLM\zSOFTWARE "$($scratchDir)\Windows\System32\config\SOFTWARE" + reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /f + reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /f /ve /t REG_SZ /d "Stop First Logon Animation Process" + reg add "HKLM\zSOFTWARE\Microsoft\Active Setup\Installed Components\CMP_NoFla" /f /v StubPath /t REG_EXPAND_SZ /d '\"%WINDIR%\System32\cmd.exe\" /C \"taskkill /f /im firstlogonanim.exe\"' + reg unload HKLM\zSOFTWARE - if ($unsupported) { - Write-Host "Bypassing system requirements (locally)" - reg add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v "SV1" /t REG_DWORD /d 0 /f - reg add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v "SV2" /t REG_DWORD /d 0 /f - reg add "HKLM\SYSTEM\Setup\LabConfig" /v "BypassCPUCheck" /t REG_DWORD /d 1 /f - reg add "HKLM\SYSTEM\Setup\LabConfig" /v "BypassRAMCheck" /t REG_DWORD /d 1 /f - reg add "HKLM\SYSTEM\Setup\LabConfig" /v "BypassSecureBootCheck" /t REG_DWORD /d 1 /f - reg add "HKLM\SYSTEM\Setup\LabConfig" /v "BypassStorageCheck" /t REG_DWORD /d 1 /f - reg add "HKLM\SYSTEM\Setup\LabConfig" /v "BypassTPMCheck" /t REG_DWORD /d 1 /f - reg add "HKLM\SYSTEM\Setup\MoSetup" /v "AllowUpgradesWithUnsupportedTPMOrCPU" /t REG_DWORD /d 1 /f - } + # We have to prepare the target system to accept the diagnostics script + reg load HKLM\zSOFTWARE "$($scratchDir)\Windows\System32\config\SOFTWARE" + reg add "HKLM\zSOFTWARE\WinUtil" /f + reg add "HKLM\zSOFTWARE\WinUtil" /f /v "ToolboxVersion" /t REG_SZ /d "$($sync.version)" + reg add "HKLM\zSOFTWARE\WinUtil" /f /v "MicroWinBuildDate" /t REG_SZ /d "$((Get-Date).ToString('yyMMdd-HHmm'))" + + # REAL software developers set execution policies to unrestricted but, because we're targeting + # mainstream population, we have to lower the level of "riskiness" -- set remotesigned; at least that + # lets us run PWSH scripts that WE create. Execution policies don't really make sense anyway if common sense + # is lacking. + reg add "HKLM\zSOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /v "ExecutionPolicy" /t REG_SZ /d "RemoteSigned" /f + + reg unload HKLM\zSOFTWARE if ($importVirtIO) { Write-Host "Copying VirtIO drivers..." @@ -217,7 +213,10 @@ public class PowerManagement { Microwin-RemoveProvisionedPackages -UseCmdlets $true # Detect Windows 11 24H2 and add dependency to FileExp to prevent Explorer look from going back - thanks @WitherOrNot and @thecatontheceiling - if ((Microwin-TestCompatibleImage $imgVersion $([System.Version]::new(10,0,26100,1))) -eq $true) { + # ----- UPDATE UPDATE UPDATE: they fixed this in 10.0.26100.7019. DO NOT DO THIS OTHERWISE IT BREAKS EXPLORER AGAIN BECAUSE THE CHEEKY LITTLE + # ----- PoS CHANGED APPRUNTIME.CBS TO APPRUNTIME.CBS.1.6. Thing is, we don't need to patch this in those builds because it no longer breaks + # ----- when you don't patch. + if (((Microwin-TestCompatibleImage $imgVersion $([System.Version]::new(10,0,26100,1))) -eq $true) -and ((Microwin-TestCompatibleImage $imgVersion $([System.Version]::new(10,0,26100,7019))) -eq $false)) { try { if (Test-Path "$scratchDir\Windows\SystemApps\MicrosoftWindows.Client.FileExp_cw5n1h2txyewy\appxmanifest.xml" -PathType Leaf) { # Found the culprit. Do the following: @@ -302,11 +301,6 @@ public class PowerManagement { Copy-Item "$env:temp\FirstStartup.ps1" "$($scratchDir)\Windows\FirstStartup.ps1" -force Write-Host "Done copy FirstRun.ps1" - Write-Host "Copy link to winutil.ps1 into the ISO" - $desktopDir = "$($scratchDir)\Windows\Users\Default\Desktop" - New-Item -ItemType Directory -Force -Path "$desktopDir" - dism /English /image:$($scratchDir) /set-profilepath:"$($scratchDir)\Windows\Users\Default" - Write-Host "Copy checkinstall.cmd into the ISO" Microwin-NewCheckInstall Copy-Item "$env:temp\checkinstall.cmd" "$($scratchDir)\Windows\checkinstall.cmd" -force @@ -316,7 +310,6 @@ public class PowerManagement { New-Item -ItemType Directory -Force -Path "$($scratchDir)\Windows\System32\OOBE\BYPASSNRO" Write-Host "Loading registry" - reg load HKLM\zCOMPONENTS "$($scratchDir)\Windows\System32\config\COMPONENTS" reg load HKLM\zDEFAULT "$($scratchDir)\Windows\System32\config\default" reg load HKLM\zNTUSER "$($scratchDir)\Users\Default\ntuser.dat" reg load HKLM\zSOFTWARE "$($scratchDir)\Windows\System32\config\SOFTWARE" @@ -375,9 +368,8 @@ public class PowerManagement { Write-Host "Disabling Reserved Storage" reg add "HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\ReserveManager" /v "ShippedWithReserves" /t REG_DWORD /d 0 /f - Write-Host "Changing theme to dark. This only works on Activated Windows" - reg add "HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize" /v "AppsUseLightTheme" /t REG_DWORD /d 0 /f - reg add "HKLM\zSOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize" /v "SystemUsesLightTheme" /t REG_DWORD /d 0 /f + Write-Host "Showing file extensions..." + reg add "HKLM\zNTUSER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "HideFileExt" /t REG_DWORD /d 0 /f if ((Microwin-TestCompatibleImage $imgVersion $([System.Version]::new(10,0,21996,1))) -eq $false) { # We're dealing with Windows 10. Configure sane desktop settings. NOTE: even though stuff to disable News and Interests is there, @@ -397,7 +389,6 @@ public class PowerManagement { Write-Error "An unexpected error occurred: $_" } finally { Write-Host "Unmounting Registry..." - reg unload HKLM\zCOMPONENTS reg unload HKLM\zDEFAULT reg unload HKLM\zNTUSER reg unload HKLM\zSOFTWARE @@ -406,9 +397,45 @@ public class PowerManagement { Write-Host "Cleaning up image..." dism /English /image:$scratchDir /Cleanup-Image /StartComponentCleanup /ResetBase Write-Host "Cleanup complete." + $committed = $false + $unmounted = $false + + Write-Host "Saving image..." + + try { + Save-WindowsImage -Path "$scratchDir" + $committed = $true + } catch { + do { + # we'll prevent stuff inside this loop from throwing exceptions and breaking from the loop. + try { + Save-WindowsImage -Path "$scratchDir" + $committed = $true + } catch { + Write-Host "Commit operation unsuccessful. Trying again after 3 seconds..." + Start-Sleep -Seconds 3 + } + } until ($committed) + } Write-Host "Unmounting image..." - Dismount-WindowsImage -Path "$scratchDir" -Save + + try { + # because we've already saved the changes earlier, we can safely discard + Dismount-WindowsImage -Discard -Path "$scratchDir" + $unmounted = $true + } catch { + do { + # we'll prevent stuff inside this loop from throwing exceptions and breaking from the loop. + try { + Dismount-WindowsImage -Discard -Path "$scratchDir" + $unmounted = $true + } catch { + Write-Host "Unmount operation unsuccessful. Trying again after 3 seconds..." + Start-Sleep -Seconds 3 + } + } until ($unmounted) + } } try { @@ -433,20 +460,6 @@ public class PowerManagement { } Write-Host "Windows image completed. Continuing with boot.wim." - $esd = $sync.MicroWinESD.IsChecked - if ($esd) { - Write-Host "Converting install image to ESD." - try { - Export-WindowsImage -SourceImagePath "$mountDir\sources\install.wim" -SourceIndex $index -DestinationImagePath "$mountDir\sources\install.esd" -CompressionType "Recovery" - Remove-Item "$mountDir\sources\install.wim" - Write-Host "Converted install image to ESD." - } catch { - Start-Process -FilePath "$env:SystemRoot\System32\dism.exe" -ArgumentList "/export-image /sourceimagefile:`"$mountDir\sources\install.wim`" /sourceindex:1 /destinationimagefile:`"$mountDir\sources\install.esd`" /compress:recovery" -Wait -NoNewWindow - Remove-Item "$mountDir\sources\install.wim" - Write-Host "Converted install image to ESD." - } - } - # Next step boot image Write-Host "Mounting boot image $mountDir\sources\boot.wim into $scratchDir" Mount-WindowsImage -ImagePath "$mountDir\sources\boot.wim" -Index 2 -Path "$scratchDir" @@ -462,7 +475,6 @@ public class PowerManagement { } Write-Host "Loading registry..." - reg load HKLM\zCOMPONENTS "$($scratchDir)\Windows\System32\config\COMPONENTS" >$null reg load HKLM\zDEFAULT "$($scratchDir)\Windows\System32\config\default" >$null reg load HKLM\zNTUSER "$($scratchDir)\Users\Default\ntuser.dat" >$null reg load HKLM\zSOFTWARE "$($scratchDir)\Windows\System32\config\SOFTWARE" >$null @@ -480,27 +492,86 @@ public class PowerManagement { reg add "HKLM\zSYSTEM\Setup\MoSetup" /v "AllowUpgradesWithUnsupportedTPMOrCPU" /t REG_DWORD /d 1 /f # Fix Computer Restarted Unexpectedly Error on New Bare Metal Install reg add "HKLM\zSYSTEM\Setup\Status\ChildCompletion" /v "setup.exe" /t REG_DWORD /d 3 /f + + # Force old Setup on 24H2+ WinPE images due to personal preference; it's simply faster and + # more reliable than MoSetup. I simply can't stand that new setup system. + if ((Microwin-TestCompatibleImage $bootVersion $([System.Version]::new(10,0,26040,0))) -and (Test-Path -Path "$scratchDir\sources\setup.exe" -PathType Leaf)) { + reg add "HKLM\zSYSTEM\Setup" /f /v "CmdLine" /t REG_SZ /d "\sources\setup.exe" + } } catch { Write-Error "An unexpected error occurred: $_" } finally { Write-Host "Unmounting Registry..." - reg unload HKLM\zCOMPONENTS reg unload HKLM\zDEFAULT reg unload HKLM\zNTUSER reg unload HKLM\zSOFTWARE reg unload HKLM\zSYSTEM + $committed = $false + $unmounted = $false + + Write-Host "Saving image..." + + try { + Save-WindowsImage -Path "$scratchDir" + $committed = $true + } catch { + do { + # we'll prevent stuff inside this loop from throwing exceptions and breaking from the loop. + try { + Save-WindowsImage -Path "$scratchDir" + $committed = $true + } catch { + Write-Host "Commit operation unsuccessful. Trying again after 3 seconds..." + Start-Sleep -Seconds 3 + } + } until ($committed) + } + Write-Host "Unmounting image..." - Dismount-WindowsImage -Path "$scratchDir" -Save + + try { + # because we've already saved the changes earlier, we can safely discard + Dismount-WindowsImage -Discard -Path "$scratchDir" + $unmounted = $true + } catch { + do { + # we'll prevent stuff inside this loop from throwing exceptions and breaking from the loop. + try { + Dismount-WindowsImage -Discard -Path "$scratchDir" + $unmounted = $true + } catch { + Write-Host "Unmount operation unsuccessful. Trying again after 3 seconds..." + Start-Sleep -Seconds 3 + } + } until ($unmounted) + } Write-Host "Creating ISO image" + $peToolsPath = "" + + $adkKitsRoot = Microwin-GetKitsRoot -wow64environment $false + $adkKitsRoot_WOW64Environ = Microwin-GetKitsRoot -wow64environment $true + + $expectedADKPath = "$($adkKitsRoot)Assessment and Deployment Kit" + $expectedADKPath_WOW64Environ = "$($adkKitsRoot_WOW64Environ)Assessment and Deployment Kit" + # if we downloaded oscdimg from github it will be in the temp directory so use it # if it is not in temp it is part of ADK and is in global PATH so just set it to oscdimg.exe $oscdimgPath = Join-Path $env:TEMP 'oscdimg.exe' - $oscdImgFound = Test-Path $oscdimgPath -PathType Leaf - if (!$oscdImgFound) { - $oscdimgPath = "oscdimg.exe" + $oscdImgFound = Test-Path -Path "$oscdimgPath" -PathType Leaf + if ((-not ($oscdImgFound)) -and ((Microwin-TestKitsRootPaths -adkKitsRootPath "$expectedADKPath" -adkKitsRootPath_WOW64Environ "$expectedADKPath_WOW64Environ") -eq $true)) { + if ($expectedADKPath -ne "Assessment and Deployment Kit") { $peToolsPath = $expectedADKPath } + if (($peToolsPath -eq "") -and ($expectedADKPath_WOW64Environ -ne "Assessment and Deployment Kit")) { $peToolsPath = $expectedADKPath_WOW64Environ } + + Write-Host "Using $peToolsPath as the Preinstallation Environment tools path..." + # Paths change depending on platform + if ([Environment]::Is64BitOperatingSystem) { + $oscdimgPath = "$peToolsPath\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + } else { + $oscdimgPath = "$peToolsPath\Deployment Tools\x86\Oscdimg\oscdimg.exe" + } } Write-Host "[INFO] Using oscdimg.exe from: $oscdimgPath" @@ -511,12 +582,6 @@ public class PowerManagement { Write-Host "OSCDIMG Error Level : $($oscdimgProc.ExitCode)" - if ($copyToUSB) { - Write-Host "Copying target ISO to the USB drive" - Microwin-CopyToUSB("$($SaveDialog.FileName)") - if ($?) { Write-Host "Done Copying target ISO to USB drive!" } else { Write-Host "ISO copy failed." } - } - Write-Host " _____ " Write-Host "(____ \ " Write-Host " _ \ \ ___ ____ ____ " diff --git a/functions/microwin/Invoke-MicrowinGetIso.ps1 b/functions/microwin/Invoke-MicrowinGetIso.ps1 index c8fe8ae2..e32c1033 100644 --- a/functions/microwin/Invoke-MicrowinGetIso.ps1 +++ b/functions/microwin/Invoke-MicrowinGetIso.ps1 @@ -122,46 +122,60 @@ function Invoke-MicrowinGetIso { Set-WinUtilTaskbaritem -state "Indeterminate" -overlay "logo" Invoke-MicrowinBusyInfo -action "wip" -message "Checking system requirements..." -interactive $false + $adkKitsRoot = Microwin-GetKitsRoot -wow64environment $false + $adkKitsRoot_WOW64Environ = Microwin-GetKitsRoot -wow64environment $true + + $expectedADKPath = "$($adkKitsRoot)Assessment and Deployment Kit" + $expectedADKPath_WOW64Environ = "$($adkKitsRoot_WOW64Environ)Assessment and Deployment Kit" + $oscdimgPath = Join-Path $env:TEMP 'oscdimg.exe' - $oscdImgFound = [bool] (Get-Command -ErrorAction Ignore -Type Application oscdimg.exe) -or (Test-Path $oscdimgPath -PathType Leaf) + $oscdImgFound = [bool] (Microwin-TestKitsRootPaths -adkKitsRootPath "$expectedADKPath" -adkKitsRootPath_WOW64Environ "$expectedADKPath_WOW64Environ") -or (Test-Path $oscdimgPath -PathType Leaf) Write-Host "oscdimg.exe on system: $oscdImgFound" - if (!$oscdImgFound) { - $downloadFromGitHub = $sync.WPFMicrowinDownloadFromGitHub.IsChecked - - if (!$downloadFromGitHub) { - # only show the message to people who did check the box to download from github, if you check the box - # you consent to downloading it, no need to show extra dialogs - [System.Windows.MessageBox]::Show("oscdimg.exe is not found on the system, winutil will now attempt do download and install it using choco. This might take a long time.") - # the step below needs choco to download oscdimg - # Install Choco if not already present - Install-WinUtilChoco - $chocoFound = [bool] (Get-Command -ErrorAction Ignore -Type Application choco) - Write-Host "choco on system: $chocoFound" - if (!$chocoFound) { - [System.Windows.MessageBox]::Show("choco.exe is not found on the system, you need choco to download oscdimg.exe") - return - } - - Start-Process -Verb runas -FilePath powershell.exe -ArgumentList "choco install windows-adk-oscdimg" - $msg = "oscdimg is installed, now close, reopen PowerShell terminal and re-launch winutil.ps1" - Invoke-MicrowinBusyInfo -action "done" -message $msg # We set it to done because it immediately returns from this function - [System.Windows.MessageBox]::Show($msg) - return + if (-not ($oscdImgFound)) { + # First we try to grab it from github, if not, run the ADK installer. + if ((Microwin-GetOscdimg -oscdimgPath $oscdimgPath) -eq $true) { + Write-Host "OSCDIMG download succeeded." } else { - [System.Windows.MessageBox]::Show("oscdimg.exe is not found on the system, winutil will now attempt do download and install it from github. This might take a long time.") - Invoke-MicrowinBusyInfo -action "wip" -message "Downloading oscdimg.exe..." -interactive $false - Microwin-GetOscdimg -oscdimgPath $oscdimgPath - $oscdImgFound = Test-Path $oscdimgPath -PathType Leaf - if (!$oscdImgFound) { - $msg = "oscdimg was not downloaded can not proceed" - Invoke-MicrowinBusyInfo -action "warning" -message $msg - [System.Windows.MessageBox]::Show($msg, "Winutil", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error) + Write-Host "OSCDIMG could not be downloaded from GitHub. Downloading deployment tools..." + if (-not (Microwin-GetAdkDeploymentTools)) { + Invoke-MicrowinBusyInfo -action "warning" -message "Neither OSCDIMG nor ADK could be downloaded." + Write-Host "Neither OSCDIMG nor ADK could be downloaded." return } else { - Write-Host "oscdimg.exe was successfully downloaded from github" + $msg = "ADK/OSCDIMG is installed, now restart this process." + Invoke-MicrowinBusyInfo -action "done" -message $msg # We set it to done because it immediately returns from this function + [System.Windows.MessageBox]::Show($msg) + Remove-Item -Path "$env:TEMP\adksetup.exe" -Force -ErrorAction SilentlyContinue + return } } + } elseif (Microwin-TestKitsRootPaths -adkKitsRootPath "$expectedADKPath" -adkKitsRootPath_WOW64Environ "$expectedADKPath_WOW64Environ") { + # We have to guess where oscdimg is. We'll check both values... + $peToolsPath = "" + + if ($expectedADKPath -ne "Assessment and Deployment Kit") { $peToolsPath = $expectedADKPath } + if (($peToolsPath -eq "") -and ($expectedADKPath_WOW64Environ -ne "Assessment and Deployment Kit")) { $peToolsPath = $expectedADKPath_WOW64Environ } + + Write-Host "Using $peToolsPath as the Preinstallation Environment tools path..." + # Paths change depending on platform + if ([Environment]::Is64BitOperatingSystem) { + $oscdimgPath = "$peToolsPath\Deployment Tools\amd64\Oscdimg\oscdimg.exe" + } else { + $oscdimgPath = "$peToolsPath\Deployment Tools\x86\Oscdimg\oscdimg.exe" + } + + # If it's a non-existent file, we won't continue. + if (-not (Test-Path -Path "$oscdimgPath" -PathType Leaf)) { + $oscdimgFound = $false + } + } + + $oscdImgFound = [bool] (Microwin-TestKitsRootPaths -adkKitsRootPath "$expectedADKPath" -adkKitsRootPath_WOW64Environ "$expectedADKPath_WOW64Environ") -or (Test-Path $oscdimgPath -PathType Leaf) + + if (-not ($oscdimgFound)) { + [System.Windows.MessageBox]::Show("oscdimg.exe is not found on the system. Cannot continue.") + return } Invoke-MicrowinBusyInfo -action "wip" -message "Checking disk space..." -interactive $false @@ -209,23 +223,6 @@ function Invoke-MicrowinGetIso { # there is probably a better way of doing this, I don't have time to figure this out $sync.MicrowinIsoDrive.Text = $driveLetter - $mountedISOPath = (Split-Path -Path "$filePath") - if ($sync.MicrowinScratchDirBox.Text.Trim() -eq "Scratch") { - $sync.MicrowinScratchDirBox.Text ="" - } - - $UseISOScratchDir = $sync.WPFMicrowinISOScratchDir.IsChecked - - if ($UseISOScratchDir) { - $sync.MicrowinScratchDirBox.Text=$mountedISOPath - } - - if( -Not $sync.MicrowinScratchDirBox.Text.EndsWith('\') -And $sync.MicrowinScratchDirBox.Text.Length -gt 1) { - - $sync.MicrowinScratchDirBox.Text = Join-Path $sync.MicrowinScratchDirBox.Text.Trim() '\' - - } - # Detect if the folders already exist and remove them if (($sync.MicrowinMountDir.Text -ne "") -and (Test-Path -Path $sync.MicrowinMountDir.Text)) { try { @@ -244,13 +241,8 @@ function Invoke-MicrowinGetIso { $randomMicrowinScratch = "MicrowinScratch_${timestamp}_${randomNumber}" $sync.BusyText.Text=" - Mounting" Write-Host "Mounting Iso. Please wait." - if ($sync.MicrowinScratchDirBox.Text -eq "") { - $mountDir = Join-Path $env:TEMP $randomMicrowin - $scratchDir = Join-Path $env:TEMP $randomMicrowinScratch - } else { - $scratchDir = $sync.MicrowinScratchDirBox.Text+"Scratch" - $mountDir = $sync.MicrowinScratchDirBox.Text+"micro" - } + $mountDir = Join-Path $env:TEMP $randomMicrowin + $scratchDir = Join-Path $env:TEMP $randomMicrowinScratch $sync.MicrowinMountDir.Text = $mountDir $sync.MicrowinScratchDir.Text = $scratchDir diff --git a/functions/microwin/Microwin-CopyToUSB.ps1 b/functions/microwin/Microwin-CopyToUSB.ps1 deleted file mode 100644 index f1b37c95..00000000 --- a/functions/microwin/Microwin-CopyToUSB.ps1 +++ /dev/null @@ -1,71 +0,0 @@ -function Microwin-CopyToUSB([string]$fileToCopy) { - foreach ($volume in Get-Volume) { - if ($volume -and $volume.FileSystemLabel -ieq "ventoy") { - $destinationPath = "$($volume.DriveLetter):\" - #Copy-Item -Path $fileToCopy -Destination $destinationPath -Force - # Get the total size of the file - $totalSize = (Get-Item "$fileToCopy").length - - Copy-Item -Path "$fileToCopy" -Destination "$destinationPath" -Verbose -Force -Recurse -Container -PassThru | - ForEach-Object { - # Calculate the percentage completed - $completed = ($_.BytesTransferred / $totalSize) * 100 - - # Display the progress bar - Write-Progress -Activity "Copying File" -Status "Progress" -PercentComplete $completed -CurrentOperation ("{0:N2} MB / {1:N2} MB" -f ($_.BytesTransferred / 1MB), ($totalSize / 1MB)) - } - - Write-Host "File copied to Ventoy drive $($volume.DriveLetter)" - - # Detect if config files are present, move them if they are, and configure the Ventoy drive to not bypass the requirements - $customVentoyConfig = @' -{ - "control":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ], - "control_legacy":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ], - "control_uefi":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ], - "control_ia32":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ], - "control_aa64":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ], - "control_mips":[ - { "VTOY_WIN11_BYPASS_CHECK": "0" }, - { "VTOY_WIN11_BYPASS_NRO": "0" } - ] -} -'@ - - try { - Write-Host "Writing custom Ventoy configuration. Please wait..." - if (Test-Path -Path "$($volume.DriveLetter):\ventoy\ventoy.json" -PathType Leaf) { - Write-Host "A Ventoy configuration file exists. Moving it..." - Move-Item -Path "$($volume.DriveLetter):\ventoy\ventoy.json" -Destination "$($volume.DriveLetter):\ventoy\ventoy.json.old" -Force - Write-Host "Existing Ventoy configuration has been moved to `"ventoy.json.old`". Feel free to put your config back into the `"ventoy.json`" file." - } - if (-not (Test-Path -Path "$($volume.DriveLetter):\ventoy")) { - New-Item -Path "$($volume.DriveLetter):\ventoy" -ItemType Directory -Force | Out-Null - } - $customVentoyConfig | Out-File -FilePath "$($volume.DriveLetter):\ventoy\ventoy.json" -Encoding utf8 -Force - Write-Host "The Ventoy drive has been successfully configured." - } catch { - Write-Host "Could not configure Ventoy drive. Error: $($_.Exception.Message)`n" - Write-Host "Be sure to add the following configuration to the Ventoy drive by either creating a `"ventoy.json`" file in the `"ventoy`" directory (create it if it doesn't exist) or by editing an existing one: `n`n$customVentoyConfig`n" - Write-Host "Failure to do this will cause conflicts with your target ISO file." - } - return - } - } - Write-Host "Ventoy USB Key is not inserted" -} diff --git a/functions/microwin/Microwin-GetAdkDeploymentTools.ps1 b/functions/microwin/Microwin-GetAdkDeploymentTools.ps1 new file mode 100644 index 00000000..20df1faa --- /dev/null +++ b/functions/microwin/Microwin-GetAdkDeploymentTools.ps1 @@ -0,0 +1,25 @@ +function Microwin-GetAdkDeploymentTools { + <# + .DESCRIPTION + This function will download the deployment tools from Microsoft + + .EXAMPLE + Microwin-GetAdkDeploymentTools + #> + + # ADK 10.1.28000.1 download link is the same; no need to guess it + $adkDownloadLink = "https://download.microsoft.com/download/615540bc-be0b-433a-b91b-1f2b0642bb24/adk/adksetup.exe" + $adkVersion = "10.1.28000.1" + Write-Host "Downloading ADK version $adkVersion ..." + Invoke-WebRequest -UseBasicParsing -Uri "$adkDownloadLink" -OutFile "$env:TEMP\adksetup.exe" + + if ((-not ($?)) -or (-not (Test-Path -Path "$env:TEMP\adksetup.exe" -PathType Leaf))) { + Write-Host "ADK could not be downloaded." + return $false + } + + Write-Host "Installing ADK version $adkVersion -- This may take a few minutes..." + Start-Process -FilePath "$env:TEMP\adksetup.exe" -ArgumentList "/features OptionId.DeploymentTools /q /ceip off" -Wait + + return $? +} diff --git a/functions/microwin/Microwin-GetKitsRoot.ps1 b/functions/microwin/Microwin-GetKitsRoot.ps1 new file mode 100644 index 00000000..594f5f6a --- /dev/null +++ b/functions/microwin/Microwin-GetKitsRoot.ps1 @@ -0,0 +1,40 @@ +function Microwin-GetKitsRoot { + <# + .SYNOPSIS + Gets the kits root path for the Windows Assessment and Deployment Kit (ADK) + .PARAMETER wow64environment + Determines whether to search in a WOW64 compatibility environment (HKLM\SOFTWARE\WOW6432Node) + .OUTPUTS + The path to the kits root + #> + + param ( + [Parameter(Mandatory = $true, Position = 0)] [bool]$wow64environment + ) + + $adk10KitsRoot = "" + + # if we set the wow64 bit on and we're on a 32-bit system, then we prematurely return the value + if (($wow64environment -eq $true) -and (-not [Environment]::Is64BitOperatingSystem)) { + return $adk10KitsRoot + } + + $regPath = "" + if ($wow64environment) { + $regPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots" + } else { + $regPath = "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots" + } + + if ((Test-Path "$regPath") -eq $false) { + return $adk10KitsRoot + } + + try { + $adk10KitsRoot = Get-ItemPropertyValue -Path $regPath -Name "KitsRoot10" -ErrorAction Stop + } catch { + Write-Debug "Could not find ADK." + } + + return $adk10KitsRoot +} diff --git a/functions/microwin/Microwin-GetOscdimg.ps1 b/functions/microwin/Microwin-GetOscdimg.ps1 index f395f71f..643a56e5 100644 --- a/functions/microwin/Microwin-GetOscdimg.ps1 +++ b/functions/microwin/Microwin-GetOscdimg.ps1 @@ -15,6 +15,10 @@ function Microwin-GetOscdimg { $oscdimgPath = "$env:TEMP\oscdimg.exe" $downloadUrl = "https://github.com/ChrisTitusTech/winutil/raw/main/releases/oscdimg.exe" Invoke-RestMethod -Uri $downloadUrl -OutFile $oscdimgPath + if (-not (Test-Path "$oscdimgPath" -PathType Leaf)) { + Write-Host "OSCDIMG could not be downloaded." + return $false + } $hashResult = Get-FileHash -Path $oscdimgPath -Algorithm SHA256 $sha256Hash = $hashResult.Hash @@ -23,7 +27,9 @@ function Microwin-GetOscdimg { $expectedHash = "AB9E161049D293B544961BFDF2D61244ADE79376D6423DF4F60BF9B147D3C78D" # Replace with the actual expected hash if ($sha256Hash -eq $expectedHash) { Write-Host "Hashes match. File is verified." + return $true } else { Write-Host "Hashes do not match. File may be corrupted or tampered with." + return $false } } diff --git a/functions/microwin/Microwin-NewFirstRun.ps1 b/functions/microwin/Microwin-NewFirstRun.ps1 index 0fe6ff1d..2cbf842d 100644 --- a/functions/microwin/Microwin-NewFirstRun.ps1 +++ b/functions/microwin/Microwin-NewFirstRun.ps1 @@ -103,6 +103,35 @@ function Microwin-NewFirstRun { reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Start" /v ShowRecentList /t REG_DWORD /d 0 /f reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v Start_TrackDocs /t REG_DWORD /d 0 /f + # Color Modes -- requires sending messages to apply to everything + reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" /v "AppsUseLightTheme" /t REG_DWORD /d 0 /f + reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" /v "SystemUsesLightTheme" /t REG_DWORD /d 0 /f + + # Send the WM_SETTINGCHANGE message to all windows + Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; +public class Win32 { + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)] + public static extern IntPtr SendMessageTimeout( + IntPtr hWnd, + uint Msg, + IntPtr wParam, + string lParam, + uint fuFlags, + uint uTimeout, + out IntPtr lpdwResult); +} +"@ + + $HWND_BROADCAST = [IntPtr]0xffff + $WM_SETTINGCHANGE = 0x1A + $SMTO_ABORTIFHUNG = 0x2 + $timeout = 100 + + # Send the broadcast message to all windows + [Win32]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [IntPtr]::Zero, "ImmersiveColorSet", $SMTO_ABORTIFHUNG, $timeout, [ref]([IntPtr]::Zero)) + Clear-Host Write-Host "The taskbar will take around a minute to show up, but you can start using your computer now. Try pressing the Windows key to open the Start menu, or Windows + E to launch File Explorer." Start-Sleep -Seconds 10 diff --git a/functions/microwin/Microwin-TestKitsRootPaths.ps1 b/functions/microwin/Microwin-TestKitsRootPaths.ps1 new file mode 100644 index 00000000..2188c16d --- /dev/null +++ b/functions/microwin/Microwin-TestKitsRootPaths.ps1 @@ -0,0 +1,11 @@ +function Microwin-TestKitsRootPaths { + param ( + [Parameter(Mandatory = $true, Position = 0)] [string]$adkKitsRootPath, + [Parameter(Mandatory = $true, Position = 1)] [string]$adkKitsRootPath_WOW64Environ + ) + + if (Test-Path "$adkKitsRootPath") { return $true } + if (Test-Path "$adkKitsRootPath_WOW64Environ") { return $true } + + return $false +} diff --git a/functions/public/Invoke-ScratchDialog.ps1 b/functions/public/Invoke-ScratchDialog.ps1 deleted file mode 100644 index a35366c6..00000000 --- a/functions/public/Invoke-ScratchDialog.ps1 +++ /dev/null @@ -1,28 +0,0 @@ - -function Invoke-ScratchDialog { - - <# - - .SYNOPSIS - Enable Editable Text box Alternate Scratch path - - .PARAMETER Button - #> - $sync.WPFMicrowinISOScratchDir.IsChecked - - - [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null - $Dialog = New-Object System.Windows.Forms.FolderBrowserDialog - $Dialog.SelectedPath = $sync.MicrowinScratchDirBox.Text - $Dialog.ShowDialog() - $filePath = $Dialog.SelectedPath - Write-Host "No ISO is chosen+ $filePath" - - if ([string]::IsNullOrEmpty($filePath)) { - Write-Host "No Folder had chosen" - return - } - - $sync.MicrowinScratchDirBox.Text = Join-Path $filePath "\" - -} diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1 index 8f8ace51..68f91e2e 100644 --- a/functions/public/Invoke-WPFButton.ps1 +++ b/functions/public/Invoke-WPFButton.ps1 @@ -59,7 +59,6 @@ function Invoke-WPFButton { "WPFGetIso" {Invoke-MicrowinGetIso} "WPFMicrowin" {Invoke-Microwin} "WPFCloseButton" {Invoke-WPFCloseButton} - "MicrowinScratchDirBT" {Invoke-ScratchDialog} "WPFWinUtilInstallPSProfile" {Invoke-WinUtilInstallPSProfile} "WPFWinUtilUninstallPSProfile" {Invoke-WinUtilUninstallPSProfile} "WPFWinUtilSSHServer" {Invoke-WPFSSHServer} diff --git a/xaml/inputXML.xaml b/xaml/inputXML.xaml index 295df951..45c5a805 100644 --- a/xaml/inputXML.xaml +++ b/xaml/inputXML.xaml @@ -1350,41 +1350,11 @@ HorizontalAlignment="Stretch"> - Choose a Windows ISO file that you've downloaded Check the status in the console - Scratch directory settings (optional) - - - - - - - - - - - - - Custom user settings (leave empty for default user) User name (20 characters max.): - Tweaks (leave empty for default settings) - - - - WinUtil configuration file (JSON) @@ -1564,6 +1527,7 @@ MicroWin features: - Remove Telemetry and Tracking - Fast Install using either the "User" local account or the account of your choosing + - Bypasses Windows 11 System Requirements on unsupported computers - No internet requirement for install - Apps debloat