Search code examples
delphicompressionntfs

How can I set a file's compression attribute in Delphi?


How can I compact files (set the 'c' attribute) from Delphi? I am speaking about the "compress contents to save disk space" function available under NTFS.

It seems that FileSetAttr does not allow me to set the 'c' attribute for a file.


Solution

  • The documentation for SetFileAttributes() explains that the FILE_ATTRIBUTE_COMPRESSED flag is not accepted by that function (although it is for GetFileAttributes). Instead it states:

    To set a file's compression state, use the DeviceIoControl function with the FSCTL_SET_COMPRESSION operation.

    The FSCTL_SET_COMPRESSION link in particular explains precisely how to do it. It goes something like this:

    const
      COMPRESSION_FORMAT_NONE = 0;
      COMPRESSION_FORMAT_DEFAULT = 1;
      COMPRESSION_FORMAT_LZNT1 = 2;
    
    procedure SetCompressionAttribute(const FileName: string; const CompressionFormat: USHORT);
    const
      FSCTL_SET_COMPRESSION = $9C040;
    var
      Handle: THandle;
      Flags: DWORD;
      BytesReturned: DWORD;
    begin
      if DirectoryExists(FileName) then
        Flags := FILE_FLAG_BACKUP_SEMANTICS
      else if FileExists(FileName) then
        Flags := 0
      else
        raise Exception.CreateFmt('%s does not exist', [FileName]);
    
      Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, Flags, 0);
      Win32Check(Handle <> INVALID_HANDLE_VALUE);
      try
        if not DeviceIoControl(Handle, FSCTL_SET_COMPRESSION, @CompressionFormat, SizeOf(Comp), nil, 0, BytesReturned, nil) then
          RaiseLastOSError;
      finally
        CloseHandle(Handle);
      end;
    end;