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.
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;
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;
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With