Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 774
Server Overflow Avatar asked Aug 09 '11 20:08

Server Overflow


3 Answers

you can also use the CIM_DataFile and CIM_Directory WMI classes, both had two methods called Compress and UnCompress which can be used to set the NTFS compression in a file or folder.

Check these samples (if the )

Compress (NTFS) or UnCompress a File

function  CompressFile(const FileName:string;Compress:Boolean):integer;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObject   : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObject   := FWMIService.Get(Format('CIM_DataFile.Name="%s"',[StringReplace(FileName,'\','\\',[rfReplaceAll])]));
  if Compress then
    Result:=FWbemObject.Compress()
  else
    Result:=FWbemObject.UnCompress();
end;

Compress (NTFS) or UnCompress a Folder

function  CompressFolder(const FolderName:string;Recursive, Compress:Boolean):integer;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObject   : OLEVariant;
  StopFileName  : OLEVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObject   := FWMIService.Get(Format('CIM_Directory.Name="%s"',[StringReplace(FolderName,'\','\\',[rfReplaceAll])]));
  if Compress then
    if Recursive then
     Result:=FWbemObject.CompressEx(StopFileName, Null, Recursive)
    else
     Result:=FWbemObject.Compress()
  else
    if Recursive then
     Result:=FWbemObject.UnCompressEx(StopFileName, Null, Recursive)
    else
     Result:=FWbemObject.UnCompress();
end;
like image 129
RRUZ Avatar answered Oct 14 '22 06:10

RRUZ


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;
like image 6
David Heffernan Avatar answered Oct 14 '22 07:10

David Heffernan


Here you go. Call this against a file or a folder and it should do the job for you. State=true makes it compressed, State=false undoes the compression. Remember, though, that if you run it against a folder it only changes the attribute and makes it so future files created in that folder are compressed. To compress the ones already in there, you have to iterate and call this on each file (FindFirst/FindNext/FindClose). HTH.

function CompressFile(filepath: string; state: boolean): boolean;
  const
    COMPRESSION_FORMAT_DEFAULT = 1;
    COMPRESSION_FORMAT_NONE = 0;
    FSCTL_SET_COMPRESSION: DWord = $9C040;
  var
    compsetting: Word;
    bytesreturned: DWord;
    FHandle: THandle;
  begin
   //if not os_is_nt then
   //  raise Exception.Create('A Windows NT based OS is required for this function.');
    FHandle := CreateFile(PChar(filepath), GENERIC_READ or GENERIC_WRITE,
              0, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
    if FHandle = INVALID_HANDLE_VALUE then
      raise Exception.Create('CompressFile Message: ' + SysErrorMessage(GetLastError));
    if state = true then
      compsetting := COMPRESSION_FORMAT_DEFAULT
    else
      compsetting := COMPRESSION_FORMAT_NONE;
    try
      Result := DeviceIOControl(FHandle, FSCTL_SET_COMPRESSION, @compsetting,
         sizeof(compsetting), nil, 0, bytesreturned, nil);
    finally
      CloseHandle(FHandle);
    end;
  end;
like image 6
Glenn1234 Avatar answered Oct 14 '22 07:10

Glenn1234