C:\Windows\WinSxS\Temp\InFlight\ Deleting Empty Folders


Updates & running dism gives no errors. Also no eventvwr errors.

While doing updates on that testmachine, that was 2months behind on updates.
Did the updates, rebooted. Ran DISM /Online /Cleanup-Image /StartComponentCleanup and DISM /Online /Cleanup-Image /StartComponentCleanup /resetbase
rebooted again.
Then checked again for empty folders. There where 6346 new empty folders. I guess if this happens every month, then after 2years you get the 100k empty folder i see on some machines.

Reverted vm snapshot back to orginal state.
Took a shot with: @MyITGuy MyITGuy/Invoke-WinSxSTempCleanup.psm1

MyITGuy's is a few minutes faster in changing access rights and deleting folders.
But his script does not check if folders are empty, so deletes also the files inside some of the temp folders as well.
This is not my goal, as windows will release them by it self. (No where i found old folders with files in it, only from within the last few weeks.)

He has defined PendingDeletes and PendingRenames, but only writes a line, that there are files, and does nothing with it.

I will keep his script, as it might be a handy working example to change access rights in situations where the other take ownership scripts not work.

Made this script, so you can give up the path to scan to delete folders.
After the question -> Would you like to delete empty folders recursively until none are left? (yes/no)
It will delete all empty folders that are found.

Adjust scanPath with fixed path and adjust deleteEmptyFolders to Yes, to run this script automated as a scheduled task. Or create a function of it, to intergrate into excisting scripts that runs on your pc or networks.

Feedback is welcome!

Powershell:
# Prompt user
$scanPath = Read-Host "Enter the path to scan (e.g. C:\Windows\WinSxS\Temp\InFlight)"

if (!(Test-Path -Path $scanPath)) {
    Write-Host "The entered path does not exist. Please try again."
    exit
}

$deleteEmptyFolders = Read-Host "Would you like to delete empty folders recursively until none are left? (yes/no)"
$totalDeletedFolderCount = 0

if ($deleteEmptyFolders -eq "yes") {
    do {
        $emptyFolders = Get-ChildItem -Path $scanPath -Recurse -Directory | Where-Object { (Get-ChildItem -Path $_.FullName).Count -eq 0 }
        if ($emptyFolders.Count -eq 0) {
            Write-Host "No more empty folders found."
            break
        }
        $deletedFolderCount = 0
        foreach ($folder in $emptyFolders) {
            try {
                takeown /f "$($folder.FullName)" /r /d y
                icacls "$($folder.FullName)" /grant administrators:F /t
            }
            catch {
                Write-Warning "Failed to take ownership or grant permissions for $($folder.FullName).  Error: $($_.Exception.Message)"
                continue
            }
            # remove the folder
            try {
                Remove-Item -Path "$($folder.FullName)" -Recurse -Force -Confirm:$false
                $deletedFolderCount++
            }
            catch {
                Write-Warning "Failed to delete folder $($folder.FullName). Error: $($_.Exception.Message)"
            }
        }
        $totalDeletedFolderCount += $deletedFolderCount
    } while ($true)
    Write-Host "Total empty folders deleted across all iterations: $totalDeletedFolderCount"
} else {
    Write-Host "Empty folders not deleted."
}
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
took me 14 sec
Screenshot 2025-02-14 130207.webp

There is a problem with these before and after 's in that windows indexing has remembered a lot of what it does the second time. Like if I go back and do the above, the answer is instant since it already knows.
 

My Computers

System One System Two

  • OS
    win 11Pro, 24H2 current-1mo, build a/o 2-13-25: 26100.3194
    Computer type
    PC/Desktop
    Manufacturer/Model
    DIY
    CPU
    I9-13900k
    Motherboard
    ASUS ROG STRIX Z790-E Gam wifi-6E, latest AMI BIOS 2801
    Memory
    G.Skill Ripjaws S5 32GB Kit DDR5 6000. 6000.
    Graphics Card(s)
    GeForce RTX™ 3060 Ti VISION OC 8G (rev. 2.0)
    Sound Card
    "Realtek USB Audio", ROG SupremeFX 7.1 Surround Sound High Definition Audio CODEC ALC4080, Bose Computer Music Monitor/ Desktop Computer Speakers
    Monitor(s) Displays
    Dell S3221QS
    Screen Resolution
    4K but I usually display at 1K
    Hard Drives
    Sam 980 Pro 1T M.2, 990Pro, WD 570, others(all NVMe)
    PSU
    Corsair RM750e fully modular
    Case
    CoolerMaster TD500 mesh white case
    Cooling
    CPU:Thermalright aio A-E-360 V4 TOP(Thermalright anti-bend plate), CASE:3 front fans IN, 1 back fan OUT.
    Keyboard
    Corsair K55 Core RGB
    Mouse
    Corsair Harpoon RGB wireless
    Internet Speed
    220Mbs Starry I226-V
    Browser
    Firefox current, Tab center Reborn, etc etc etc.
    Antivirus
    BitDefender AV Free
    Other Info
    WuMgr, StartAllBack, Terabyte Image for Win, Revo Uninstaller Pro Portable.
    Time Spy Extreme 6239, VRMark Orange Room 16430, etc.
    Been doing computers since '62(IBM 7070/4(just missed their 650!!!). Managed a bunch of 360's. My first PC I think was one of the RS puters (I tried them all), a commodore, an IBM PC w DOS 3.11, and others mainly DIY (COMPUTER SHOPPER!!!) w 95, 98, xp, vista, me, 7!!!, 8, 10!!!, 11.
    I use Wondershare Filmora Basic to process my vlogs. Pocket 2
  • Operating System
    win 10, Mint
    Computer type
    Laptop
    Manufacturer/Model
    Lenovo P15s
    CPU
    i7-10510U 1.8-4.9GHz
    Motherboard
    Intel SoC
    Memory
    16GB DDR4
    Graphics card(s)
    Nvidia Quadro P520 2GB DDR5 supports 4K external via HDMI or USB-C
    Sound Card
    Realtek ALC3287 HD
    Monitor(s) Displays
    15.6"
    Screen Resolution
    1K
    Hard Drives
    NVMe various
One new question would be; what to do with files like this in the PendingDeletes:
Because on some pc's there are about 300 to 1gb of pending deletes, that date back more then a year.
1739556082636.webp
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
I recommend 2 things - do an occasional (twice: plain and system) ControlPanel/windows tools/Disk cleanup.

do an occasional defrag.even on an ssd. I alternate between Ultra and defraggler. Does make things cleaner/faster.


I definitely would NOT manually delete anything in there. You might luck out but you might well wind up with a problem that .........
 

My Computers

System One System Two

  • OS
    win 11Pro, 24H2 current-1mo, build a/o 2-13-25: 26100.3194
    Computer type
    PC/Desktop
    Manufacturer/Model
    DIY
    CPU
    I9-13900k
    Motherboard
    ASUS ROG STRIX Z790-E Gam wifi-6E, latest AMI BIOS 2801
    Memory
    G.Skill Ripjaws S5 32GB Kit DDR5 6000. 6000.
    Graphics Card(s)
    GeForce RTX™ 3060 Ti VISION OC 8G (rev. 2.0)
    Sound Card
    "Realtek USB Audio", ROG SupremeFX 7.1 Surround Sound High Definition Audio CODEC ALC4080, Bose Computer Music Monitor/ Desktop Computer Speakers
    Monitor(s) Displays
    Dell S3221QS
    Screen Resolution
    4K but I usually display at 1K
    Hard Drives
    Sam 980 Pro 1T M.2, 990Pro, WD 570, others(all NVMe)
    PSU
    Corsair RM750e fully modular
    Case
    CoolerMaster TD500 mesh white case
    Cooling
    CPU:Thermalright aio A-E-360 V4 TOP(Thermalright anti-bend plate), CASE:3 front fans IN, 1 back fan OUT.
    Keyboard
    Corsair K55 Core RGB
    Mouse
    Corsair Harpoon RGB wireless
    Internet Speed
    220Mbs Starry I226-V
    Browser
    Firefox current, Tab center Reborn, etc etc etc.
    Antivirus
    BitDefender AV Free
    Other Info
    WuMgr, StartAllBack, Terabyte Image for Win, Revo Uninstaller Pro Portable.
    Time Spy Extreme 6239, VRMark Orange Room 16430, etc.
    Been doing computers since '62(IBM 7070/4(just missed their 650!!!). Managed a bunch of 360's. My first PC I think was one of the RS puters (I tried them all), a commodore, an IBM PC w DOS 3.11, and others mainly DIY (COMPUTER SHOPPER!!!) w 95, 98, xp, vista, me, 7!!!, 8, 10!!!, 11.
    I use Wondershare Filmora Basic to process my vlogs. Pocket 2
  • Operating System
    win 10, Mint
    Computer type
    Laptop
    Manufacturer/Model
    Lenovo P15s
    CPU
    i7-10510U 1.8-4.9GHz
    Motherboard
    Intel SoC
    Memory
    16GB DDR4
    Graphics card(s)
    Nvidia Quadro P520 2GB DDR5 supports 4K external via HDMI or USB-C
    Sound Card
    Realtek ALC3287 HD
    Monitor(s) Displays
    15.6"
    Screen Resolution
    1K
    Hard Drives
    NVMe various
Powershell:
        $emptyFolders = Get-ChildItem -Path $scanPath -Recurse -Directory | Where-Object { (Get-ChildItem -Path $_.FullName).Count -eq 0 }
}
The second Get-ChildItem should have a -Force here, because if the folder has hidden files, a regular Get-ChildItem will not see them and think the folder is empty.
 

My Computers

System One System Two

  • OS
    Windows 11 Pro 24H2
    Computer type
    PC/Desktop
    Manufacturer/Model
    Intel NUC12WSHi7
    CPU
    12th Gen Intel Core i7-1260P, 2100 MHz
    Motherboard
    NUC12WSBi7
    Memory
    64 GB
    Graphics Card(s)
    Intel Iris Xe
    Sound Card
    built-in Realtek HD audio
    Monitor(s) Displays
    Dell U3219Q
    Screen Resolution
    3840x2160 @ 60Hz
    Hard Drives
    Samsung SSD 990 PRO 1TB
    Keyboard
    CODE 104-Key Mechanical with Cherry MX Clears
    Antivirus
    Microsoft Defender
  • Operating System
    Linux Mint 21.2 (Cinnamon)
    Computer type
    PC/Desktop
    Manufacturer/Model
    Intel NUC8i5BEH
    CPU
    Intel Core i5-8259U CPU @ 2.30GHz
    Memory
    32 GB
    Graphics card(s)
    Iris Plus 655
    Keyboard
    CODE 104-Key Mechanical with Cherry MX Clears
Powershell:
# Prompt user
$scanPath = Read-Host "Enter the path to scan (e.g. C:\Windows\WinSxS\Temp\InFlight)"

if (!(Test-Path -Path $scanPath)) {
    Write-Host "The entered path does not exist. Please try again."
    exit
}
Seriously don't do this. This script should decide which folders to scan.

What you don't want happening is someone less knowledgeable typing in "C:\Windows\WinSxS" or another path. I have seen one admin declare he simply nukes everything under WinSxS\Temp. If you want to hardcode that into the script, fine.

Calling takeown & icacls on a per-folder instance is a performance hit since you're invoking two EXE processes which have to run (over and over). The other script does everything in-line (because it self-elevates) so it's faster in that regard.
 

My Computer

System One

  • OS
    Windows 7
Like this?
Powershell:
 $emptyFolders = Get-ChildItem -Path $scanPath -Recurse -Directory | Where-Object { (Get-ChildItem -Path $_.FullName -Force).Count -eq 0 }
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
Adjusted with all feedback, new version:
Hope i did not adjust to much access rights, but will recheck after some food.

Powershell:
  function Invoke-WinSxSDeleteEmtpyInFlightFolders {
    [CmdletBinding()]
    param(
        [string[]]
        $ScanID
    )

    begin {
        #region Enable Privilege function
        function Enable-Privilege {
            param(
                ## The privilege to adjust. This set is taken from
                ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
                [ValidateSet(
                    "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
                    "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
                    "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
                    "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
                    "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
                    "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
                    "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
                    "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
                    "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
                    "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
                    "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
                $Privilege,
                ## The process on which to adjust the privilege. Defaults to the current process.
                $ProcessId = $pid,
                ## Switch to disable the privilege, rather than enable it.
                [Switch] $Disable
            )
  
            ## Taken from P/Invoke.NET with minor adjustments.
            $definition = @'
using System;
using System.Runtime.InteropServices;
 
    public class AdjPriv
{
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
    ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
 
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
    [DllImport("advapi32.dll", SetLastError = true)]
    internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct TokPriv1Luid
    {
    public int Count;
    public long Luid;
    public int Attr;
    }
 
    internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
    {
    bool retVal;
    TokPriv1Luid tp;
    IntPtr hproc = new IntPtr(processHandle);
    IntPtr htok = IntPtr.Zero;
    retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
    tp.Count = 1;
    tp.Luid = 0;
    if(disable)
    {
    tp.Attr = SE_PRIVILEGE_DISABLED;
    }
    else
    {
    tp.Attr = SE_PRIVILEGE_ENABLED;
    }
    retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
    retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
    return retVal;
    }
}
'@
  
            $processHandle = (Get-Process -id $ProcessId).Handle
            $typeexists = try { ([AdjPriv] -is [type]); $true } catch { $false }
            if ( $typeexists -eq $false ) {
                $type = Add-Type $definition -PassThru
            }
            [AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable)
        }
        #endregion Enable Privilege function
    }

process {
    try {
        do {
            # Find empty folders
            $emptyFolders = Get-ChildItem -Path $env:windir\WinSxS\Temp\InFlight -Recurse -Directory | Where-Object { (Get-ChildItem -Path $_.FullName -Force).Count -eq 0 }

            # If no empty folders are found, exit the loop
            if ($emptyFolders.Count -eq 0) {
                Write-Host "No more empty folders found."
                break
            }

            # Initialize a counter for deleted folders in THIS ITERATION
            $deletedFolderCount = 0

            # Check for Admin rights
            if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
                Write-Warning "Must be run with administrator credentials"
                return
            }

            # Add SeTakeOwnershipPrivilege for this process
            Enable-Privilege -Privilege SeTakeOwnershipPrivilege | Out-Null
            # Add SeRestorePrivilege for this process
            Enable-Privilege -Privilege SeRestorePrivilege | Out-Null

            $NTAccount_Administrators = [System.Security.Principal.NTAccount]"Administrators"

            foreach ($emptyInFlightFolder In $emptyFolders) {
                #region Change ownership of the root folder
                try {
                    $acl = Get-Acl -Path $emptyInFlightFolder.FullName
                    $acl.SetOwner($NTAccount_Administrators)
                    Set-Acl -Path $emptyInFlightFolder.FullName -AclObject $acl
                    Write-Host "Changed owner on '$($emptyInFlightFolder.FullName)' to '$($NTAccount_Administrators)'..."
                }
                catch {
                    Write-Warning "Failed to change owner on folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if ownership change fails
                }
                #endregion

                #region Add Administrators Full Control
                try {
                    $acl = Get-Acl -Path $emptyInFlightFolder.FullName
                    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ($NTAccount_Administrators, [System.Security.AccessControl.FileSystemRights]::FullControl, @("ObjectInherit", "ContainerInherit"), "None", "Allow")
                    $acl.AddAccessRule($accessRule)
                    Set-Acl -Path $emptyInFlightFolder.FullName -AclObject $acl
                    Write-Host "[Folder] Added Administrators with full control on '$($emptyInFlightFolder.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Cyan
                }
                catch {
                    Write-Warning "Failed to add full control permissions to folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if adding permissions fails
                }
                #endregion

                # Attempt to remove the folder
                try {
                    Remove-Item -Path "$($emptyInFlightFolder.FullName)" -Recurse -Force -Confirm:$false
                    $deletedFolderCount++ # Increment the counter if deletion is successful
                    Write-Host "Successfully deleted folder: $($emptyInFlightFolder.FullName)"
                }
                catch {
                    Write-Warning "Failed to delete folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                }
            }
            # Add the number of folders deleted in this iteration to the total
            $totalDeletedFolderCount += $deletedFolderCount
        } while ($true) # Infinite loop that breaks when no empty folders are found
        Write-Host "Total empty folders deleted across all iterations: $totalDeletedFolderCount"  # Display total at the end
    }
    catch {
        Write-Host "An error occurred: $($_.Exception.Message)" -ForegroundColor Red
        throw $_
    }
}

end {}
}

Invoke-Command -ScriptBlock { Invoke-WinSxSDeleteEmtpyInFlightFolders }
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
1. Move this block to the top of the try {}. There's no point proceeding if you're not Admin, or the privilege elevation fails.

Don't redirect Enable-Privilege's return value to NULL, you should be checking for a failed result.
Code:
try {
            # Check for Admin rights
            if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
                Write-Warning "Must be run with administrator credentials"
                return
            }

            # Add SeTakeOwnershipPrivilege for this process
            Enable-Privilege -Privilege SeTakeOwnershipPrivilege | Out-Null
            # Add SeRestorePrivilege for this process
            Enable-Privilege -Privilege SeRestorePrivilege | Out-Null

            $NTAccount_Administrators = [System.Security.Principal.NTAccount]"Administrators"

2. Get-Item -Force is prolly redundant. None of the folders are marked hidden.
 

My Computer

System One

  • OS
    Windows 7
Well this is the result on one other test system, deleted over 84943 empty folders.
1739567104600.webp

Update on the code with feedback from @garlin, only did not remove the -Force for now.
And was retested on test system.

Powershell:
  function Invoke-WinSxSDeleteEmtpyInFlightFolders {
    [CmdletBinding()]
    param(
        [string[]]
        $ScanID
    )

    begin {
        #region Enable Privilege function
        function Enable-Privilege {
            param(
                ## The privilege to adjust. This set is taken from
                ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
                [ValidateSet(
                    "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
                    "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
                    "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
                    "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
                    "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
                    "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
                    "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
                    "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
                    "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
                    "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
                    "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
                $Privilege,
                ## The process on which to adjust the privilege. Defaults to the current process.
                $ProcessId = $pid,
                ## Switch to disable the privilege, rather than enable it.
                [Switch] $Disable
            )
   
            ## Taken from P/Invoke.NET with minor adjustments.
            $definition = @'
using System;
using System.Runtime.InteropServices;
 
public class AdjPriv
{
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
    ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
 
    [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
    internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
    [DllImport("advapi32.dll", SetLastError = true)]
    internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct TokPriv1Luid
    {
    public int Count;
    public long Luid;
    public int Attr;
    }
 
    internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
    {
        bool retVal;
        TokPriv1Luid tp;
        IntPtr hproc = new IntPtr(processHandle);
        IntPtr htok = IntPtr.Zero;
        retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
        if (!retVal) return false;
        tp.Count = 1;
        tp.Luid = 0;
        tp.Attr = disable ? SE_PRIVILEGE_DISABLED : SE_PRIVILEGE_ENABLED;
        retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
        if (!retVal) return false;
        retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
        return retVal;
    }
}
'@
   
            $processHandle = (Get-Process -id $ProcessId).Handle
            $typeexists = try { ([AdjPriv] -is [type]); $true } catch { $false }
            if ( $typeexists -eq $false ) {
                $type = Add-Type $definition -PassThru
            }
            $result = [AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable)
            if (-not $result) {
                $errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
                throw "Failed to change privilege '$Privilege'. Error code: $errorCode."
            }
        }
        #endregion Enable Privilege function
    }

process {
    try {
        # Check for Admin rights
        if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
            Write-Warning "Must be run with administrator credentials"
            return
        }

        try {
            # Add SeTakeOwnershipPrivilege and SeRestorePrivilege for this process
            Enable-Privilege -Privilege SeTakeOwnershipPrivilege
            Enable-Privilege -Privilege SeRestorePrivilege
        }
        catch {
            Write-Error $_.Exception.Message
            return
        }

        do {
            # Find empty folders
            $emptyFolders = Get-ChildItem -Path $env:windir\WinSxS\Temp\InFlight -Recurse -Directory | Where-Object { (Get-ChildItem -Path $_.FullName -Force).Count -eq 0 }

            # If no empty folders are found, exit the loop
            if ($emptyFolders.Count -eq 0) {
                Write-Host "No more empty folders found."
                break
            }

            # Initialize a counter for deleted folders in THIS ITERATION
            $deletedFolderCount = 0

            $NTAccount_Administrators = [System.Security.Principal.NTAccount]"Administrators"

            foreach ($emptyInFlightFolder In $emptyFolders) {
                #region Change ownership of the root folder
                try {
                    $acl = Get-Acl -Path $emptyInFlightFolder.FullName
                    $acl.SetOwner($NTAccount_Administrators)
                    Set-Acl -Path $emptyInFlightFolder.FullName -AclObject $acl
                    Write-Host "Changed owner on '$($emptyInFlightFolder.FullName)' to '$($NTAccount_Administrators)'..."
                }
                catch {
                    Write-Warning "Failed to change owner on folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if ownership change fails
                }
                #endregion

                #region Add Administrators Full Control on the folder
                try {
                    $acl = Get-Acl -Path $emptyInFlightFolder.FullName
                    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule ($NTAccount_Administrators, [System.Security.AccessControl.FileSystemRights]::FullControl, @("ObjectInherit", "ContainerInherit"), "None", "Allow")
                    $acl.AddAccessRule($accessRule)
                    Set-Acl -Path $emptyInFlightFolder.FullName -AclObject $acl
                    Write-Host "[Folder] Added Administrators with full control on '$($emptyInFlightFolder.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Cyan
                }
                catch {
                    Write-Warning "Failed to add full control permissions to folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if adding permissions fails
                }
                #endregion

                # Attempt to remove the folder
                try {
                    Remove-Item -Path "$($emptyInFlightFolder.FullName)" -Recurse -Force -Confirm:$false
                    $deletedFolderCount++ # Increment the counter if deletion is successful
                    Write-Host "Successfully deleted folder: $($emptyInFlightFolder.FullName)"
                }
                catch {
                    Write-Warning "Failed to delete folder $($emptyInFlightFolder.FullName). Error: $($_.Exception.Message)"
                }
            }
            # Add the number of folders deleted in this iteration to the total
            $totalDeletedFolderCount += $deletedFolderCount
        } while ($true) # Infinite loop that breaks when no empty folders are found
        Write-Host "Total empty folders deleted across all iterations: $totalDeletedFolderCount"  # Display total at the end
    }
    catch {
        Write-Host "An error occurred: $($_.Exception.Message)" -ForegroundColor Red
        throw $_
    }
}

end {}
}

Invoke-Command -ScriptBlock { Invoke-WinSxSDeleteEmtpyInFlightFolders }
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
For morbid curiousity, check each deleted folder's LastWriteTime and report the oldest entry you come across. That would give better context into why 85,000 folders were purged. Machine was around for 2-3 years? 1 year?
 

My Computer

System One

  • OS
    Windows 7
That machine about 1year and 6months.
Had checked the folders before, and those last write times where between the system was installed and now. With an increase of about every 14/15days between groups of folders. So that looks to me about every windows update, the normal windows update and feature updates.

When the folder is nearly empty, and you keep the folder open in view, then at random times, you can see a folder being created, and some time later it gets deleted. So some folders do get deleted...

Will have to check other machines later.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop

Latest Support Threads

Back
Top Bottom