神奇的GetOverlappedResult

在msdn中关于GetOverlappedResult的描述如下:

GetOverlappedResult Function

Retrieves the results of an overlapped operation on the specified file, named pipe, or communications device.

BOOL WINAPI GetOverlappedResult(
  __in          HANDLE hFile,
  __in          LPOVERLAPPED lpOverlapped,
  __out         LPDWORD lpNumberOfBytesTransferred,
  __in          BOOL bWait
);
Parameters

hFile

A handle to the file, named pipe, or communications device. This is the same handle that was specified when the overlapped operation was started by a call to the ReadFile, WriteFile, ConnectNamedPipe, TransactNamedPipe, DeviceIoControl, or WaitCommEvent function.

 

lpOverlapped

A pointer to an OVERLAPPED structure that was specified when the overlapped operation was started.

 

lpNumberOfBytesTransferred

A pointer to a variable that receives the number of bytes that were actually transferred by a read or write operation. For a TransactNamedPipe operation, this is the number of bytes that were read from the pipe. For a DeviceIoControl operation, this is the number of bytes of output data returned by the device driver. For a ConnectNamedPipe or WaitCommEvent operation, this value is undefined.

 

bWait

If this parameter is TRUE, the function does not return until the operation has been completed. If this parameter is FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.

第一个参数hFile是之前打开的文件,命名管道的句柄,第二个是OVERLAPPED结构的指针,里面有一个event,第三个是读取的字节数,第四个为是否等待的标志位。现在让我们进入正题:

按照一般人类的猜想,这个函数作为异步IO时完成读取的“善后工作”,接受的第一个参数自然是未完成就返回的IO句柄,OVERLAPPED结构体内的event提供一个同步机制,在读取完数据后通知调用者可以继续执行(和bWait标志位配合)。这一想法看着挺合理的,要是我来设计GetOverlappedResult函数,我就这么实现它。但实际情况如何呢,还是让我们来看看代码吧

BOOL
STDCALL
GetOverlappedResult (
  IN HANDLE   hFile,
	IN LPOVERLAPPED	lpOverlapped,
	OUT LPDWORD		lpNumberOfBytesTransferred,
	IN BOOL		bWait
	)
{
	DWORD WaitStatus;
  HANDLE hObject;

  if (lpOverlapped->Internal == STATUS_PENDING)
  {
    if (!bWait)
    {
      /* can't use SetLastErrorByStatus(STATUS_PENDING) here, 
      since STATUS_PENDING translates to ERROR_IO_PENDING */
      SetLastError(ERROR_IO_INCOMPLETE);
      return FALSE;
    }
    
    hObject = lpOverlapped->hEvent ? lpOverlapped->hEvent : hFile; //here is the fucking trap

    /* Wine delivers pending APC's while waiting, but Windows does
    not, nor do we... */
    WaitStatus = WaitForSingleObject(hObject, INFINITE);
    
    if (WaitStatus == WAIT_FAILED)
    {
      DPRINT("Wait failed!\n");
      /* WaitForSingleObjectEx sets the last error */
      return FALSE;
    }
  }

  *lpNumberOfBytesTransferred = lpOverlapped->InternalHigh;
  
  if (!NT_SUCCESS(lpOverlapped->Internal))
  {
    SetLastErrorByStatus(lpOverlapped->Internal);
    return FALSE;
  }

	return TRUE;
}
以上代码抄袭自Reactos。我没看过windows的源代码,但是Reactos这帮哥们很敬业,他们在实现一个接口之前会仔细测试它
的所有行为细节,不放心的还会反编译二进制文件进行深入研究,所以我认为windows里的实际代码应该和上述代码相差无几。
在这段代码里我们能发现什么秘密呢?那就是,第一个参数hFile其实不是用来完成IO的,而是用来同步的,其地位和第二个参
数的某成员hEvent相同!
hObject = lpOverlapped->hEvent ? lpOverlapped->hEvent : hFile; //here is the fucking trap 
/* Wine delivers pending APC's while waiting, but Windows does not, nor do we... */ 
WaitStatus = WaitForSingleObject(hObject, INFINITE); 
如上述代码所示,如果lpOverlapped->hEvent为空,则拿hFile作为同步的object,若lpOverlapped->hEvent不为空,那
么可怜的hFile根本一点用都没有!
这个函数迷惑过小弟,现在我把它解剖了跟大家分享,希望大家别再上当。。。
 
update 仔细看完msdn后我又发现:

If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed. Use of file, named pipe, or communications-device handles for this purpose is discouraged. It is safer to use an event object because of the confusion that can occur when multiple simultaneous overlapped operations are performed on the same file, named pipe, or communications device. In this situation, there is no way to know which operation caused the object's state to be signaled.

哎,我还是想说,太迷惑人了这个函数。。。

 
posted @ 2009-06-30 20:24  gussing  阅读(15793)  评论(1编辑  收藏  举报