windows 在 .NET 中使用 NTFS 压缩来压缩文件夹

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/624125/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 12:07:54  来源:igfitidea点击:

Compress a folder using NTFS compression in .NET

c#.netwindowsntfs

提问by decasteljau

I want to compress a folder using NTFS compression in .NET. I found this post, but it does not work. It throws an exception ("Invalid Parameter").

我想在 .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 );
   }
}

Anybody knows how to enable NTFS compression on a folder?

有人知道如何在文件夹上启用 NTFS 压缩吗?

回答by Zack Elan

Using P/Invoke is, in my experience, usually easier than WMI. I believe the following should work:

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

Since you're trying to set this on a directory, you will probably need to use P/Invoke to call CreateFileusing FILE_FLAG_BACKUP_SEMANTICSto get the SafeFileHandle on the directory.

既然你要设置此上的目录,你可能会需要使用P / Invoke来调用的CreateFile使用FILE_FLAG_BACKUP_SEMANTICS,以获得SafeFileHandle上的目录。

Also, note that setting compression on a directory in NTFS does not compress all the contents, it only makes new files show up as compressed (the same is true for encryption). If you want to compress the entire directory, you'll need to walk the entire directory and call DeviceIoControl on each file/folder.

另请注意,在 NTFS 中的目录上设置压缩不会压缩所有内容,它只会使新文件显示为压缩(加密也是如此)。如果要压缩整个目录,则需要遍历整个目录并在每个文件/文件夹上调用 DeviceIoControl。

回答by Igal Serban

I have tested the code and it alt text!

我已经测试了代码和它 替代文字

  • Make sure it works for you with the gui. Maybe the allocation unit size is too big for compression. Or you don't have sufficient permissions.
  • For your destination use format like so: "c:/temp/testcomp" with forward slashes.
  • 确保它适用于您的 gui。也许分配单元的大小对于压缩来说太大了。或者您没有足够的权限。
  • 对于您的目的地,请使用如下格式:带有正斜杠的“c:/temp/testcomp”。

Full code:

完整代码:

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);
            }
        }
     }
}

回答by thphoenix

There is a much simpler way, which I am using in Windows 8 64-bit, rewritten for VB.NET. Enjoy.

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

works great for me. Chagne .\root to \pcname\root if you need to do it on another computer. Use with care.

对我很有用。Chagne .\root 到 \pcname\root 如果您需要在另一台计算机上执行此操作。小心使用。

回答by Lee

When creating the Win32_Directory.Name=... string you need to double the backslashes, so for example the path C:\Foo\Bar would be built up as:

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

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

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

or using your example code:

或使用您的示例代码:

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

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

Apparently the string is fed to some process that expects an escaped form of the path string.

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

回答by John Leidegren

This is a slight adaption of Igal Serban answer. I ran into a subtle issue with the Namehaving to be in a very specific format. So I added some Replace("\\", @"\\").TrimEnd('\\')magicto normalize the path first, I also cleaned up the code a bit.

这是对 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");
    }
}

回答by Scott Saad

I don't believe there is a way to set folder compression in the .NET framework as the docs (remarks section) claim it cannot be done through File.SetAttributes. This seems to be only available in the Win32 API using the DeviceIoControlfunction. One can still do this through .NET by using PInvoke.

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

Once familiar with PInvoke in general, check out the reference at pinvoke.netthat discusses what the signatureneeds to look like in order to make this happen.

一般熟悉 PInvoke 后,请查看pinvoke.net上的参考资料,其中讨论了签名需要什么样的内容才能实现这一点。