之前在写重叠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);这一句来完成上面两句的任务,效果一样。不过好像大家都是用的上面两句。

posted on 2017-08-22 19:35  敲代码的小女孩  阅读(1307)  评论(0编辑  收藏  举报