使用.NET在NTFS压缩文件夹

15

我想在.NET中使用NTFS压缩来压缩一个文件夹。 我找到了这篇帖子,但它不起作用。 它会抛出异常(“无效参数”)。

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个回答

15

根据我的经验,使用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。


14

我已经测试了代码,并且它 worked
(来源:typepad.com)
!

  • 确保它能在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=" + "'" + directoryInfo.FullName.Replace("\\", @"\\").TrimEnd('\\') + "'";
            using (ManagementObject dir = new ManagementObject(objPath))
            {
                ManagementBaseObject outParams = dir.InvokeMethod("Compress", null, null);
                uint ret = (uint)(outParams.Properties["ReturnValue"].Value);
            }
        }
     }
}

5
这种方法比使用P/Invoke更干净,更重要的是,即使返回成功状态代码,它实际上并没有起作用。虽然“ManagementObject”的构造函数有些挑剔,但我使用了这个字符串objPath = "Win32_Directory.Name=" + "'" + dir.FullName.Replace("\\", @"\\").TrimEnd('\\') + "'";来确保“ManagementObject”不会抛出无效参数异常。 - John Leidegren
1
你怎么解压目录呢? - Arsen Zahray
只是说“类型'ManagementObject'未定义。”和“类型'ManagementBaseObject'未定义。”还警告不需要导入System.Management。我的版本是VS 2019。 - www-0av-Com

2

有一种更简单的方法,我在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

对我来说非常好用。如果您需要在另一台计算机上执行此操作,请将.\root更改为\pcname\root。请谨慎使用。


1
这是对Igal Serban答案的轻微改编。我遇到了一个微妙的问题,需要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");
    }
}

1

创建 Win32_Directory.Name=... 字符串时,需要使用双反斜杠(\)转义,例如路径 C:\Foo\Bar 应构建为:

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

或者使用您的示例代码:

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

显然,该字符串被提供给某个期望路径字符串的转义形式的进程。


0

我不相信在.NET框架中有一种设置文件夹压缩的方法,因为文档(备注部分)声称无法通过File.SetAttributes完成。这似乎只能在Win32 API中使用DeviceIoControl函数实现。可以通过使用PInvoke在.NET中仍然实现此操作。

一旦熟悉了PInvoke的一般用法,请查看pinvoke.net上的参考资料,了解需要哪些签名才能实现此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接