之前在写重叠I\O的代码的时候,记得那时是从网络上拷贝的代码例子。在关于重叠操作的等待结果时,一般都是如下的过程:
先 调用waitformultipleobject或waitforsingleobject函数来等待某一个I\O请求绑定的事件对象的激活。等到激活事件对象后,再调用GetOverlappedResult函数取得重叠I\O的结果,这里GetOverlappedResult的返回值一般都是TRUE(即重叠操作成功)。
代码如下:
rc = ReadFile(hFile,buf,300,&numread,&overlap); //因为是overlapped操作,ReadFile会将读文件请求放入读队列之后立即返回(false), //而不会等到文件读完才返回(true) if (rc) { //文件真是被读完了,rc为true // 或当数据被放入cache中,或操作系统认为它可以很快速地取得数据,rc为true } else { if (GetLastError() == ERROR_IO_PENDING) {//当错误是ERROR_IO_PENDING,那意味着读文件的操作还在进行中 //等候,直到文件读完 WaitForSingleObject(hFile, INFINITE); rc = GetOverlappedResult(hFile,&overlap,&numread,FALSE); //上面二条语句完成的功能与下面一条语句的功能等价: // GetOverlappedResult(hFile,&overlap,&numread,TRUE); } else { //出错了 } } CloseHandle(hFile);
如果这样做也可以,不用关心一些关于getoverlappedresult函数的细节。
现在深入研究下getoverlappedresult函数的细节:
BOOL
WINAPI
GetOverlappedResult(
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
BOOL bWait
)
下面是OVERLAPPED的结构定义:
typedef struct _OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
其函数实现的伪代码为:
{
DWORD WaitReturn;
// Did caller specify an event to the original operation or was the
// default (file handle) used?
//
if (lpOverlapped->Internal == (DWORD)STATUS_PENDING ) {
if ( bWait )
{
//
//现在还是PENDING,且还需要等待,则无限期等待。
//很多人会自己调用WaitForSingleObject后再调用GetOverlappedResult,其实看起来
//没多少必要。
//
WaitReturn = WaitForSingleObject(
( lpOverlapped->hEvent != NULL ) ?
lpOverlapped->hEvent : hFile,
INFINITE
);
}
else
{
WaitReturn = WAIT_TIMEOUT;
}
if ( WaitReturn == WAIT_TIMEOUT )
{
// !bWait and event in not signalled state
SetLastError( ERROR_IO_INCOMPLETE );
return FALSE;
}
if ( WaitReturn != 0 )
{
return FALSE; // WaitForSingleObject calls BaseSetLastError
}
}
*lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh;
if ( NT_SUCCESS((NTSTATUS)lpOverlapped->Internal) )
{
return TRUE;
}
else
{
BaseSetLastNTError( (NTSTATUS)lpOverlapped->Internal );
return FALSE;
}
}
由上面代码可以看出,Internal成员就是用来存储已经处理的I\O请求的状态码。InternalHigh成员是在I\O请求完成后,实际传输的字节数,如果没有完成,则这个成员值应该被忽略。
WaitForSingleObject(hFile, INFINITE); rc = GetOverlappedResult(hFile,&overlap,&numread,FALSE);
一般情况下waitsingle的第二个参数为INFINITE的话,除非出现什么其他I\O错误,要不然等wait到事件对象后,I\O操作都是已经成功完成了的。那么再调用Getoverlappedresult函数就会直接返回TRUE了。
如果把waitsingle的第二个参数为某个超时时间值时,那么Getoverlappedresult函数的最后一个blwait参数最好设置为TRUE,这样的话才能保证Getoverlappedresult返回TRUE,要不然就会返回FALSE,而此时的GetLastError返回值就是ERROR_IO_INCOMPLETE 了,代表I\O操作还未完成。
或者是直接用Getoverlappedresult(hFile, &overlap, &numread, TRUE);这一句来完成上面两句的任务,效果一样。不过好像大家都是用的上面两句。