C#中如何使用Windows API ReadFile()从指定偏移位置读取文件
问题说明
以下C#代码片段可实现对二进制文件的分页顺序读取。
出于特定场景的严格要求,必须使用Windows系统API ReadFile()完成操作。
for (iReadCounter = 0; iReadCounter < iReadCountLimit; iReadCounter++) { readsize = DefineConstants.READ_BUF_SIZE; bool bResult = ReadFile(fhnd, readbuffer, (uint)readsize, out readresult, ref _overlapped); . . }
核心诉求:实现指定ReadFile()从自定义的文件偏移/位置处开始读取数据。
可行实现方案
根据是否使用重叠IO(即代码中传入_overlapped参数的模式),有两种直接可用的实现方式:
方案1:重叠IO场景(适配当前代码写法)
当前代码调用ReadFile时传入了_overlapped结构,这种场景下不需要修改文件指针,直接给OVERLAPPED结构的偏移字段赋值即可指定读取起始位置:
Offset字段:填入目标偏移的低32位数值OffsetHigh字段:填入目标偏移的高32位数值- 这里的偏移是从文件起始位置计算的字节偏移,和系统维护的默认文件指针位置无关,赋值完成后直接调用ReadFile就会从指定位置开始读取。
示例代码:
// 定义需要读取的起始偏移,例:从文件头开始4096字节位置 long customOffset = 4096; // 给OVERLAPPED结构赋值偏移 _overlapped.Offset = (int)(customOffset & 0xFFFFFFFF); _overlapped.OffsetHigh = (int)(customOffset >> 32); // 正常调用ReadFile即可,会从customOffset位置开始读取 for (iReadCounter = 0; iReadCounter < iReadCountLimit; iReadCounter++) { readsize = DefineConstants.READ_BUF_SIZE; bool bResult = ReadFile(fhnd, readbuffer, (uint)readsize, out readresult, ref _overlapped); // 后续处理逻辑 }
注意:这种方式指定偏移读取不会修改系统默认维护的文件指针位置,仅对当前传入OVERLAPPED的读操作生效。如果文件句柄是用FILE_FLAG_OVERLAPPED标志打开的,必须用这种方式指定偏移,调用移动文件指针的API不会对重叠IO读操作生效。
方案2:非重叠IO场景
如果调用ReadFile时最后一个lpOverlapped参数传入IntPtr.Zero(不使用重叠IO),可以在调用ReadFile前先移动文件指针到目标位置:
- 优先调用
SetFilePointerExAPI设置文件指针,该API原生支持64位偏移,处理大文件时不会出现精度问题,比旧版SetFilePointer更易用。 - 移动文件指针时可以选择从文件头、当前位置、文件尾三个基准位置计算偏移,设置完成后调用ReadFile就会从新的指针位置开始读取,读取完成后指针会自动向后移动本次读取的字节长度。
示例逻辑:
// 定义需要读取的起始偏移,例:从文件头开始4096字节位置 long customOffset = 4096; long newFilePointer; // 从文件头开始计算,移动指针到目标偏移,0代表FILE_BEGIN即文件头基准 SetFilePointerEx(fhnd, customOffset, out newFilePointer, 0); // 调用ReadFile,最后一个参数传空,不使用重叠IO bool bResult = ReadFile(fhnd, readbuffer, (uint)readsize, out readresult, IntPtr.Zero);
注意事项
- 指定偏移前需要校验偏移合法性,不要设置超过文件实际长度的偏移值,否则ReadFile会调用失败,返回的系统错误码会提示读取位置越界。
- 两种方案不要混用:如果用重叠IO打开文件句柄,就必须通过OVERLAPPED结构指定偏移,不要额外调用文件指针移动API;如果用普通阻塞模式打开句柄,就通过移动文件指针的方式指定位置。
内容的提问来源于stack exchange,提问作者Hillel
相关产品推荐
相关产品推荐

