Private/Resolve-WUGenericErrors.ps1

function Resolve-WUGenericErrors {
    <#
    .SYNOPSIS
        Performs generic Windows Update service reset and repair operations.
 
    .DESCRIPTION
        Comprehensive Windows Update service remediation including service reset,
        cache cleanup, DLL re-registration, and network stack reset. This function
        implements the "nuclear option" for Windows Update service repair.
 
    .PARAMETER LogPath
        Path to the log file for detailed logging.
 
    .EXAMPLE
        $result = Resolve-WUGenericErrors -LogPath "C:\Logs\wu.log"
 
    .NOTES
        This function requires Administrator privileges.
        Returns an object with Success, ActionsPerformed, and RebootRequired properties.
         
        Performs comprehensive service reset including:
        - Windows Update service stop/start cycle
        - Cache folder reset (SoftwareDistribution, CatRoot2)
        - DLL re-registration
        - WinSock reset
        - Service security descriptor reset
    #>


    [CmdletBinding()]
    param(
        [string]$LogPath
    )

    # Initialize result object
    $result = [PSCustomObject]@{
        Success = $false
        RebootRequired = $false  # Will be set to true if WinSock reset succeeds
        ActionsPerformed = @()
        ErrorMessage = $null
        ServicesRestarted = 0
        DllsRegistered = 0
    }

    Write-WULog -Message "Starting generic Windows Update remediation" -LogPath $LogPath

    try {
        # Define Windows Update services
        $services = @('BITS', 'wuauserv', 'cryptsvc', 'msiserver')
        
        # Step 1: Stop Windows Update Services
        Write-WULog -Message "Step 1: Stopping Windows Update Services..." -LogPath $LogPath
        
        foreach ($service in $services) {
            try {
                $serviceObj = Get-Service -Name $service -ErrorAction SilentlyContinue
                if ($serviceObj) {
                    if ($serviceObj.Status -eq 'Running') {
                        Write-WULog -Message "Stopping service: $service" -LogPath $LogPath
                        Stop-Service -Name $service -Force -ErrorAction Stop -WarningAction SilentlyContinue
                        Write-WULog -Message "Stopped service: $service" -LogPath $LogPath
                    } else {
                        Write-WULog -Message "Service $service was already stopped" -LogPath $LogPath
                    }
                } else {
                    Write-WULog -Message "Service $service not found" -Level Warning -LogPath $LogPath
                }
            }
            catch {
                Write-WULog -Message "Failed to stop service $service`: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
            }
        }
        
        $result.ActionsPerformed += "Stopped Windows Update Services"

        # Step 2: Remove QMGR Data files
        Write-WULog -Message "Step 2: Removing QMGR Data files..." -LogPath $LogPath
        
        try {
            $qmgrPath = "$env:ProgramData\Microsoft\Network\Downloader"
            $qmgrFiles = Get-ChildItem -Path "$qmgrPath\qmgr*.dat" -ErrorAction SilentlyContinue
            
            if ($qmgrFiles) {
                $totalSize = ($qmgrFiles | Measure-Object -Property Length -Sum).Sum
                Remove-Item -Path $qmgrFiles.FullName -Force -ErrorAction SilentlyContinue
                Write-WULog -Message "Removed $($qmgrFiles.Count) QMGR data files ($([math]::Round($totalSize / 1KB, 1)) KB)" -LogPath $LogPath
                $result.ActionsPerformed += "Removed QMGR Data Files"
            } else {
                Write-WULog -Message "No QMGR data files found" -LogPath $LogPath
            }
        }
        catch {
            Write-WULog -Message "Error removing QMGR data files: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Step 3: Rename Software Distribution and CatRoot2 folders
        Write-WULog -Message "Step 3: Renaming Software Distribution and CatRoot2 folders..." -LogPath $LogPath
        
        try {
            # Handle SoftwareDistribution
            $sdPath = "$env:systemroot\SoftwareDistribution"
            $sdOldPath = "$env:systemroot\SoftwareDistribution.old"
            
            if (Test-Path $sdOldPath) {
                Write-WULog -Message "Removing existing SoftwareDistribution.old" -LogPath $LogPath
                Remove-Item -Path $sdOldPath -Recurse -Force -ErrorAction SilentlyContinue
            }
            
            if (Test-Path $sdPath) {
                Rename-Item -Path $sdPath -NewName "SoftwareDistribution.old" -ErrorAction Stop
                Write-WULog -Message "Renamed SoftwareDistribution folder" -LogPath $LogPath
            } else {
                Write-WULog -Message "SoftwareDistribution folder not found" -Level Warning -LogPath $LogPath
            }
            
            # Handle CatRoot2
            $catroot2Path = "$env:systemroot\System32\Catroot2"
            $catroot2OldPath = "$env:systemroot\System32\Catroot2.old"
            
            if (Test-Path $catroot2OldPath) {
                Write-WULog -Message "Removing existing Catroot2.old" -LogPath $LogPath
                Remove-Item -Path $catroot2OldPath -Recurse -Force -ErrorAction SilentlyContinue
            }
            
            if (Test-Path $catroot2Path) {
                Rename-Item -Path $catroot2Path -NewName "Catroot2.old" -ErrorAction Stop
                Write-WULog -Message "Renamed Catroot2 folder" -LogPath $LogPath
            } else {
                Write-WULog -Message "Catroot2 folder not found" -Level Warning -LogPath $LogPath
            }
            
            $result.ActionsPerformed += "Reset Windows Update Cache Folders"
        }
        catch {
            Write-WULog -Message "Error renaming cache folders: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Step 4: Reset Windows Update Services to default settings
        Write-WULog -Message "Step 4: Resetting Windows Update Services to default settings..." -LogPath $LogPath
        
        try {
            # Reset service security descriptors
            $securityCommands = @(
                @{ Service = 'bits'; Command = 'sc.exe sdset bits "D:(A;CI;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)"' },
                @{ Service = 'wuauserv'; Command = 'sc.exe sdset wuauserv "D:(A;;CCLCSWRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)"' }
            )
            
            foreach ($secCmd in $securityCommands) {
                try {
                    $output = Invoke-Expression $secCmd.Command 2>&1
                    if ($LASTEXITCODE -eq 0) {
                        Write-WULog -Message "Reset security descriptor for $($secCmd.Service)" -LogPath $LogPath
                    } else {
                        Write-WULog -Message "Failed to reset security descriptor for $($secCmd.Service): $output" -Level Warning -LogPath $LogPath
                    }
                }
                catch {
                    Write-WULog -Message "Error resetting security descriptor for $($secCmd.Service): $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                }
            }
            
            $result.ActionsPerformed += "Reset Service Security Descriptors"
        }
        catch {
            Write-WULog -Message "Error resetting service security descriptors: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Step 5: Re-register DLLs (modernized for Windows 10/11)
        Write-WULog -Message "Step 5: Re-registering DLLs..." -LogPath $LogPath
        
        try {
            # Determine OS build to decide registration strategy
            $osInfo = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -ErrorAction SilentlyContinue
            $buildNumber = [int]($osInfo.CurrentBuildNumber)
            
            # Legacy full list kept for older systems (pre Win10 / specialized scenarios)
            $legacyDlls = @(
                'atl.dll','urlmon.dll','mshtml.dll','shdocvw.dll','browseui.dll',
                'jscript.dll','vbscript.dll','scrrun.dll','msxml.dll','msxml3.dll',
                'msxml6.dll','actxprxy.dll','softpub.dll','wintrust.dll','dssenh.dll',
                'rsaenh.dll','gpkcsp.dll','sccbase.dll','slbcsp.dll','cryptdlg.dll',
                'oleaut32.dll','ole32.dll','shell32.dll','initpki.dll','wuapi.dll',
                'wuaueng.dll','wuaueng1.dll','wucltui.dll','wups.dll','wups2.dll',
                'wuweb.dll','qmgr.dll','qmgrprxy.dll','wucltux.dll','muweb.dll','wuwebv.dll'
            )
            
            # Curated modern list – only DLLs that typically still self-register successfully on Win10/11
            $modernDlls = @(
                'atl.dll','urlmon.dll','jscript.dll','vbscript.dll','scrrun.dll',
                'msxml3.dll','msxml6.dll','actxprxy.dll','softpub.dll','wintrust.dll',
                'dssenh.dll','rsaenh.dll','cryptdlg.dll','oleaut32.dll','ole32.dll',
                'shell32.dll','wups.dll','wups2.dll'
            )
            
            # Allow override via environment variable (WU_DLL_REG_MODE): Modern | Legacy | Skip
            $mode = if ($env:WU_DLL_REG_MODE) { $env:WU_DLL_REG_MODE.Trim().ToLower() } else { $null }
            switch ($mode) {
                'legacy' { $dlls = $legacyDlls; $modeLabel = 'Legacy (env override)'}
                'skip'   { $dlls = @();     $modeLabel = 'Skip (env override)' }
                'modern' { $dlls = $modernDlls; $modeLabel = 'Modern (env override)' }
                default {
                    if ($buildNumber -ge 22000) { # Windows 11
                        $dlls = $modernDlls; $modeLabel = 'Modern (auto Win11)'
                    } elseif ($buildNumber -ge 10240) { # Windows 10
                        $dlls = $modernDlls; $modeLabel = 'Modern (auto Win10)'
                    } else {
                        $dlls = $legacyDlls; $modeLabel = 'Legacy (auto pre-Win10)'
                    }
                }
            }
            
            # Ensure $dlls is never null
            if ($null -eq $dlls) {
                $dlls = @()
                $modeLabel = 'Empty (fallback)'
            }
            
            if ($dlls.Count -eq 0) {
                Write-WULog -Message "DLL registration skipped (Mode=$modeLabel)" -LogPath $LogPath
            } else {
                Write-WULog -Message "DLL registration mode: $modeLabel; Attempting to register $($dlls.Count) DLLs" -LogPath $LogPath
                Push-Location "$env:systemroot\system32"

                $registeredCount = 0
                $failedCount = 0
                $nonCriticalFailures = 0

                # Known legacy DLLs that are expected to fail on modern systems (suppress noisy warnings)
                $expectedFail = @('mshtml.dll','shdocvw.dll','browseui.dll','msxml.dll','gpkcsp.dll','sccbase.dll','slbcsp.dll','initpki.dll','wuaueng1.dll','wucltui.dll','wuweb.dll','qmgrprxy.dll','wucltux.dll','muweb.dll','wuwebv.dll')

                foreach ($dll in $dlls) {
                    try {
                        # Skip null or empty DLL names
                        if ([string]::IsNullOrWhiteSpace($dll)) {
                            Write-WULog -Message "Skipping null/empty DLL entry" -Level Warning -LogPath $LogPath
                            continue
                        }
                        
                        if (-not (Test-Path $dll)) {
                            $failedCount++
                            Write-WULog -Message "DLL not found: $dll" -Level Warning -LogPath $LogPath
                            continue
                        }
                        & regsvr32.exe $dll /s 2>&1 | Out-Null
                        if ($LASTEXITCODE -eq 0) {
                            $registeredCount++
                        } else {
                            $failedCount++
                            if ($expectedFail -contains $dll) {
                                $nonCriticalFailures++
                                Write-WULog -Message "Expected non-critical registration failure: $dll" -LogPath $LogPath
                            } else {
                                Write-WULog -Message "Failed to register $dll (ExitCode=$LASTEXITCODE)" -Level Warning -LogPath $LogPath
                            }
                        }
                    }
                    catch {
                        $failedCount++
                        if ($expectedFail -contains $dll) {
                            $nonCriticalFailures++
                            Write-WULog -Message "Expected non-critical exception registering ${dll}: $($_.Exception.Message)" -LogPath $LogPath
                        } else {
                            Write-WULog -Message "Error registering ${dll}: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                        }
                    }
                }

                Write-WULog -Message "DLL registration summary: Registered=$registeredCount Failed=$failedCount (ExpectedNonCritical=$nonCriticalFailures) Mode=$modeLabel" -LogPath $LogPath
                $result.DllsRegistered = $registeredCount
                $result.ActionsPerformed += "Re-registered Windows Update DLLs ($modeLabel)"
                Pop-Location
            }
        }
        catch {
            Write-WULog -Message "Error during DLL registration phase: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
            try { Pop-Location } catch { }
        }

        # Step 6: Reset WinSock
        Write-WULog -Message "Step 6: Resetting WinSock..." -LogPath $LogPath
        
        try {
            $winsockOutput = & netsh winsock reset 2>&1
            if ($LASTEXITCODE -eq 0) {
                Write-WULog -Message "WinSock reset completed successfully - reboot required" -LogPath $LogPath
                $result.RebootRequired = $true
                $result.ActionsPerformed += "WinSock Reset"
            } else {
                Write-WULog -Message "WinSock reset failed: $winsockOutput" -Level Warning -LogPath $LogPath
            }
        }
        catch {
            Write-WULog -Message "Failed to reset WinSock: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Step 7: Start Windows Update Services
        Write-WULog -Message "Step 7: Starting Windows Update Services..." -LogPath $LogPath
        
        foreach ($service in $services) {
            try {
                $serviceObj = Get-Service -Name $service -ErrorAction SilentlyContinue
                if ($serviceObj) {
                    if ($serviceObj.Status -ne 'Running') {
                        Write-WULog -Message "Starting service: $service" -LogPath $LogPath
                        Start-Service -Name $service -ErrorAction Stop
                        Write-WULog -Message "Started service: $service" -LogPath $LogPath
                        $result.ServicesRestarted++
                    } else {
                        Write-WULog -Message "Service $service was already running" -LogPath $LogPath
                    }
                } else {
                    Write-WULog -Message "Service $service not found" -Level Warning -LogPath $LogPath
                }
            }
            catch {
                Write-WULog -Message "Failed to start service $service`: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
            }
        }
        
        $result.ActionsPerformed += "Restarted Windows Update Services"

        # Step 8: Force Windows Update discovery
        Write-WULog -Message "Step 8: Forcing Windows Update discovery..." -LogPath $LogPath
        
        try {
            $usoclientOutput = & USOClient.exe StartInteractiveScan 2>&1
            if ($LASTEXITCODE -eq 0) {
                Write-WULog -Message "Windows Update discovery initiated successfully" -LogPath $LogPath
                $result.ActionsPerformed += "Initiated Windows Update Discovery"
            } else {
                Write-WULog -Message "Failed to start update discovery: $usoclientOutput" -Level Warning -LogPath $LogPath
            }
        }
        catch {
            Write-WULog -Message "Failed to start update discovery: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Step 9: Additional cleanup - Remove temporary internet files and Windows Update logs
        Write-WULog -Message "Step 9: Additional cleanup..." -LogPath $LogPath
        
        try {
            # Clean Windows Update logs that might be corrupted
            $wuLogPaths = @(
                "$env:systemroot\WindowsUpdate.log",
                "$env:systemroot\SoftwareDistribution\ReportingEvents.log"
            )
            
            foreach ($logPath in $wuLogPaths) {
                if (Test-Path $logPath) {
                    try {
                        Remove-Item -Path $logPath -Force -ErrorAction Stop
                        Write-WULog -Message "Removed potentially corrupted log: $logPath" -LogPath $LogPath
                    }
                    catch {
                        Write-WULog -Message "Could not remove log file $logPath`: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
                    }
                }
            }
            
            $result.ActionsPerformed += "Cleaned Windows Update Logs"
        }
        catch {
            Write-WULog -Message "Error during additional cleanup: $($_.Exception.Message)" -Level Warning -LogPath $LogPath
        }

        # Determine overall success
        if ($result.ActionsPerformed.Count -gt 0) {
            $result.Success = $true
        }

    }
    catch {
        $result.ErrorMessage = $_.Exception.Message
        Write-WULog -Message "Critical error during generic Windows Update remediation: $($_.Exception.Message)" -Level Error -LogPath $LogPath
    }

    # Summary
    if ($result.Success) {
        Write-WULog -Message "Generic Windows Update remediation completed successfully" -LogPath $LogPath
        Write-WULog -Message "Actions performed:" -LogPath $LogPath
        foreach ($action in $result.ActionsPerformed) {
            Write-WULog -Message " - $action" -LogPath $LogPath
        }
        
        Write-WULog -Message "Services restarted: $($result.ServicesRestarted)" -LogPath $LogPath
        Write-WULog -Message "DLLs registered: $($result.DllsRegistered)" -LogPath $LogPath
        
        if ($result.RebootRequired) {
            Write-WULog -Message "REBOOT REQUIRED: WinSock reset requires system restart" -Level Warning -LogPath $LogPath
        }
    } else {
        Write-WULog -Message "Generic Windows Update remediation failed: $($result.ErrorMessage)" -Level Error -LogPath $LogPath
    }

    return $result
}