【问题标题】:Compress a folder using NTFS compression in .NET在 .NET 中使用 NTFS 压缩来压缩文件夹
【发布时间】:2010-10-12 01:37:25
【问题描述】:

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

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 压缩吗?

【问题讨论】:

    标签: c# .net windows 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);
                  }
              }
           }
      }
      

      【讨论】:

      • 这是一种比解析 P/Invoke 更简洁的方法,更重要的是,尽管返回了成功状态代码,但它实际上并没有工作。虽然ManagementObject .ctor 很挑剔,但我使用了这个string objPath = "Win32_Directory.Name=" + "'" + dir.FullName.Replace("\\", @"\\").TrimEnd('\\') + "'"; 来确保ManagementObject 不会抛出无效参数。
      • btw 如何解压目录?
      • 只是说“未定义类型'ManagementObject'。” &“未定义类型‘ManagementBaseObject’。”还警告 Imports System.Management not nec。我的版本是 VS 2019
      【解决方案3】:

      创建 Win32_Directory.Name=... 字符串时,您需要将反斜杠加倍,例如路径 C:\Foo\Bar 将被构建为:

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

      或使用您的示例代码:

      字符串 objPath = "Win32_Directory.Name=\"C:\\\\Foo\\\\Bar\"";

      显然,该字符串被馈送到某个需要路径字符串的转义形式的进程。

      【讨论】:

        【解决方案4】:

        有一种更简单的方法,我在 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 到 \pcname\root 如果您需要在另一台计算机上执行此操作。小心使用。

        【讨论】:

          【解决方案5】:

          我不相信有办法在 .NET 框架中设置文件夹压缩,因为文档(备注部分)声称无法通过 File.SetAttributes 完成。这似乎仅在使用 DeviceIoControl 函数的 Win32 API 中可用。仍然可以通过 .NET 使用 PInvoke 来做到这一点。

          一旦熟悉了 PInvoke,请查看 pinvoke.net 上的参考资料,其中讨论了 signature 需要什么样的外观才能实现这一点。

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              猜你喜欢
              • 2013-10-30
              • 2013-03-09
              • 2010-09-05
              • 1970-01-01
              • 1970-01-01
              • 2019-05-07
              • 2010-12-14
              • 1970-01-01
              • 2012-06-19
              相关资源
              最近更新 更多