SetFilePointer异常:读取物理驱动器扇区遇保护内存错误
解决SetFilePointer在C# P/Invoke中HighPart非零时的保护内存异常问题
我在尝试从物理驱动器读取扇区时碰到了一个头疼的问题:当通过P/Invoke调用SetFilePointer,且传入的HighPart参数非零时,运行一段时间后就会抛出以下异常:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
奇怪的是,当HighPart传0时代码能正常工作,而且对应的C版本完全没有这个问题。
原问题代码
最初使用的P/Invoke签名和调用逻辑如下:
[DllImport("kernel32.dll", EntryPoint = "SetFilePointer", CallingConvention = CallingConvention.StdCall)] static extern uint SetFilePointer( [In] IntPtr hFile, [In] int lDistanceToMove, [In,Out] int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod); public void SetFilePointer(LARGE_INTEGER li) { SetFilePointer((IntPtr)handle, li.LowPart, li.HighPart, EMoveMethod.Begin); } // 传入参数值:HighPart=381 int,LowPart=-1323466752 int
问题根源
问题出在lpDistanceToMoveHigh参数的传递方式上。原生Win32 API的SetFilePointer中,这个参数是一个指向int的指针(用来传递64位偏移量的高32位,支持输入和输出),但原签名里直接把它声明为int类型——这就导致C#会把传入的int值直接当作内存地址来传递,当HighPart非零时,这个值对应的内存区域通常是受系统保护的,自然就触发了内存访问异常。
修正后的解决方案
按照@mjwills的建议,我调整了P/Invoke签名,解决了这个问题:
[DllImport("kernel32.dll", EntryPoint = "SetFilePointer")] static extern uint SetFilePointer( [In] Microsoft.Win32.SafeHandles.SafeFileHandle hFile, [In] int lDistanceToMove, [In, Out] ref int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod); public void SetFilePointer(LARGE_INTEGER li) { SetFilePointer(shandle, li.LowPart, ref li.HighPart, EMoveMethod.Begin); }
这里的关键修改有两点:
- 将文件句柄替换为
SafeFileHandle,这是更安全的托管句柄类型,能有效避免句柄泄漏等潜在问题; - 将
lpDistanceToMoveHigh声明为ref int,这样C#会正确传递该变量的内存地址,完全符合原生API对指针参数的要求。
内容的提问来源于stack exchange,提问作者user964829
相关产品推荐
相关产品推荐

