【问题标题】:C# BackupRead, BackupWrite - System.AccessViolationExceptionC# BackupRead、BackupWrite - System.AccessViolationException
【发布时间】:2017-11-20 14:16:16
【问题描述】:

我试图让BackupRead()BackupWrite() 使用http://pinvoke.net 作为我的参考。我在谷歌找到的大多数答案都是引用 C++ 甚至是 Delphi...我想用他们的 ACL 备份一个文件/文件夹。

编辑:

这是我当前的代码(我正在为SE_BACKUP_NAMESE_RESTORE_NAMESE_TAKE_OWNERSHIP_NAME 设置权限):

            var bReadHandle = ArchiveWinApi.CreateFile(sourcePath, (uint)(ArchiveWinApi.ACCESS_MASK.GENERIC_READ | ArchiveWinApi.ACCESS_MASK.READ_CONTROL | ArchiveWinApi.ACCESS_MASK.ACCESS_SYSTEM_SECURITY), 0, IntPtr.Zero, FileMode.Open, ArchiveWinApi.ExtendedFileAttributes.BackupSemantics, IntPtr.Zero);
            if (!IsValid(bReadHandle))
            {
                var error = GetLastWin32Error();
                throw new Exception("InvalidFileHandle");
            }

            var bWriteHandle = ArchiveWinApi.CreateFile(destPath, (uint)(ArchiveWinApi.ACCESS_MASK.GENERIC_WRITE | ArchiveWinApi.ACCESS_MASK.WRITE_OWNER | ArchiveWinApi.ACCESS_MASK.WRITE_DAC | ArchiveWinApi.ACCESS_MASK.ACCESS_SYSTEM_SECURITY), 0, IntPtr.Zero, FileMode.Create, ArchiveWinApi.ExtendedFileAttributes.BackupSemantics, IntPtr.Zero);
            if (!IsValid(bWriteHandle))
            {
                var error = GetLastWin32Error();
                throw new Exception("InvalidFileHandle");
            }


            var bufferSize = 4096;
            var buffer = Marshal.AllocHGlobal(bufferSize);

            var backupReadContext = IntPtr.Zero;
            var backupWriteContext = IntPtr.Zero;

            uint lpNumberOfBytesRead = 0;
            uint lpNumberOfBytesWritten = 0;

            while (true)
            {
                var result = ArchiveWinApi.BackupRead(bReadHandle, out buffer, (uint) bufferSize, out lpNumberOfBytesRead, false, true, ref backupReadContext);
                if (!result)
                {
                    var error = GetLastWin32Error();
                    throw new Exception("BackupRead failed");
                }

                if (lpNumberOfBytesRead == 0) break;

                result = ArchiveWinApi.BackupWrite(bWriteHandle, buffer, lpNumberOfBytesRead, out lpNumberOfBytesWritten, false, true, ref backupWriteContext);
                if (!result)
                {
                    var error = GetLastWin32Error();
                    throw new Exception("BackupWrite failed");
                }
            }

            ArchiveWinApi.BackupRead(bReadHandle, out buffer, 0, out lpNumberOfBytesRead, true, true, ref backupReadContext);
            ArchiveWinApi.CloseHandle(bReadHandle);

            ArchiveWinApi.BackupWrite(bWriteHandle, buffer, 0, out lpNumberOfBytesWritten, true, true, ref backupWriteContext);
            ArchiveWinApi.CloseHandle(bWriteHandle);

我收到 System.AccessViolationException: 'Attempted to read or write protected memory。这通常表明其他内存已损坏。'

编辑 2:

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  internal static extern bool BackupRead(IntPtr hFile, out IntPtr lpBuffer, [MarshalAs(UnmanagedType.U4)] uint nNumberOfBytesToRead, [MarshalAs(UnmanagedType.U4)] out uint lpNumberOfBytesRead, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  internal static extern bool BackupWrite(IntPtr hFile, IntPtr lpBuffer, [MarshalAs(UnmanagedType.U4)] uint nNumberOfBytesToWrite, [MarshalAs(UnmanagedType.U4)] out uint lpNumberOfBytesWritten, [MarshalAs(UnmanagedType.Bool)] bool bAbort, [MarshalAs(UnmanagedType.Bool)] bool bProcessSecurity, ref IntPtr lpContext);

 [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
      internal static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPWStr)] string lpFileName, [MarshalAs(UnmanagedType.U4)] uint dwDesiredAccess, [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, IntPtr lpSecurityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, [MarshalAs(UnmanagedType.U4)] ExtendedFileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);

public enum ACCESS_MASK : uint
  {
     READ_CONTROL = 0x00020000,
     WRITE_DAC = 0x00040000,
     WRITE_OWNER = 0x00080000,

     ACCESS_SYSTEM_SECURITY = 0x01000000,

     GENERIC_READ = 0x80000000,
     GENERIC_WRITE = 0x40000000,
     GENERIC_ALL = 0x10000000,
  }

  [Flags]
  public enum ExtendedFileAttributes
  {
     BackupSemantics = 33554432,
  }

【问题讨论】:

  • lpBuffer 设置为您分配的缓冲区,nNumberOfBytesToRead 不能超过缓冲区lpBuffer 的大小(通常完全正确)。它不能是文件的大小,lpNumberOfBytesRead 是指向接收读取字节数的变量的指针。
  • 我应该为缓冲区分配多少?
  • 你想要多少。仅不小于 WIN32_STREAM_ID 的大小。通常在 0x10000 - 0x100000 范围内
  • 已编辑问题,包含代码和当前进度

标签: c# winapi pinvoke


【解决方案1】:

检查问题/调试有点困难,因为您没有提供 P/Invoke 类(并且http://pinvoke.net 不一定是 100% 正确的)。

是的,我知道帮助您解决这个特定问题是个好主意,但我想向您展示一些替代方法:

寻找https://github.com/alphaleonis/AlphaFS

这主要被称为长路径类(长度> 259个字符的路径),但也有一个BackupRead类。多年来,我们将 AlphaFS 库用于生产代码。

我写了一个小样本,快速而肮脏,没有错误检查:-)

using System.Security.AccessControl;
using Alphaleonis.Win32.Filesystem;
using Alphaleonis.Win32.Security;
...
...

var sourceFile = @"c:\temp\a.txt";
var destinationFile = @"c:\temp\b.txt";

var buffer = new byte[4096];

using (var privilegeEnabler = new PrivilegeEnabler(Privilege.Backup, Privilege.Restore))
{
    using (var readStream = new BackupFileStream(sourceFile,
                 System.IO.FileMode.Open, FileSystemRights.Read))
    using (var writeStream = new BackupFileStream(destinationFile,
                 System.IO.FileMode.Create, FileSystemRights.FullControl))
    {
        int count;

        while ((count = readStream.Read(buffer, 0, buffer.Length, true)) > 0)
        {
            writeStream.Write(buffer, 0, count, true);
        }
    }
}

这对我有用(使用 NTFS 备用数据流和安全性复制文件)。

我建议进一步测试它的稳定性,我没有为此目的使用 AlphaFS 的经验。

【讨论】:

  • 感谢您推荐 AlphaFS。但是我们已经修复了长路径和特殊字符。我研究了他们对 CreateFile 和 BackupRead 的使用,但找不到与我的任何大的差异......我也可以在 ACL 损坏的文件夹上使用 CreateFile,而我无法使用 AlphaFS(公平地说,我只尝试了一会儿)。我将在主帖中添加我的 PInvoke 类。
【解决方案2】:

在 P/Invoke 声明中

... BackupRead(IntPtr hFile, out IntPtr lpBuffer, ...

删除out。当然,在通话期间也是如此。

对于 BackupRead 和 BackupWrite API,都给出了指向调用者提供的缓冲区的指针。这个指针会传值,这里不应该放out参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-22
    • 1970-01-01
    • 2012-05-02
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    相关资源
    最近更新 更多