C:\Windows\assembly\NativeImages_v*\Temp Delete Emtpy Folders


Tester

Well-known member
Member
Local time
6:49 AM
Posts
248
OS
Windows 11
After searching empty folders that are not deleted by windows, this is one more locations where a lot of empty folders are created and not used anymore.
So based on this topic C:\Windows\WinSxS\Temp\InFlight\ Deleting Empty Folders
I created a powershell script to also delete the Empty Folders from the Temp Folders from these locations:
c:\Windows\assembly\NativeImages_v2.0.50727_32\Temp\
c:\Windows\assembly\NativeImages_v2.0.50727_64\Temp\
c:\Windows\assembly\NativeImages_v4.0.30319_32\Temp\
c:\Windows\assembly\NativeImages_v4.0.30319_64\Temp\

For me this deleted, about 1000empty folders in above locations.

Let me know if there are more paths for NativeImages_v*/Temp/ then i will add them to the script.

Feedback is welcome.

Powershell:
function Invoke-AssemblyNativeImagesDeleteEmptyTempFolders {
    [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
        }

        # Array of paths to check
        $pathsToCheck = @(
            "$env:windir\assembly\NativeImages_v2.0.50727_32\Temp",
            "$env:windir\assembly\NativeImages_v2.0.50727_64\Temp",
            "$env:windir\assembly\NativeImages_v4.0.30319_32\Temp",
            "$env:windir\assembly\NativeImages_v4.0.30319_64\Temp"
        )

        # Initialize an empty array to store empty folders
        $emptyFolders = @()
        $totalDeletedFolderCount = 0 # Initialize the total deleted count

        do {
            # Clear the empty folders array at the beginning of each loop iteration
            $emptyFolders = @()

            # Find empty folders
            foreach ($path in $pathsToCheck) {
                # Check if the path exists.
                if (Test-Path -Path $path -PathType Container) {
                    try {
                        $addToEmptyFolders = Get-ChildItem -Path $path -Recurse -Directory | Where-Object {
                            (Get-ChildItem -Path $_.FullName -Force | Measure-Object).Count -eq 0
                        }
                        # Add the results to the combined array
                        $emptyFolders += $addToEmptyFolders
                    }
                    catch {
                        Write-Error "Error processing path '$path': $($_.Exception.Message)"
                    }
                } else {
                    Write-Warning "The directory '$path' does not exist.  Skipping."
                }
            }
            # 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 ($EmptyTempFolders in $emptyFolders) {

                #region Add Administrators Full Control on the folder
                try {
                    # Check if the folder still exists before attempting to get ACL.
                    if (Test-Path -Path $EmptyTempFolders.FullName -PathType Container) {
                        $acl = Get-Acl -Path $EmptyTempFolders.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 $EmptyTempFolders.FullName -AclObject $acl
                        Write-Host "[Folder] Added Administrators with full control on '$($EmptyTempFolders.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Cyan
                    }
                    else {
                        Write-Warning "Folder $($EmptyTempFolders.FullName) no longer exists. Skipping ACL modification."
                        continue # Skip to the next folder if it doesn't exist
                    }
                }
                catch {
                    Write-Warning "Failed to add full control permissions to folder $($EmptyTempFolders.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if adding permissions fails
                }
                #endregion

                # Attempt to remove the folder
                try {
                    if (Test-Path -Path $EmptyTempFolders.FullName -PathType Container) {
                        Remove-Item -Path "$($EmptyTempFolders.FullName)" -Recurse -Force -Confirm:$false
                        $deletedFolderCount++ # Increment the counter if deletion is successful
                        Write-Host "Successfully deleted folder: $($EmptyTempFolders.FullName)"
                    } else {
                        Write-Warning "Folder $($EmptyTempFolders.FullName) no longer exists. Skipping deletion."
                    }
                }
                catch {
                    Write-Warning "Failed to delete folder $($EmptyTempFolders.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-AssemblyNativeImagesDeleteEmptyTempFolders }
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
New version:

Added: -Verbose option
Bug, if 0 files detected, this was not shown, adjusted the message given.


Powershell:
# Save as AssemblyNativeImagesDeleteEmptyTempFolders.ps1
# Run with .\AssemblyNativeImagesDeleteEmptyTempFolders.ps1
# To See the all output in the console, run with .\AssemblyNativeImagesDeleteEmptyTempFolders.ps1 -Verbose
# Report issus with this script at: https://www.elevenforum.com/t/c-windows-assembly-nativeimages_v-temp-delete-emtpy-folders.33461/
# Version 0.2
param(
    [switch]
    $Verbose
)

function Invoke-AssemblyNativeImagesDeleteEmptyTempFolders {
    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
        }

        # Array of paths to check
        $pathsToCheck = @(
            "$env:windir\assembly\NativeImages_v2.0.50727_32\Temp",
            "$env:windir\assembly\NativeImages_v2.0.50727_64\Temp",
            "$env:windir\assembly\NativeImages_v4.0.30319_32\Temp",
            "$env:windir\assembly\NativeImages_v4.0.30319_64\Temp"
        )

        # Initialize an empty array to store empty folders
        $emptyFolders = @()
        $totalDeletedFolderCount = 0

        do {
            # Clear the empty folders array at the beginning of each loop iteration
            $emptyFolders = @()

            # Find empty folders
            foreach ($path in $pathsToCheck) {
                # Check if the path exists.
                if (Test-Path -Path $path -PathType Container) {
                    try {
                        $addToEmptyFolders = Get-ChildItem -Path $path -Recurse -Directory | Where-Object {
                            (Get-ChildItem -Path $_.FullName -Force | Measure-Object).Count -eq 0
                        }
                        # Add the results to the combined array
                        $emptyFolders += $addToEmptyFolders
                    }
                    catch {
                        Write-Error "Error processing path '$path': $($_.Exception.Message)"
                    }
                } else {
                    Write-Warning "The directory '$path' does not exist.  Skipping."
                }
            }
            # If no empty folders are found, exit the loop
            if ($emptyFolders.Count -eq 0) {
                break
            }

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

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

            foreach ($EmptyTempFolders in $emptyFolders) {
                #region Add Administrators Full Control on the folder
                try {
                    # Check if the folder still exists before attempting to get ACL.
                    if (Test-Path -Path $EmptyTempFolders.FullName -PathType Container) {
                        $acl = Get-Acl -Path $EmptyTempFolders.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 $EmptyTempFolders.FullName -AclObject $acl
                        if ($Verbose) { Write-Host "[Folder] Added Administrators with full control on '$($EmptyTempFolders.FullName)' to '$($NTAccount_Administrators)'..." -ForegroundColor Cyan }
                    }
                    else {
                        Write-Warning "Folder $($EmptyTempFolders.FullName) no longer exists. Skipping ACL modification."
                        continue # Skip to the next folder if it doesn't exist
                    }
                }
                catch {
                    Write-Warning "Failed to add full control permissions to folder $($EmptyTempFolders.FullName). Error: $($_.Exception.Message)"
                    continue # Skip to the next folder if adding permissions fails
                }
                #endregion

                # Attempt to remove the folder
                try {
                    if (Test-Path -Path $EmptyTempFolders.FullName -PathType Container) {
                        Remove-Item -Path "$($EmptyTempFolders.FullName)" -Recurse -Force -Confirm:$false
                        $deletedFolderCount++ # Increment the counter if deletion is successful
                        if ($Verbose) { Write-Host "Successfully deleted folder: $($EmptyTempFolders.FullName)" }
                    } else {
                        Write-Warning "Folder $($EmptyTempFolders.FullName) no longer exists. Skipping deletion."
                    }
                }
                catch {
                    Write-Warning "Failed to delete folder $($EmptyTempFolders.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
        if ($totalDeletedFolderCount -eq 0) {
            Write-Host "No empty folders were found."
        } else {
            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-AssemblyNativeImagesDeleteEmptyTempFolders }
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
After searching empty folders that are not deleted by windows, this is one more locations where a lot of empty folders are created and not used anymore.
So based on this topic C:\Windows\WinSxS\Temp\InFlight\ Deleting Empty Folders
I created a powershell script to also delete the Empty Folders from the Temp Folders from these locations:
c:\Windows\assembly\NativeImages_v2.0.50727_32\Temp\
c:\Windows\assembly\NativeImages_v2.0.50727_64\Temp\
c:\Windows\assembly\NativeImages_v4.0.30319_32\Temp\
c:\Windows\assembly\NativeImages_v4.0.30319_64\Temp\

For me this deleted, about 1000empty folders in above locations.

Let me know if there are more paths for NativeImages_v*/Temp/ then i will add them to the script.
C:\Windows\assembly is for .NET versions below 4.0
C:\Windows\Microsoft.NET\assembly is for 4.0 and above.

You should just match against NativeImages_* since your current or previous .NET builds may vary in version number.
 

My Computer

System One

  • OS
    Windows 7
C:\Windows\assembly is for .NET versions below 4.0
The following version: c:\Windows\assembly\NativeImages_v4.0.30319_32\ c:\Windows\assembly\NativeImages_v4.0.30319_64\ are in the C:\Windows\assembly directory. So those paths needs to stay in there.
C:\Windows\Microsoft.NET\assembly is for 4.0 and above.
No need to check those (yet?) as that location do not contain a Temp folder where empty folders are created.
You should just match against NativeImages_* since your current or previous .NET builds may vary in version number.

Valid reason, thats why i ask for: "Let me know if there are more paths for NativeImages_v*/Temp/ "

However v2.0.50727_32/64 is already 13years+ the same on build and not being updated anymore.
v4.0.30319_64 is also for a few years the on the same build number, don't think they get updates anymore also.

Will have to do a mass search trough machines later this week to get some results back.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop

Latest Support Threads

Back
Top Bottom