Сжатие папки с использованием сжатия NTFS в .NET - PullRequest
11 голосов
/ 08 марта 2009

Я хочу сжать папку, используя сжатие 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 для папки?

Ответы [ 6 ]

13 голосов
/ 09 марта 2009

По моему опыту, использование 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 для каждого файла / папки.

9 голосов
/ 09 марта 2009

Я проверил код и alt text!

  • Убедитесь, что он работает для вас с графическим интерфейсом. Возможно, размер единицы выделения слишком велик для сжатия. Или у вас недостаточно прав.
  • Для пункта назначения используйте такой формат: «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);
            }
        }
     }
}
1 голос
/ 15 ноября 2010

При создании строки Win32_Directory.Name = ... вам необходимо удвоить обратную косую черту, например, путь C: \ Foo \ Bar будет выглядеть так:

Win32_Directory.Name = "C: \\ \\ Foo Bar",

или используйте ваш пример кода:

string objPath = "Win32_Directory.Name = \" C: \\\\ Foo \\\\ Bar \ "";

Очевидно, что строка передается какому-либо процессу, который ожидает экранированную форму строки пути.

0 голосов
/ 27 октября 2013

Существует гораздо более простой способ, который я использую в Windows 8 64-разрядная версия, переписанная для 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, если вам нужно сделать это на другом компьютере. Используйте с осторожностью.

0 голосов
/ 27 сентября 2013

Это небольшая адаптация игальского ответа сербов. Я столкнулся с тонкой проблемой с Name в очень специфическом формате. Поэтому я добавил немного Replace("\\", @"\\").TrimEnd('\\') magic , чтобы сначала нормализовать путь, я также немного очистил код.

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");
    }
}
0 голосов
/ 08 марта 2009

Я не верю, что есть способ установить сжатие папок в .NET Framework, поскольку документы (раздел примечаний) утверждают, что это невозможно сделать с помощью File.SetAttributes . Похоже, это доступно только в Win32 API с использованием функции DeviceIoControl . Еще можно сделать это через .NET, используя PInvoke .

После ознакомления с PInvoke в целом, посмотрите ссылку на pinvoke.net , в которой обсуждается, как должна выглядеть подпись , чтобы это произошло.

...