Wednesday, May 18, 2022

Cleanup huge WindowsApps folder

There is an updated post here, with less details, but including the script.

In some cases the "c:\Program Files\WindowsApps" folder starts to fill the harddisk.

We had one customer with a 256GB ssd, where the WindowsApps folder did take 180GB.

So what's going on, and how to cleanup this?

It's not clear why there are so many orphaned versions/installations in the WindowsApps folder, must be some bug somewhere in Windows 10 to cause this.

So, the first question is not realy answered, so what about the cleanup?

You can google for this and find a lot of results, but not many with a real solution.

The best post I did find is this one: https://www.tenforums.com/performance-maintenance/185009-how-clean-up-windowsapps-folder-3.html with a script to detect all orphanes on page 3 of the answers/discussions.

It works like a charm, the only thing missing is actually deleting the folder/files from the standard system, and not the recovery console.

The reason you need the recovery console, is the NFTS owner and rights of that special folder.

But of course there is a way arround this too:

1. Take ownership of the folder (and it's content) with takeown

2. Set the acl's to allow the current user to delete the folder+content with icacls

3. Finally, with 182 orphan folder, you don't want to accept each deletion manually, so we add the /Q argument to the RD command.

A small note, the takeown command uses either /d y on english systems for the confirmation or on german systems the /d j argument. So modify that line to match your confirmation letter

The lines from line number 120 onwards looks then like this

echo rem folder "%%z" (about !oldlineSize! Bytes^, about !SizeMB! MBytes^)
echo takeown /F "%WA%\%%z" /r /d y
echo icacls "%WA%\%%z" /t /grant %USERNAME%:F

echo RD /Q /S "%WA%\%%z"

So you can now run the script as admin, rename the resulting file into delorpahns.cmd and run that one as admin too and your WindowsAppps folder is clean once more.

 

To have the complete script available in one place, here my enhanced version:

 

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: By Einstein1969 for www.tenforum.com
::
%= CleanWA =% @set "Version=0.1.4 BETA"
::
:: Detect orphaned dirs in %ProgramFiles%\WindowsApps, check for integrity/consistency
::
:: Requirements: Save to UTF-8. Use Lucida Console Font in CMD window. Run with double click over icon script.
::         Windows 10 version 1511 (build number 10586) onward
::
:: history:
::   04/02/2022 0.1.4 BETA      fix some minor bug and aesthetic improvements
::     /10/2021 0.1.4 BETA    fix bug for elevated char ;,=() &^
::   29/09/2021 0.1.3 BETA     Search for applications that do not have InstallLocation set.
::                Enable support for Unicode UTF-8 and VT-ANSI
::   25/09/2021    0.1.2 BETA     removed debug, added run as administrator , thanks Matthew Wai
::   25/09/2021    0.1.1 BETA     add debug instruction for "file not found" bug/error.
::
:: ref: https://www.tenforums.com/tutorials/4689-uninstall-apps-windows-10-a.html
:: ref: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off & setlocal EnableDelayedExpansion&color 1f&title CleanWA %Version%

Rem Choice the WindowsApps (WA) directory to check.
set "WA=%ProgramW6432%\WindowsApps"


Rem Check for Run as administrator.
call :Restart_as_Administrator %1

Call :video_setting

Rem check for permission on WA
if not exist "%WA%\." echo( & echo Error: Problem accessing "%WA%" & goto :the_end

echo(
echo   • Analyze "%WA%"
echo(

Echo     • Check for applications that do not have "InstallLocation" set.
echo(

rem create list of registered app
> "%tmp%\CleanWA.RegisteredApps.tmp.txt"  ^
 (
    rem exclude "system" applications in c:\windows folder
    powershell -Command "Get-AppxPackage -AllUsers | ? { $_.SignatureKind -ne 'System' } | sort -property {$_.InstallLocation+' '} | ForEach-Object {'{0,-40}  {1,-20}  {2}' -f $_.name,$_.version,$_.InstallLocation}"
 )

type nul: > "%tmp%\CleanWA.RegisteredApps_good.tmp.txt"
type nul: > "%tmp%\CleanWA.RegisteredApps_NO_good.tmp.txt"
FOR /f "usebackq tokens=1,2,*" %%a in ("%tmp%\CleanWA.RegisteredApps.tmp.txt") do (
    if "%%c" equ "" (
        echo(    %CSI%7m? Warning: Registered app "%%a" does not have a "InstallLocation" set/defined. ?%CSI%27m
        echo(
        echo Rem Warning: Registered app "%%a" does not have a "InstallLocation" set/defined. >> "%tmp%\CleanWA.RegisteredApps_NO_good.tmp.txt"
    ) else echo(%%a %%b %%c >> "%tmp%\CleanWA.RegisteredApps_good.tmp.txt"
)

echo     v check finished.
echo(

echo     • Detect orphaned dirs
echo(

type nul > "%tmp%\CleanWA.report_orphans.tmp.txt"
set "dn=0" & rem number of folders/directories
set /A "Size=0, Totsize=0,dirsO"
FOR /f "tokens=*" %%z IN ('dir /b /o:n "%WA%"') DO (
  set /a "dn+=1"
 
  FOR /f "tokens=1-4" %%g in ('dir /S /W /-C "%WA%\%%z"') do (set "oldlineSize=!line!" & set line=%%i)
  call :pad dn 4
  set /A "size=!oldlineSize:~0,-4!+0, sizeMB=size/105"
  echo       • !dn! - Searching for folder:
  echo                "%%z" (about !SizeMB! MBytes^) %CSI%0K

  set "found="
  for /f "usebackq tokens=1,2,*" %%a in ("%tmp%\CleanWA.RegisteredApps_good.tmp.txt") do if not defined found (
        rem echo       • Pkg. InstallLocation "%%c" %CSI%0K %RI%
        set _V_=%%z
        set v1="x!_V_:%%a_%%b=!x"
        set v2="x!_V_!x"
        if !v1! NEQ !v2! (
        set Found=1
        rem echo(      %CSI%102m? Found an app that use this folder ?%CSI%44m : %CSI%102m"%%a"%CSI%44m version: "%%b" %CSI%0K
    )
  )

  if defined found (
    echo(%RI%%RI%%RI%
  ) else (
    rem check for unknown folder/dir
    rem known:_x64_, _x86_, _neutral_ .... others?
        set v1="x!_V_:_x64_=!x"
        set v2="x!_V_!x"
    set "OK=N"
        if !v1! NEQ !v2! set "OK=Y"
        set v1="x!_V_:_x86_=!x"
    if !v1! NEQ !v2! set "OK=Y"
    set v1="x!_V_:_neutral_=!x"
    if !v1! NEQ !v2! set "OK=Y"

    if !OK! NEQ Y (
        echo(
        echo       %CSI%43m? No Match folder: "%%z" ?%CSI%44m%CSI%0K
        echo(
        echo(
        ping -n 2 127.0.0.1 >nul
    ) else (
        echo(
        echo       %CSI%101m? orphans folder! ?%CSI%44m%CSI%0K
        echo(
        echo(
        rem why 105? 1024*1024/10000 ~ 105
        set /A "Totsize+=size, TotsizeMB=Totsize/105, dirsOrphans+=1"
        title CleanWA %Version% [Tot. space orphans: about !TotsizeMB! MB] [dirs/folder orphans: !dirsOrphans!]
        (
          echo rem folder "%%z" (about !oldlineSize! Bytes^, about !SizeMB! MBytes^)
          echo takeown /F "%WA%\%%z" /r /d y
          echo icacls "%WA%\%%z" /t /grant %USERNAME%:F
          echo RD /Q /S "%WA%\%%z"
          echo(
                ) >> "%tmp%\CleanWA.report_orphans.tmp.txt"
    )
  )
  rem pathping 127.0.0.1 -n -q 1 -p 100 >nul
)
echo(
echo(
echo(
echo     v check finished.
echo(
echo dirs/folder orphans: !dirsOrphans!
echo(
echo Tot. space orphans: about !TotsizeMB! MB
echo(

>nul: copy /a "%tmp%\CleanWA.RegisteredApps_NO_good.tmp.txt" + /a "%tmp%\CleanWA.report_orphans.tmp.txt" "%tmp%\CleanWA.report.tmp.txt"

echo coping Report/script "%tmp%\CleanWA.report.tmp.txt" for offline delete in \Users\Public
copy "%tmp%\CleanWA.report.tmp.txt" \Users\Public
echo(
pause

start notepad "\Users\Public\CleanWA.report.tmp.txt"

:the_end
:: pause if double clicked on instead of run from command line.
echo %cmdcmdline% | findstr /I /L %comspec% >nul 2>&1
if %errorlevel% == 0 echo( & pause
exit /B 0
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::   SUBROUTINE   ::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:Restart_as_Administrator
(Fsutil Dirty Query %SystemDrive%>Nul)&&(
        if "%1" neq "admin" (
        start /MAX %~f0 admin
        Exit
    )
)||(
    mode con cols=90 lines=20
    echo( & echo     It is necessary to start the script with administrative rights.
    echo( & echo     Please wait ... I am restarting the script with administrative rights.
    echo( & echo     Answer "YES" to the next User Account Control UAC request to continue
    echo(     running this script with administrative permissions. & echo(
    timeout /t 4
    powershell.exe -c "Start -WindowStyle Maximized -Verb RunAs cmd /c, ("^""%~f0"^"" -replace '[;,=() &^]', '^$0'), "admin" " & Exit
)
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:video_setting
    For /F %%a In ('echo prompt $E^| cmd') Do Set "ESC=%%a"
    set "CSI=%ESC%[" & set "RI=%ESC%M"
    set "echoVT=<nul set/p.="
        rem get windows size
    for /f "tokens=1,2 skip=3" %%A in ('powershell -command "$Host.ui.rawui.WindowSize"'
    ) do set /a windowWidth=%%A, windowHeight=%%B, sm_e=%%B - 3
    mode con: COLS=%windowWidth% LINES=%windowHeight%
    Rem Setting Scrolling Margins
    echo %CSI%4;%sm_e%r
    rem set UTF-8
    chcp 65001
    cls
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:pad
  set "pad=!%1!"
  for /L %%L in (1,1,%2) do set "pad= !pad!"
  set "pad=!pad:~-%2!"
  set "%1=!pad!"
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



1 comment:

  1. Good work! thank for sharing. I've added some tips for handling the language in your script part. check on tenforums

    ReplyDelete