Сжатие папки с использованием сжатия NTFS в .NET.
Я хочу сжать папку, используя сжатие NTFS в .NET. Я нашел этот пост, но он не работает. Он генерирует исключение ( "Недопустимый параметр" ).
DirectoryInfo directoryInfo = new DirectoryInfo( destinationDir );
if( ( directoryInfo.Attributes & FileAttributes.Compressed ) != FileAttributes.Compressed )
{
string objPath = "Win32_Directory.Name=" + "\"" + destinationDir + "\"";
using( ManagementObject dir = new ManagementObject( objPath ) )
{
ManagementBaseObject outParams = dir.InvokeMethod( "Compress", null, null );
uint ret = (uint)( outParams.Properties["ReturnValue"].Value );
}
}
Кто-нибудь знает, как включить сжатие NTFS в папке?
Ответы
Ответ 1
Использование P/Invoke, по моему опыту, обычно проще, чем WMI. Я считаю, что следующее должно работать:
private const int FSCTL_SET_COMPRESSION = 0x9C040;
private const short COMPRESSION_FORMAT_DEFAULT = 1;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int DeviceIoControl(
SafeFileHandle hDevice,
int dwIoControlCode,
ref short lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
ref int lpBytesReturned,
IntPtr lpOverlapped);
public static bool EnableCompression(SafeFileHandle 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;
}
Поскольку вы пытаетесь установить это в каталоге, вам, вероятно, понадобится использовать P/Invoke для вызова CreateFile с помощью FILE_FLAG_BACKUP_SEMANTICS
, чтобы получить SafeFileHandle в каталоге.
Также обратите внимание, что установка сжатия в каталоге в NTFS не сжимает все содержимое, а только создает новые файлы как сжатые (то же самое верно для шифрования). Если вы хотите сжать весь каталог, вам нужно пройти весь каталог и вызвать DeviceIoControl для каждого файла/папки.
Ответ 2
Я проверил код и он
!
- Удостоверьтесь, что он работает для вас с gui. Возможно, размер блока выделения слишком большой для сжатия. Или у вас недостаточно прав.
- Для вашего назначения используйте формат: "c:/temp/testcomp" с косой чертой.
Полный код:
using System.IO;
using System.Management;
class Program
{
static void Main(string[] args)
{
string destinationDir = "c:/temp/testcomp";
DirectoryInfo directoryInfo = new DirectoryInfo(destinationDir);
if ((directoryInfo.Attributes & FileAttributes.Compressed) != FileAttributes.Compressed)
{
string objPath = "Win32_Directory.Name=" + "\"" + destinationDir + "\"";
using (ManagementObject dir = new ManagementObject(objPath))
{
ManagementBaseObject outParams = dir.InvokeMethod("Compress", null, null);
uint ret = (uint)(outParams.Properties["ReturnValue"].Value);
}
}
}
}
Ответ 3
При создании строки Win32_Directory.Name =... вам нужно удвоить обратную косую черту, так что путь C:\Foo\Bar будет создан как:
Win32_Directory.Name = "C:\\\\ Foo Bar",
или используя код примера:
string objPath = "Win32_Directory.Name = \" C: \\\\ Foo \\\\ Bar\"";
По-видимому, строка передается в некоторый процесс, который ожидает escape-форму строки пути.
Ответ 4
Я не верю, что есть способ установить сжатие папки в платформе .NET, поскольку документы (раздел замечаний) утверждают, что это невозможно сделать через File.SetAttributes. Похоже, что это доступно только в Win32 API, используя функцию DeviceIoControl. Все еще можно сделать это через .NET, используя PInvoke.
Как только вы знакомы с PInvoke в общем, посмотрите ссылку pinvoke.net, в которой обсуждается, что подпись должна выглядеть так, чтобы это произошло.
Ответ 5
Это небольшая адаптация ответа Игаля Сербана. Я столкнулся с тонкой проблемой, когда Name
должен быть в очень специфическом формате. Поэтому я добавил магию Replace("\\", @"\\").TrimEnd('\\')
для нормализации пути во-первых, я также немного очистил код.
var dir = new DirectoryInfo(_outputFolder);
if (!dir.Exists)
{
dir.Create();
}
if ((dir.Attributes & FileAttributes.Compressed) == 0)
{
try
{
// Enable compression for the output folder
// (this will save a ton of disk space)
string objPath = "Win32_Directory.Name=" + "'" + dir.FullName.Replace("\\", @"\\").TrimEnd('\\') + "'";
using (ManagementObject obj = new ManagementObject(objPath))
{
using (obj.InvokeMethod("Compress", null, null))
{
// I don't really care about the return value,
// if we enabled it great but it can also be done manually
// if really needed
}
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine("Cannot enable compression for folder '" + dir.FullName + "': " + ex.Message, "WMI");
}
}
Ответ 6
Существует гораздо более простой способ, который я использую в 64-разрядной версии Windows 8, переписанный для VB.NET. Наслаждайтесь.
Dim Path as string = "c:\test"
Dim strComputer As String = "."
Dim objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Dim colFolders = objWMIService.ExecQuery("Select * from Win32_Directory where name = '" & Replace(path, "\", "\\") & "'")
For Each objFolder In colFolders
objFolder.Compress()
Next
отлично работает для меня. Chagne.\Root to\pcname\root, если вам нужно сделать это на другом компьютере. Используйте с осторожностью.