DWORD SetFilePointer(
HANDLE hFile, // handle to file
LONG lDistanceToMove, // bytes to move pointer
PLONG lpDistanceToMoveHigh, // bytes to move pointer
DWORD dwMoveMethod // starting point
);
其中各项参数的含义为:
hFile:文件句柄,为CreateFile时返回的句柄
lpBuffer:保存读入的数据的指针
lDistanceToMove:移动的字节数低DWORD
lpDistanceToMoveHigh:移动的字节数高DWORD,为了支持64位(2的64次方字节)长度的大文件,而用来指定64字节的高32位,如果文件大小只需要32位就可以表示,则设置为NULL
ldwMoveMethod:移动方法,可以选择下面的值。
FILE_BEGIN 从文件开始处开始移动
FILE_CURRENT 从文件开始除开始移动
FILE_END 从文件末尾开始移动
RtlUnwind
正如在 Visual C++ 的 __except_handler3 函数中见到的,unwinding 是由 __global_un
wind2 RTL 函数完成的。此函数是对未公开的 RtlUnwind API 的非常简单的封装:
__global_unwind2(void * pRegistFrame)
{
_RtlUnwind( pRegistFrame,
&__ret_label,
0, 0 );
__ret_label:
}
尽管 RtlUnwind 是实现编译器级 SEH 的关键的 API,但却没有公开。它是一个 KERNEL32
函数,Windows NT 将 KERNEL32.DLL 调用 forward 到了 NTDLL.DLL,在 NTDLL.DLL 里的
也是一个 RtlUnwind 函数。我拼凑了这个函数的一些伪码,即 Figure 12 所示。
尽管 RtlUnwind 看起来很繁琐,但如果合理地划分一下还是不难理解的。此 API 首先从
FS:[4] 和 FS:[8] 取得线程堆栈的当前栈顶和栈底。这两个值对于后面的健壮性检查是很
重要的,这里的健壮性检查就是保证所有被 unwound 的异常帧都落在堆栈的范围内。
接着, RtlUnwind 在堆栈上建立一个 EXCEPTION_RECORD,并将 ExceptionCode 域设为 ST
ATUS_UNWIND。而且 EXCEPTION_RECORD 的 ExceptionFlags 域中的 EXCEPTION_UNWINDING
标志也要置位。指向此 EXCEPTION_RECORD 结构体的指针之后会作为参数传递给每一个异
常回调函数。此后,代码调用 _RtlpCaptureContext 函数来创建一个 CONTEXT 结构体,此
结构体也会作为异常回调的 unwind 调用的一个参数。 RtlUnwind 后面的部分就遍历 EXCE
PTION_REGISTRATION 结构体的链表。对于每一帧,代码调用 RtlpExecuteHandlerForUnwi
nd 函数,后面会讲到此函数。正是这个函数用 EXCEPTION_UNWINDING 标志调用了异常回调
函数。每次回调之后,相应的异常帧通过调用 RtlpUnlinkHandler 将其移除。当 RtlUnwi
nd 到达第一个参数指定地址的帧时,就停止 unwinding 帧。这些代码中间还有许多用于错
误检查的代码,这些代码保证了程序的正常执行。如果出现了问题, RtlUnwind 就会引起异
常来告知所遇到的问题,而且此异常的 EXCEPTION_NONCONTINUABLE 标志是置位的。当此标
志置位时,是不允许进程继续执行的,因此进程必须结束。