'POWERSHELL - Enable ntfs compression of subfolders without knowing parent folder name

I'm trying to enable NTFS compression on different subfolders, while having parent folders that can have different name.

Here is what I've tried so far :

$test = "D:\azerty\temp\sandbox_del_old\*\uiop\"
If((Test-Path $test))
{
      Get-ChildItem -Path $test -Recurse -Directory | ForEach-Object {
        compact /C /f $_.FullName
      }
}

What I want in the end is compress every subfolders (and files) in ...\uiop\

When I execute the script, it compress every folders and files in D:\azerty so first, this is wrong. And it doesn't even compress subfolders and "sub"files.

I can't find it working with my beginner powershell knowledge...

Can anyone help me please ? :)

EDIT : Managed to get it to "work" with adding -Directory (edited). But text files after those subdirectories are not compressed



Solution 1:[1]

You can compress the FILES without relying on compact.exe. This example ignores the directory compression attribute. A side effect: Works with unicode files and with files which include spaces. See https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ni-winioctl-fsctl_set_compression as well.

$MethodDefinition= @'
public static class FileTools
{
  private const int FSCTL_SET_COMPRESSION = 0x9C040;
  private const short COMPRESSION_FORMAT_DEFAULT = 1;
  private const short COMPRESSION_FORMAT_DISABLE = 0;

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  private static extern int DeviceIoControl(
      IntPtr hDevice,
      int dwIoControlCode,
      ref short lpInBuffer,
      int nInBufferSize,
      IntPtr lpOutBuffer,
      int nOutBufferSize,
      ref int lpBytesReturned,
      IntPtr lpOverlapped);

  public static bool Compact(IntPtr handle)
  {
    int lpBytesReturned = 0;
    short lpInBuffer = COMPRESSION_FORMAT_DEFAULT;

    return DeviceIoControl(handle, FSCTL_SET_COMPRESSION,
        ref lpInBuffer, sizeof(short), IntPtr.Zero, 0,
        ref lpBytesReturned, IntPtr.Zero) != 0;
  }
  public static bool Uncompact(IntPtr handle)
  {
    int lpBytesReturned = 0;
    short lpInBuffer = COMPRESSION_FORMAT_DISABLE;

    return DeviceIoControl(handle, FSCTL_SET_COMPRESSION,
        ref lpInBuffer, sizeof(short), IntPtr.Zero, 0,
        ref lpBytesReturned, IntPtr.Zero) != 0;
  }
}
'@

$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name ‘Kernel32’ -Namespace ‘Win32’ -PassThru

$filespec = "c:\folder\*.log"

# compact anything that is not yet compacted
foreach ($File in (Get-ChildItem -Path $filespec -Recurse -File).Where({$_.Attributes  -notmatch [System.IO.FileAttributes]::Compressed})) {
    $FileObject = [System.IO.File]::Open($File.FullName,'Open','ReadWrite','None')
    $Method = [Win32.Kernel32+FileTools]::Compact($FileObject.Handle)
    $FileObject.Close()
}

# decompact
foreach ($File in (Get-ChildItem -Path $filespec -Recurse -File).Where({$_.Attributes  -match [System.IO.FileAttributes]::Compressed})) {
    $FileObject = [System.IO.File]::Open($File.FullName,'Open','ReadWrite','None')
    $Method = [Win32.Kernel32+FileTools]::Uncompact($FileObject.Handle)
    $FileObject.Close()
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Joachim Otahal