Skip to content

Commit 6e66d63

Browse files
committed
Update
1 parent 73db0d0 commit 6e66d63

6 files changed

Lines changed: 89 additions & 123 deletions

functions/microwin/Force-CleanupMountDirectory.ps1

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,25 @@ function Force-CleanupMountDirectory {
2727
try {
2828
$null = reg query $hiveName 2>$null
2929
if ($LASTEXITCODE -eq 0) {
30-
reg unload $hiveName 2>$null
30+
# Registry hive is loaded, try to unload it with retries
31+
$attempts = 0
32+
$maxAttempts = 10
33+
do {
34+
$attempts++
35+
reg unload $hiveName 2>$null
36+
if ($LASTEXITCODE -eq 0) {
37+
break
38+
}
39+
Start-Sleep -Milliseconds 100
40+
} until ($attempts -ge $maxAttempts)
3141
}
3242
} catch {
3343
# Hive not loaded or error checking - continue
3444
}
3545
}
3646

3747
# Force garbage collection to release any PowerShell file handles
38-
[System.GC]::Collect()
39-
[System.GC]::WaitForPendingFinalizers()
40-
[System.GC]::Collect()
41-
42-
# Wait a moment for handles to be released
43-
Start-Sleep -Seconds 2
48+
Invoke-GarbageCollection -WaitSeconds 2
4449

4550
# Try to set the mount directory and its contents to not readonly
4651
try {
@@ -64,8 +69,7 @@ function Force-CleanupMountDirectory {
6469
}
6570

6671
# Final cleanup
67-
[System.GC]::Collect()
68-
[System.GC]::WaitForPendingFinalizers()
72+
Invoke-GarbageCollection
6973

7074
return $true
7175

functions/microwin/Invoke-WPFMicroWinGetIsoRunspace.ps1

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -358,48 +358,16 @@ function Invoke-WPFMicroWinGetIsoRunspace {
358358
try {
359359

360360
$totalTime = Measure-Command {
361-
# Use native Copy-Item instead of Copy-Files function
362-
Write-Host "Starting copy operation with robocopy for better performance..."
363-
$robocopyArgs = @(
364-
"$($driveLetter):",
365-
"$mountDir",
366-
"/E", # Copy subdirectories, including empty ones
367-
"/R:3", # Retry 3 times on failed copies
368-
"/W:1", # Wait 1 second between retries
369-
"/MT:8", # Multi-threaded copying with 8 threads
370-
"/XJ" # Exclude junction points
371-
)
372-
373-
$robocopyResult = Start-Process -FilePath "robocopy" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow
374-
375-
# Robocopy exit codes: 0-7 are success, 8+ are errors
376-
if ($robocopyResult.ExitCode -gt 7) {
377-
throw "Robocopy failed with exit code: $($robocopyResult.ExitCode)"
378-
}
379-
380-
Write-Host "Robocopy completed with exit code: $($robocopyResult.ExitCode)"
381-
361+
Copy-Files -Path "$($driveLetter):" -Destination "$mountDir" -Recurse -Force
362+
382363
# Force UI update during long operation
383364
$sync.form.Dispatcher.Invoke([action]{
384365
[System.Windows.Forms.Application]::DoEvents()
385366
})
386367
}
387368
Write-Host "Copy complete! Total Time: $($totalTime.Minutes) minutes, $($totalTime.Seconds) seconds"
388369
} catch {
389-
390-
# Fallback to PowerShell Copy-Item if robocopy fails
391-
try {
392-
$totalTime = Measure-Command {
393-
Copy-Item -Path "$($driveLetter):*" -Destination "$mountDir" -Recurse -Force
394-
# Force UI update during long operation
395-
$sync.form.Dispatcher.Invoke([action]{
396-
[System.Windows.Forms.Application]::DoEvents()
397-
})
398-
}
399-
Write-Host "Fallback copy complete! Total Time: $($totalTime.Minutes) minutes, $($totalTime.Seconds) seconds"
400-
} catch {
401-
throw $_
402-
}
370+
throw $_
403371
} $sync.form.Dispatcher.Invoke([action]{
404372
Set-WinUtilTaskbaritem -state "Normal" -value (8 / $totalSteps) -overlay "logo"
405373
# Invoke-MicrowinBusyInfo -action "wip" -message "Processing Windows image... (Step 8/$totalSteps)" -interactive $false
@@ -460,6 +428,7 @@ function Invoke-WPFMicroWinGetIsoRunspace {
460428
$sync.form.Dispatcher.Invoke([action]{
461429
$sync.MicrowinWindowsFlavors.SelectedIndex = $_.ImageIndex - 1
462430
})
431+
break # Exit the loop since we found the Pro edition
463432
}
464433
# Allow UI updates during this loop
465434
$sync.form.Dispatcher.Invoke([action]{

functions/microwin/Invoke-WPFMicroWinRunspace.ps1

Lines changed: 26 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,7 @@ function Invoke-WPFMicroWinRunspace {
3030
param($MicroWinSettings, $DebugPreference)
3131

3232
# Function to set DISM-compatible permissions on a directory
33-
function Set-DismCompatiblePermissions {
34-
param([string]$Path)
3533

36-
try {
37-
# Use icacls for reliable permission setting with language-independent SIDs
38-
# Grant full control to Administrators (S-1-5-32-544)
39-
& icacls "$Path" /grant "*S-1-5-32-544:(OI)(CI)F" /T /C | Out-Null
40-
41-
# Grant full control to SYSTEM (S-1-5-18)
42-
& icacls "$Path" /grant "*S-1-5-18:(OI)(CI)F" /T /C | Out-Null
43-
44-
# Grant full control to current user
45-
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
46-
& icacls "$Path" /grant "${currentUser}:(OI)(CI)F" /T /C | Out-Null
47-
48-
# Grant modify to Authenticated Users (S-1-5-11) for subfolders and files
49-
& icacls "$Path" /grant "*S-1-5-11:(OI)(CI)M" /T /C | Out-Null
50-
51-
return $true
52-
} catch {
53-
return $false
54-
}
55-
}
5634

5735
$sync.ProcessRunning = $true
5836

@@ -117,12 +95,6 @@ function Invoke-WPFMicroWinRunspace {
11795

11896
Write-Host "Target ISO location: $SaveDialogFileName"
11997

120-
# Performance optimization: Determine optimal thread count
121-
$coreCount = (Get-WmiObject -Class Win32_Processor | Measure-Object -Property NumberOfCores -Sum).Sum
122-
$logicalProcessors = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors
123-
$optimalThreads = [Math]::Min($logicalProcessors, [Math]::Max(2, $coreCount))
124-
Write-Host "System has $coreCount cores, $logicalProcessors logical processors. Using $optimalThreads threads for optimal performance."
125-
12698
# Extract settings from hashtable
12799
$index = $MicroWinSettings.selectedIndex
128100
$mountDir = $MicroWinSettings.mountDir
@@ -213,10 +185,7 @@ function Invoke-WPFMicroWinRunspace {
213185
}
214186

215187
# Check if running as administrator
216-
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
217-
$isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
218-
219-
if (-not $isAdmin) {
188+
if (-not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
220189
$msg = "Administrator privileges are required to mount and modify Windows images. Please run WinUtil as Administrator and try again."
221190
Write-Host $msg
222191
$sync.form.Dispatcher.Invoke([action]{
@@ -286,12 +255,6 @@ function Invoke-WPFMicroWinRunspace {
286255

287256
# Pre-mount system checks
288257

289-
# Check if DISM is available and working
290-
try {
291-
$dismCheck = & dism /? 2>&1
292-
} catch {
293-
}
294-
295258
# Check available disk space
296259
try {
297260
$scratchDrive = Split-Path $scratchDir -Qualifier
@@ -303,23 +266,12 @@ function Invoke-WPFMicroWinRunspace {
303266
} catch {
304267
}
305268

306-
# Check if scratch directory is accessible and set proper permissions
269+
# Check if scratch directory is accessible
307270
try {
308271
if (-not (Test-Path $scratchDir)) {
309272
New-Item -Path $scratchDir -ItemType Directory -Force | Out-Null
310273
}
311274

312-
# Set proper permissions for DISM operations using the helper function
313-
$permissionsSet = Set-DismCompatiblePermissions -Path $scratchDir
314-
315-
if ($permissionsSet) {
316-
# Verify permissions were set correctly
317-
$newAcl = Get-Acl -Path $scratchDir
318-
foreach ($access in $newAcl.Access) {
319-
}
320-
} else {
321-
}
322-
323275
# Test write access
324276
$testFile = Join-Path $scratchDir "test_access.tmp"
325277
"test" | Out-File -FilePath $testFile -Force
@@ -357,22 +309,14 @@ function Invoke-WPFMicroWinRunspace {
357309
foreach ($wimFilePath in $wimFilePaths) {
358310
if (Test-Path $wimFilePath) {
359311
try {
360-
$wimItem = Get-Item -Path $wimFilePath -Force
361-
if ($wimItem.Attributes -band [System.IO.FileAttributes]::ReadOnly) {
362-
$wimItem.Attributes = $wimItem.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
363-
364-
# Verify the change was successful
365-
$wimItem.Refresh()
366-
if ($wimItem.Attributes -band [System.IO.FileAttributes]::ReadOnly) {
367-
$criticalWimError = $true
368-
} else {
369-
}
370-
} else {
312+
# Remove ReadOnly attribute using attrib command
313+
& attrib -R "$wimFilePath" 2>$null
314+
if ($LASTEXITCODE -ne 0) {
315+
$criticalWimError = $true
371316
}
372317
} catch {
373318
$criticalWimError = $true
374319
}
375-
} else {
376320
}
377321
}
378322

@@ -899,14 +843,25 @@ function Invoke-WPFMicroWinRunspace {
899843

900844
# Last attempt - try multiple fallback strategies
901845

902-
# Try DISM discard
846+
847+
# First, commit the image
903848
try {
904-
$dismResult = & dism /english /unmount-image /mountdir:"$scratchDir" /discard /loglevel:1
905-
if ($LASTEXITCODE -eq 0) {
906-
$dismountSuccess = $true
907-
} else {
908-
}
909-
} catch {
849+
& dism /english /commit-image /mountdir:"$scratchDir" /loglevel:1
850+
} catch {}
851+
852+
# Now, keep discarding the image in a loop
853+
$discardAttempts = 0
854+
$maxDiscardAttempts = 6
855+
while (-not $dismountSuccess -and $discardAttempts -lt $maxDiscardAttempts) {
856+
try {
857+
$dismResult = & dism /english /unmount-image /mountdir:"$scratchDir" /discard /loglevel:1
858+
if ($LASTEXITCODE -eq 0) {
859+
$dismountSuccess = $true
860+
break
861+
}
862+
} catch {}
863+
$discardAttempts++
864+
Start-Sleep -Seconds 5
910865
}
911866

912867
# Try PowerShell discard if DISM failed
@@ -942,9 +897,8 @@ function Invoke-WPFMicroWinRunspace {
942897
try {
943898
Write-Host "Exporting image into $mountDir\sources\install2.wim with optimized settings..."
944899
try {
945-
# Use Fast compression for better performance, especially during development/testing
946-
# Users can change this to "Max" if they prefer smaller file size over speed
947-
Export-WindowsImage -SourceImagePath "$mountDir\sources\install.wim" -SourceIndex $index -DestinationImagePath "$mountDir\sources\install2.wim" -CompressionType "Fast"
900+
# Use Max compression for smaller file size (slower, but more efficient)
901+
Export-WindowsImage -SourceImagePath "$mountDir\sources\install.wim" -SourceIndex $index -DestinationImagePath "$mountDir\sources\install2.wim" -CompressionType "Max"
948902
} catch {
949903
# Fall back to DISM with optimized settings
950904
dism /english /export-image /sourceimagefile="$mountDir\sources\install.wim" /sourceindex=$index /destinationimagefile="$mountDir\sources\install2.wim" /compress:fast /checkintegrity /verify /loglevel:1

functions/private/Copy-Files.ps1

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,9 @@ function Copy-Files {
4040
try {
4141
Copy-Item $file.FullName ($destination+$restpath) -ErrorAction Stop -Force:$force
4242

43-
# Use more robust method to remove ReadOnly attribute
44-
$copiedFile = Get-Item -Path ($destination+$restpath) -Force -ErrorAction SilentlyContinue
45-
if ($copiedFile -and ($copiedFile.Attributes -band [System.IO.FileAttributes]::ReadOnly)) {
46-
$copiedFile.Attributes = $copiedFile.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
47-
}
43+
44+
# Remove ReadOnly attribute using attrib for consistency
45+
& attrib -R ($destination+$restpath) 2>$null
4846

4947
# Force garbage collection to release file handles
5048
$copiedFile = $null
@@ -53,8 +51,8 @@ function Copy-Files {
5351
# Try alternative method if standard copy fails
5452
try {
5553
[System.IO.File]::Copy($file.FullName, ($destination+$restpath), $force)
56-
# Remove ReadOnly using .NET method
57-
[System.IO.File]::SetAttributes(($destination+$restpath), [System.IO.File]::GetAttributes(($destination+$restpath)) -band (-bnot [System.IO.FileAttributes]::ReadOnly))
54+
# Remove ReadOnly attribute using attrib for consistency
55+
& attrib -R ($destination+$restpath) 2>$null
5856
} catch {
5957
Write-Debug "Alternative copy method also failed: $($_.Exception.Message)"
6058
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function Get-LocalGroupNameFromSid {
2+
param (
3+
[Parameter(Mandatory, Position = 0)] [string] $sid
4+
)
5+
# You can fine-tune this to add error handling, but this should do the trick
6+
return (Get-LocalGroup | Where-Object { $_.SID.Value -like "$sid" }).Name
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
function Invoke-GarbageCollection {
2+
<#
3+
.SYNOPSIS
4+
Forces garbage collection to release file handles and free memory
5+
6+
.DESCRIPTION
7+
This function performs a complete garbage collection cycle to help release
8+
file handles that might be keeping files or directories locked.
9+
10+
.PARAMETER WaitSeconds
11+
Optional wait time after garbage collection (default: 0)
12+
13+
.EXAMPLE
14+
Invoke-GarbageCollection
15+
16+
.EXAMPLE
17+
Invoke-GarbageCollection -WaitSeconds 2
18+
#>
19+
param(
20+
[int]$WaitSeconds = 0
21+
)
22+
23+
try {
24+
[System.GC]::Collect()
25+
[System.GC]::WaitForPendingFinalizers()
26+
[System.GC]::Collect()
27+
28+
if ($WaitSeconds -gt 0) {
29+
Start-Sleep -Seconds $WaitSeconds
30+
}
31+
} catch {
32+
# Ignore GC errors - not critical
33+
}
34+
}

0 commit comments

Comments
 (0)