【问题标题】:AccessViolationException when using FileRead API使用 FileRead API 时出现 AccessViolationException
【发布时间】:2011-12-04 08:53:12
【问题描述】:

我正在使用 FileRead API。 我使用的是 Windows 7 x64,我的代码运行良好且正确。 现在我安装了新的 Windows 7 x86 和 VS2008 teamsuit 和 .NET 2、3+SP1+SP2、3.5、3.5.1。 我以管理员身份运行代码,但仍然遇到以下错误:

AccessViolationException(试图读取或写入受保护的内存。这通常表明其他内存已损坏。)

 int nread = 0;
 uint handle;
 byte[] buff = new byte[1024];
 string driveRoot = string.Concat("\\\\.\\", driveLetter);
 uint hRoot = CreateFile(driveRoot,
                GENERIC_READ | GENERIC_WRITE,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                IntPtr.Zero,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL,
                IntPtr.Zero);
 if (hRoot != -1)
      handle = ReadFile(hRoot, buff, 1024, nread, new System.Threading.NativeOverlapped());

【问题讨论】:

  • 您的 HANDLE 应该是 IntPtr 但这在 x86 上不会有问题。不过在 x64 上应该是个问题。 nread 也作为指针传递,你传递的是一个 uint。也应该是一个 IntPtr。

标签: c# .net windows winapi pinvoke


【解决方案1】:

虽然我不是 C# 专家,但在我看来,您使用错误的参数调用 ReadFile()

第 4 个参数必须是一个指向整数的指针,该整数将接收读取的字节数。您提供整数本身 (nread),而不是其地址 (&nread)。

除非您想要异步文件 I/O,否则 ReadFile() 的最后一个参数必须是 NULL 指针(或只是 0)。

this example on MSDN

【讨论】:

  • 但它在 windows7 x64 上正常工作!!!为什么相同的代码在 windows7 x86 上不能正常工作???有什么问题?
  • nread 可能会成功,因为 win32 需要一个指针并且传递零 int 等于 NULL 指针。
  • 我认为重叠阅读可能是个问题。你绝对不想在这里重叠。这可能会导致在缓冲区被释放后很久才写入缓冲区。
【解决方案2】:

我怀疑您的代码的主要问题是您请求重叠的 I/O,但提供的缓冲区在 ReadFile 返回时不再存在。它适用于某些系统而不适用于其他系统,因为系统决定是否异步执行操作,并且它可能选择在一个系统上不执行异步操作,而在另一个系统上选择不同。

我确定您不希望 I/O 重叠,因此您只需将 NULL 传递给 ReadFile 的最终参数。

另一方面,也许您的代码根本无法在 x64 系统上运行,并且永远无法达到 AV 的水平。您的句柄类型被错误地声明为 32 位整数。

您的代码还有许多其他小问题。这是纠正这些错误的代码的编辑版本。 P/invoke 签名取自 pinvoke.net。

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

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(
    IntPtr hFile, 
    [Out] byte[] lpBuffer,
    uint nNumberOfBytesToRead, 
    out uint lpNumberOfBytesRead, 
    IntPtr lpOverlapped
);

static void Main(string[] args)
{
    string physicalDrive = @"\\.\PhysicalDrive0";
    IntPtr hFile = CreateFile(
        physicalDrive, 
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        IntPtr.Zero,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        IntPtr.Zero
    );
    if (hFile.ToInt64() != INVALID_HANDLE_VALUE)
    {
        byte[] buff = new byte[1024];
        uint nread;
        if (ReadFile(hFile, buff, (uint)buff.Length, out nread, IntPtr.Zero))
            Console.WriteLine("Read successful");
    }
}

总结代码中的错误:

  • 不正确地使用 32 位整数来存储句柄。
  • 您的 ReadFile 的 P/invoke 声明错误地声明了 lpNumberOfBytesRead
  • ReadFile 不返回句柄,它返回一个表示函数调用成功的布尔值。
  • 使用了您不希望的重叠 I/O,并且无法与编组的 byte[] 缓冲区一起使用。
  • 您绝不能从托管代码调用GetLastError(您在注释中显示的代码中这样做了)。而是致电Marshal.GetLastWin32Error。原因在documentation 中解释了该方法。

【讨论】:

  • tnx for edit.now 我用 SetFilePointerEx 移动到 hfile 的偏移量。我这样做了,但它不能正常工作 ==> SetFilePointerEx(hRoot, MFTAddress, ref newaddress, 0); int 错误 = GetLastError(); if (hRoot != IntPtr.Zero) 句柄 = ReadFile(hRoot, buff, 1024,ref nread, new System.Threading.NativeOverlapped());
  • @Saeed 我想我回答了你原来的问题。现在,您似乎在评论中提出了不同的问题。你没有说它以什么方式失败。 “它不能正常工作”永远不足以让我们为您提供帮助。此外,在 cmets 中提供代码也无济于事,因为它从未以可读的方式格式化。可以编辑问题,可以提出新问题。作为记录,我的回答是否解决了您最初的问题?
  • 现在,查看评论中的代码,我可以看到两个问题。永远不要从托管代码中调用GetLastError。致电Marshal.GetLastWin32Error 文档解释了为什么这很重要。其次,请停止要求重叠 I/O。你不想要它,它不能与 P/invoke byte[] 缓冲区一起使用。第三,ReadFile 不返回句柄,它返回bool。我已经在回答中向您展示了这一切。
  • 也许我在回答中没有明确说明您的代码有很多问题。看看你的代码和我的有什么不同。查看您的 P/invoke 签名并查看我的。看看它们有何不同。我在答案的底部添加了一些文字,以说明我在您的代码中发现的所有错误。
  • 如果这回答了您的问题,那么请记住接受答案:meta.stackexchange.com/questions/5234
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多