2009-03-08 5 views
10

Я хочу сжать папку, используя сжатие NTFS в .NET. Я нашел this post, но он не работает. Он генерирует исключение («Недопустимый параметр»).Сжатие папки с использованием сжатия 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 в папке?

ответ

11

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

0

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

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

8

Я протестировал код, и он alt text!

  • Удостоверьтесь, что он работает на вас с помощью 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); 
      } 
     } 
    } 
} 
+2

фолк слэши сделал трюк, спасибо! – decasteljau

+3

Это гораздо более чистый подход, чем разрешение на P/Invoke, что более важно, на самом деле он не работал, несмотря на возвращение кода статуса успеха. Хотя 'ManagementObject' .ctor придирчив, я использовал эту строку: string objPath =" Win32_Directory.Name = "+" '"+ dir.FullName.Replace (" \\ ", @" \\ "). TrimEnd (' \ \ ') + "'"; 'для обеспечения того, чтобы' ManagementObject' не выдавал недопустимый параметр. –

+0

как вы распаковываете каталог? –

1

При создании Win32_Directory.Name = ... строку, которую нужно удвоить обратный слэш, так, например, путь C: \ Foo \ Bar будет построен как:

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

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

строку objPath = "Win32_Directory.Name = \" C: \\\\ Foo Bar \\\\ \ "";

По-видимому, строка подается в какой-то процесс, ожидающий скрытую форму строки пути.

0

Это небольшая адаптация ответа Игаля Сербана. Я столкнулся с тонким вопросом: 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

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

Смежные вопросы