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

发布于 2024-07-14 21:28:23 字数 696 浏览 9 评论 0原文

我想在 .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 压缩吗?

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").

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

停滞 2024-07-21 21:28:23

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

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

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 CreateFile using FILE_FLAG_BACKUP_SEMANTICS to get the SafeFileHandle on the directory.

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.

唐婉 2024-07-21 21:28:23

我已经测试了代码,它 工作
(来源: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);
            }
        }
     }
}

I have tested the code and it worked
(source: typepad.com)
!

  • 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.

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=" + "'" + directoryInfo.FullName.Replace("\\", @"\\").TrimEnd('\\') + "'";
            using (ManagementObject dir = new ManagementObject(objPath))
            {
                ManagementBaseObject outParams = dir.InvokeMethod("Compress", null, null);
                uint ret = (uint)(outParams.Properties["ReturnValue"].Value);
            }
        }
     }
}
翻身的咸鱼 2024-07-21 21:28:23

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

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

    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.

耳根太软 2024-07-21 21:28:23

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

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

或使用您的示例代码:

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

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

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",

or using your example code:

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.

把人绕傻吧 2024-07-21 21:28:23

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

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

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");
    }
}
你是我的挚爱i 2024-07-21 21:28:23

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

总体熟悉 PInvoke 后,请查看 pinvoke.net 上的参考资料,其中讨论了 签名需要看起来像这样才能实现这一点。

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 DeviceIoControl function. One can still do this through .NET by using PInvoke.

Once familiar with PInvoke in general, check out the reference at pinvoke.net that discusses what the signature needs to look like in order to make this happen.

指尖凝香 2024-07-21 21:28:23

基于 Zack Elan 的解决方案,我在创建 SafeFileHandle 时遇到了一些困难,因此我在这里发布整个类以供参考:

internal class FileCompressor
{
    private const int FSCTL_SET_COMPRESSION = 0x9C040;
    private const short COMPRESSION_FORMAT_DEFAULT = 1;
    private const uint GENERIC_ALL = 0x10000000;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;

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

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode,
        IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    public void CompressFile(string path)
    {
        using SafeFileHandle handle = CreateFile(path, GENERIC_ALL, 0, IntPtr.Zero, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);

        if (!EnableCompression(handle))
        {
            // Handle error
        }
    }

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

I struggled a bit to create the SafeFileHandle, based on Zack Elan's solution, so I'm posting here the whole class for reference:

internal class FileCompressor
{
    private const int FSCTL_SET_COMPRESSION = 0x9C040;
    private const short COMPRESSION_FORMAT_DEFAULT = 1;
    private const uint GENERIC_ALL = 0x10000000;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;

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

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode,
        IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);

    public void CompressFile(string path)
    {
        using SafeFileHandle handle = CreateFile(path, GENERIC_ALL, 0, IntPtr.Zero, OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);

        if (!EnableCompression(handle))
        {
            // Handle error
        }
    }

    private 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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文