CreateFile, DeviceIoControl, ReadFile, WriteFile, CancelIo, CloseHandle, GetOverlappedResult

应用程序和驱动程序的通信过程是:

应用程序使用CreateFile函数打开设备,
然后用DeviceIoControl与驱动程序进行通信,包括读和写两种操作。
还可以用ReadFile读数据用WriteFile写数据。
操作完毕时用CloseHandle关闭设备。

我们比较常用的就是用DeviceIoControl对设备进行读写操作。

CreateFile

这是一个多功能的函数,可打开或创建以下对象,并返回可访问的句柄:

控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道。
 
HANDLE WINAPI CreateFile(
    _In_ LPCTSTR lpFileName,
    _In_ DWORD dwDesiredAccess,
    _In_ DWORD dwShareMode,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    _In_ DWORD dwCreationDisposition,
    _In_ DWORD dwFlagsAndAttributes,
    _In_opt_ HANDLE hTemplateFile
);

参数类型及说明

lpFileName String,
要打开的文件的名字
 
dwDesiredAccess Long,
如果为 GENERIC_READ 表示允许对设备进行读访问;
如果为 GENERIC_WRITE 表示允许对设备进行写访问(可组合使用);
如果为零,表示只允许获取与一个设备有关的信息
 
dwShareMode Long,
零表示不共享;
FILE_SHARE_READ 和/或 FILE_SHARE_WRITE 表示允许对文件进行共享访问
 
lpSecurityAttributes
 
 
SECURITY_ATTRIBUTES,指向一个SECURITY_ATTRIBUTES结构的指针,
定义了文件的安全特性(如果操作系统支持的话)
 
dwCreationDisposition Long,下述常数之一:
CREATE_NEW          创建文件;如文件存在则会出错
CREATE_ALWAYS       创建文件,会改写前一个文件
OPEN_EXISTING       文件必须已经存在。由设备提出要求
OPEN_ALWAYS         如文件不存在则创建它
TRUNCATE_EXISTING   将现有文件缩短为零长度

dwFlagsAndAttributes Long,一个或多个下述常数

FILE_ATTRIBUTE_ARCHIVE          标记归档属性  
FILE_ATTRIBUTE_COMPRESSED       将文件标记为已压缩,或者标记为文件在目录中的默认压缩方式
FILE_ATTRIBUTE_NORMAL           默认属性
FILE_ATTRIBUTE_HIDDEN           隐藏文件或目录
FILE_ATTRIBUTE_READONLY         文件为只读
FILE_ATTRIBUTE_SYSTEM           文件为系统文件
FILE_FLAG_WRITE_THROUGH         操作系统不得推迟对文件的写操作
FILE_FLAG_OVERLAPPED            允许对文件进行重叠操作
FILE_FLAG_NO_BUFFERING          禁止对文件进行缓冲处理。文件只能写入磁盘卷的扇区块
FILE_FLAG_RANDOM_ACCESS         针对随机访问对文件缓冲进行优化
FILE_FLAG_SEQUENTIAL_SCAN       针对连续访问对文件缓冲进行优化
FILE_FLAG_DELETE_ON_CLOSE       关闭了上一次打开的句柄后,将文件删除。特别适合临时文件

也可在Windows NT下组合使用下述常数标记:

SECURITY_ANONYMOUS,
SECURITY_IDENTIFICATION,
SECURITY_IMPERSONATION,
SECURITY_DELEGATION,
SECURITY_CONTEXT_TRACKING,
SECURITY_EFFECTIVE_ONLY
 
hTemplateFile Long,如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性

返回值

Long,如执行成功,则返回文件句柄。
INVALID_HANDLE_VALUE表示出错,会设置GetLastError。
即使函数成功,但若文件存在,且指定了CREATE_ALWAYS 或 OPEN_ALWAYS,
GetLastError也会设为ERROR_ALREADY_EXISTS
 

DeviceIoControl

DeviceIoControl的定义:

BOOL DeviceIoControl( 
HANDLE hDevice, 
DWORD dwIoControlCode, 
LPVOID lpInBuffer, 
DWORD nInBufferSize, 
LPVOID lpOutBuffer, 
DWORD nOutBufferSize, 
LPDWORD lpBytesReturned, 
LPOVERLAPPED lpOverlapped);

Parameters(参数)

hDevice (CreateFile返回的设备句柄)
[in] Handle to the device that is to perform the operation.
To obtain a device handle, call the CreateFile function.
dwIoControlCode (应用程序调用驱动程序的控制命令,就是IOCTL_XXX IOCTLs )
[in] IOCTL for the operation.
This value identifies the specific operation to perform and the type of device
on which to perform the operation.
There are no specific values defined for the dwIoControlCode parameter.
However, you can define custom IOCTL_XXX IOCTLs with the macro.
You can then advertise these IOCTLs and an application can use these IOCTLs
with DeviceIoControl to perform the driver-specific functions.
lpInBuffer (应用程序传递给驱动程序的数据缓冲区地址)
[in] Long pointer to a buffer that contains the data required to perform the operation.
Set to NULL if the dwIoControlCode parameter specifies an operation
that does not require input data.
nInBufferSize (应用程序传递给驱动程序的数据缓冲区大小,字节数)
[in] Size, in bytes, of the buffer pointed to by lpInBuffer.
lpOutBuffer (驱动程序返回给应用程序的数据缓冲区地址)
[out] Long pointer to a buffer that receives the output data for the operation.
Set to NULL if the dwIoControlCode parameter specifies an operation
that does not produce output data.
nOutBufferSize (驱动程序返回给应用程序的数据缓冲区大小,字节数)
[out] Size, in bytes, of the buffer pointed to by lpOutBuffer.
lpBytesReturned (驱动程序实际返回给应用程序的数据字节数地址)
[out] Long pointer to a variable that receives the size,
in bytes, of the data stored in lpOutBuffer.
The DeviceIoControl function may unnecessarily use this parameter.
For example, if an operation does not produce data for lpOutBuffer and lpOutBuffer is NULL,
the value of lpBytesReturned is meaningless.
lpOverlapped (重叠操作结构)
[in] Ignored; set to NULL.

Return Values(返回值)

Nonzero : indicates success.

Zero : indicates failure.

To obtain extended error information, call the function. (非0成功,0失败)

 

ReadFile

ReadFile函数从文件指针指定的位置读取数据。
读操作完成后,文件指针将根据实际读出的数据自动进行调整,除非文件句柄是以OVERLAPPED属性值打开的。
如果是以OVERLAPPED打开的I/O,应用程序就需要自己手动调整文件指针。
这个函数被设计成兼有同步和异步操作。
ReadFileEx函数则设计成只支持异步操作,异步操作允许应用程序在读文件期间可以同时进行其他的操作。

BOOL ReadFile(
HANDLE hFile,                // handle to file
LPVOID lpBuffer,             // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPDWORD lpNumberOfBytesRead, // number of bytes read
LPOVERLAPPED lpOverlapped    // overlapped buffer
);

1、hFile    文件句柄(必须具有GENERIC_READ访问权限)。
在Windows 95/98/Me平台上:对于邮槽、命名管道和磁盘文件不能使用异步读操作。 
在Windows NT/2000/XP平台上:对于异步读操作,hFile可以是由CreateFile函数以FILE_FLAG_OVERLAPPED方式打开的任何句柄,
或者一个由socket或accept函数返回的socket句柄。


2、lpBuffer   
用来接收从文件中读出的数据的缓冲区指针。

3、nNumberOfBytesToRead   
指明要读的字节总数。

4、lpNumberOfBytesRead   
一个变量指针,用来存储实际传输的字节总数。
ReadFile在做所有事情(包括错误检查)之前,先将这个值赋为0。
当ReadFile从一个命名管道上返回TRUE时这个参数为0,
说明消息管道另一端调用WriteFile时设置的nNumberOfBytesToWrite 参数为0。

Windows 95/98/Me平台上:这个参数不允许为NULL。
Windows NT/2000/XP平台上:如果lpOverlapped 为NULL,则lpNumberOfBytesRead不能为NULL。
如果lpOverlapped 不是NULL,lpNumberOfBytesRead可以设为NULL。
如果是一个overlapped形式的读操作,我们可以动用GetOverlappedResult函数来获得传输的实际字节数。
如果hFile关联的是一个完成端口(I/O completion port),那么可以调用GetQueuedCompletionStatus函数来获得传输的实际字节数。
如果完成端口(I/O completion port)被占用,而你用的是一个用于释放内存的回调例程,
对于lpOverlapped参数指向的OVERLAPPED结构体来说,为这个参数指定NULL可以避免重新分配内存时发生内存泄漏。
内存泄漏会导致返回这个参数值时是一个非法值。
    
5、lpOverlapped
一个指向OVERLAPPED结构体的指针。
如果hFile是以FILE_FLAG_OVERLAPPED方式获得的句柄,这个结构是必须的,不能为NULL。
(否则函数会在错误的时刻报告读操作已经完成了)。
这时,读操作在由OVERLAPPED中Offset成员指定的偏移地址开始读,并且在实际完成读操作之前就返回了。
在这种情况下,ReadFile返回FALSE,GerLastError报告从错误类型是ERROR_IO_PENDING。
这允许调用进程继续其他工作直到读操作完成。OVERLAPPED结构中的事件将会在读操作完成时被使能。

如果hFile不是以FILE_FLAG_OVERLAPPED方式获得的句柄,并且lpOverlapped为NULL,
读操作就从当前文件的开始位置读起,直到读操作完成ReadFile函数才能返回。

在Windows NT/2000/XP平台上:
如果hFile不是以FILE_FLAG_OVERLAPPED方式获得的句柄,并且lpOverlapped不为NULL,
则读操作在由OVERLAPPED中Offset成员指定的偏移地址开始读,直到读操作完成ReadFile函数才能返回。


在Windows 95/98/Me平台上:
对于文件、磁盘、管道和邮槽的操作,这个参数必须为NULL。
一个不为空的OVERLAPPED结构体指针将导致调用失败。
Windows 95/98/Me平台只支持串行口和并行口的overlapped 读写。

Return Values】
如下任一种情况发生都会导致函数返回:(1) 在管道另一端的写操作完成后 (2) 请求的字节数传输完毕 (3) 发生错误。
如果函数正确,返回非零。
如果返回值是非零,但接收的字节数是0,那么可能是文件指针在读操作期间超出了文件的end位置。
然而,如果文件以FILE_FLAG_OVERLAPPED方式打开,lpOverlapped 参数不为NULL,
文件指针在读操作期间超出了文件的end位置,那么返回值肯定是FALSE,GetLastError返回的错误是ERROR_HANDLE_EOF。

Remarks】
如果文件的一部分被另一个进程锁定,而当前进程试图重复锁定,那将会失败。
一个应用程序在读以FILE_FLAG_NO_BUFFERING方式打开的文件时要符合一定的条件。
(1)文件读的开始地址必须是扇区大小的整数倍。GetDiskFreeSpace函数可以取得扇区的大小。
(2)请求读的字节数也必须是扇区大小的整数倍。
(3)用于读写操作的Buffer地址必须按照扇区大小进行边界对齐。可以通过用VirtualAlloc 函数申请内存来做到。

在读操作期间试图访问相应的输入缓冲区,会导致读入到缓冲区的数据损坏。
读操作完成之前,应用程序不能对这段输入缓冲区做任何操作(包括读、写、重新分配内存,释放内存等)。

ReadFile可以通过指向控制台输入对象的句柄将控制台的输入字符读出来。控制台的模式决定了ReadFile的具体行为。
如果一个命名管道正在以消息模式被读取,并且下一条消息比nNumberOfBytesToRead参数指定的长度还大,
那么ReadFile将返回FALSE并且GetLastError返回错误为ERROR_MORE_DATA。
剩下没读完的消息可能会被随后的ReadFile或PeckNamedPipe函数读出。


读取一个通信设备时,ReadFile的行为被当前的通信延时所支配,
延时属性的设置和取得使用SetCommTimeouts和GetCommTimeouts函数。
如果你设置延时属性失败,就会得到不可预知的结果。

如果ReadFile试图读取一个buffer太小的邮槽,
将会返回FALSE并且GetLastError返回错误为ERROR_INSUFFICIENT_BUFFER 。

如果一个匿名的写管道句柄已经关闭,而ReadFile试图用响应的匿名权限读这个管道句柄,
将返回FALSE并且GetLastError返回错误为ERROR_BROKEN_PIPE。

每当有太多的异步I/O请求得不到响应,ReadFile就会失败,
并返回ERROR_INVALID_USER_BUFFER或ERROR_NOT_ENOUGH_MEMORY的错误。

在同步和异步两种情况下,ReadFile中检测EOF(文件结尾边界)的代码是不同的。
当一个同步读操作到达文件结尾时,ReadFile返回TRUE,并设置*lpNumberOfBytesRead 为0 。
异步读操作会在开始调用的读操作中或者随后的其他异步操作中突然遇到文件结尾。
(1)如果EOF在ReadFile期间被检测到,将会返回FALSE,且 GetLastError返回错误描述 ERROR_HANDLE_EOF。
(2)如果EOF在随后的其他异步操作中被检测到,则类似GetOverlappedResult 等试图获取操作结果的函数返回FALSE,
且 GetLastError返回错误描述ERROR_HANDLE_EOF。

为了取消未响应的异步I/O操作,用CancelIo函数。
这个函数只能取消由调用进程对特定句柄进行的操作。
被取消的I/O操作将被描述为ERROR_OPERATION_ABORTED。

如果你正试图从并不存在的软驱中读数据,系统会弹出消息框提示你重新操作。
为了阻止系统的消息框,调用函数SetErrorMode,参数设置为SEM_NOOPENFILEERRORBOX。

 

WriteFile

可以以同步或异步方式向一个对象句柄中写数据

BOOL WriteFile(
HANDLE hFile,                    // handle to file
LPCVOID lpBuffer,                // data buffer
DWORD nNumberOfBytesToWrite,     // number of bytes to write
LPDWORD lpNumberOfBytesWritten, // number of bytes written
LPOVERLAPPED lpOverlapped        // overlapped buffer
);

其他信息与ReadFile极其相似,可参考

CancelIo

CancelIo 函数取消由主调线程处理的所有等待的输入输出(I/O)操作。
函数不能取消由其他线程处理的I/O操作。
如果要取消其他线程处理的I/O操作,可以使用CancelIoEx函数。

BOOL WINAPI CancelIo(
__in  HANDLE hFile
);

参数

hFile [in] 文件的句柄, 函数取消该文件句柄的所有等待的I/O操作。

返回值

如果函数成功,返回值是非零值。线程可以通过使用GetOverlappedResult函数来判断I/O操作什么时候已经被完成。
如果函数失败,返回值是0.

备注

该函数只能取消句柄上的标准I/O,但不能改变句柄的状态;这意味着你不能依赖于句柄的状态,因为你不知道操作是否完成或被取消。
I/O操作必须以overlapped I/O 来处理。
如果不是的话,I/O操作不会返回以允许线程去调用该函数。
如果以某个具有FILE_FLAG_OVERLAPPED标识符打开的文件句柄,该函数将无效。

所有I/O操作如果完成的话,都返回ERROR_OPERATION_ABORTED,且所有通过都正常激活。

CloseHandle

用于关掉一个打开的对象句柄 


BOOL CloseHandle( HANDLE hObject // handle to object );

函数调用成功返回非零,失败返回0。获得更多错误信息,需要调用GetLastError函数。

 

GetOverlappedResult

该函数返回重叠操作的结果,它是通过判断OVERLAPPED结构中的hEvent是否被置位来判断异步操作是否完成

BOOL GetOverlappedResult(
    HANDLE hFile,                          // 句柄  
    LPOVERLAPPED lpOverlapped,             // 指向重叠操作开始时指定的OVERLAPPED结构
    LPDWORD lpNumberOfBytesTransferred,    // 指向一个32位变量,该变量的值返回实际读写操作传输的字节数。
    BOOL bWait                             // 该参数用于指定函数是否一直等到重叠操作结束。
);
BOOL bWait : 
如果该参数为TRUE,函数直到操作结束才返回。
如果该参数为FALSE,函数直接返回,这时如果操作没有完成,
通过调用GetLastError()函数会返回ERROR_IO_INCOMPLETE

OVERLAPPED的结构定义

typedef struct _OVERLAPPED { 
    DWORD  Internal; 
    DWORD  InternalHigh; 
    DWORD  Offset; 
    DWORD  OffsetHigh; 
    HANDLE hEvent; 
} OVERLAPPED;

这个结构中Internal和InternalHigh是两个返回值。
写过驱动程序的人知道这两个值对应着irp的IO_STATUS_BLOCK结构:

typedef struct _IO_STATUS_BLOCK 
{
    union {
        NTSTATUS Status; // Internal
        PVOID Pointer;   
    };
    ULONG_PTR Information;  // InternalHigh
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;


其中,Internal就是Status的值;
InternalHigh就是Information的值。

1.当调用返回时(用ReadFile举例):

若Internal=0时表明返回STATUS_SUCCESS,于是ReadFile返回TRUE,即成功返回;
InternalHigh的值保存在lpNumberOfBytesTransferred中。

若Internal!=0表示出现错误或PENDING,
于是ReadFile返回FALSE,GetLastError值就是Internal值。

2.当1中返回ERROR_IO_PENDING时:
这个时候就需要用到GetOverlappedResult了。

若Internal=0时表明返回STATUS_SUCCESS,于是GetOverlappedResult返回TRUE,即成功返回;
InternalHigh的值保存在lpNumberOfBytesTransferred中。

若Internal!=0表示出现错误,
于是GetOverlappedResult返回FALSE,GetLastError值就是Internal值。

/*++

 Routine Description:

 The GetOverlappedResult function returns the result of the last
 operation that used lpOverlapped and returned ERROR_IO_PENDING.

 Arguments:

 hFile - Supplies the open handle to the file that the overlapped
 structure lpOverlapped was supplied to ReadFile, WriteFile,
 ConnectNamedPipe, WaitNamedPipe or TransactNamedPipe.

 lpOverlapped - Points to an OVERLAPPED structure previously supplied to
 ReadFile, WriteFile, ConnectNamedPipe, WaitNamedPipe or
 TransactNamedPipe. //这个地址就是当初调用ReadFile是传递的参数的值,一定记住不能错。

 lpNumberOfBytesTransferred - Returns the number of bytes transferred
 by the operation.

 bWait -  A boolean value that affects the behavior when the operation
 is still in progress. 
If TRUE and the operation is still in progress,
GetOverlappedResult will wait for the operation to complete before returning.
 If FALSE and the operation is incomplete, GetOverlappedResult will return FALSE. 
In this case the extended error information available from
the GetLastError function will be set to ERROR_IO_INCOMPLETE.
//若当前还是ERROR_IO_PENDING则判断是否需要无限期的等待。

 Return Value:

 TRUE -- The operation was successful, the pipe is in the
 connected state.

 FALSE -- The operation failed. Extended error status is available using
 GetLastError.

 --*/
BOOL WINAPI GetOverlappedResult( 
  HANDLE hFile, 
  LPOVERLAPPED lpOverlapped,
  LPDWORD lpNumberOfBytesTransferred, 
  BOOL bWait )
{
  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 ); // WAIT_TIMEOUT if timeout interval elaspse, and no signaled
    }
    else // !bWait
    {
      WaitReturn = WAIT_TIMEOUT;
    }

    if ( WaitReturn == WAIT_TIMEOUT ) // WaitForSingleObject( ) or !bWait
    {
      //  !bWait and event in not signalled state
      SetLastError( ERROR_IO_INCOMPLETE );
      return FALSE;
    }

    if ( WaitReturn != WAIT_OBJECT_0 ) // WAIT_TIMEOUT, WAIT_FAILED, WAIT_ABANDONED
    {
      return FALSE;    // WaitForSingleObject calls BaseSetLastError
    }
  }
// if ( lpOverlapped->Internal != (DWORD)STATUS_PENDING )
*lpNumberOfBytesTransferred = (DWORD) lpOverlapped->InternalHigh; // InternalHigh的值保存在lpNumberOfBytesTransferred中。

if ( NT_SUCCESS( (NTSTATUS) lpOverlapped->Internal ) ) { return TRUE; // 若Internal=0时表明返回STATUS_SUCCESS } else { BaseSetLastNTError( (NTSTATUS) lpOverlapped->Internal ); return FALSE; // GetLastError值就是Internal值。 } }

The following C++ example shows how to test for the end of file
during an asynchronous read operation.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

#define BUF_SIZE (61)

LPCTSTR ErrorMessage( DWORD error )

// Routine Description:
//      Retrieve the system error message for the last-error code
{

  LPVOID lpMsgBuf;

  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
      | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
    MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), ( LPTSTR ) & lpMsgBuf, 0,
    NULL );

  return ( (LPCTSTR) lpMsgBuf );
}

void GoDoSomethingElse( void )

// Routine Description:
//     Placeholder to demo when async I/O might want to do
//     other processing.
{
  printf( "Inside GoDoSomethingElse()\n" );
}

DWORD AsyncTestForEnd( HANDLE hEvent, HANDLE hFile )

// Routine Description:
//      Demonstrate async ReadFile operations that can catch
//      End-of-file conditions. Unless the operation completes
//      synchronously or the file size happens to be an exact
//      multiple of BUF_SIZE, this routine will eventually force
//      an EOF condition on any file.

// Parameters:
//      hEvent - pre-made manual-reset event.
//
//      hFile - pre-opened file handle, overlapped.
//
//      inBuffer - the buffer to read in the data to.
//
//      nBytesToRead - how much to read (usually the buffer size).

// Return Value:
//      Number of bytes read.
{
  char inBuffer[ BUF_SIZE ];
  DWORD nBytesToRead = BUF_SIZE;
  DWORD dwBytesRead = 0;
  DWORD dwFileSize = GetFileSize( hFile, NULL );
  OVERLAPPED stOverlapped =
  { 0 };

  DWORD dwError = 0;
  LPCTSTR errMsg = NULL;

  BOOL bResult = FALSE;
  BOOL bContinue = TRUE;

  // Set up overlapped structure event. Other members are already 
  // initialized to zero.
  stOverlapped.hEvent = hEvent;

  // This is an intentionally brute-force loop to force the EOF trigger.
  // A properly designed loop for this simple file read would use the
  // GetFileSize API to regulate execution. However, the purpose here
  // is to demonstrate how to trigger the EOF error and handle it.

  while ( bContinue )
  {
    // Default to ending the loop.
    bContinue = FALSE;

    // Attempt an asynchronous read operation.
    bResult = ReadFile( hFile, inBuffer, nBytesToRead, &dwBytesRead,
      &stOverlapped );

    dwError = GetLastError( );

    // Check for a problem or pending operation. 
    if ( !bResult )
    {
      switch ( dwError )
      {

        case ERROR_HANDLE_EOF:
        {
          printf(
            "\nReadFile returned FALSE and EOF condition, async EOF not triggered.\n" );
          break;
        }
        case ERROR_IO_PENDING:
        {
          BOOL bPending = TRUE;

          // Loop until the I/O is complete, that is: the overlapped 
          // event is signaled.

          while ( bPending )
          {
            bPending = FALSE;

            // Pending asynchronous I/O, do something else
            // and re-check overlapped structure.
            printf( "\nReadFile operation is pending\n" );

            // Do something else then come back to check. 
            GoDoSomethingElse( );

            // Check the result of the asynchronous read
            // without waiting (forth parameter FALSE). 
            bResult = GetOverlappedResult( hFile, &stOverlapped, &dwBytesRead,
              FALSE );

            if ( !bResult )
            {
              switch ( dwError = GetLastError( ) )
              {
                case ERROR_HANDLE_EOF:
                {
                  // Handle an end of file
                  printf( "GetOverlappedResult found EOF\n" );
                  break;
                }

                case ERROR_IO_INCOMPLETE:
                {
                  // Operation is still pending, allow while loop
                  // to loop again after printing a little progress.
                  printf( "GetOverlappedResult I/O Incomplete\n" );
                  bPending = TRUE;
                  bContinue = TRUE;
                  break;
                }

                default:
                {
                  // Decode any other errors codes.
                  errMsg = ErrorMessage( dwError );
                  _tprintf( TEXT( "GetOverlappedResult failed (%d): %s\n" ),
                    dwError, errMsg );
                  LocalFree( (LPVOID) errMsg );
                }
              }
            }
            else
            {
              printf( "ReadFile operation completed\n" );

              // Manual-reset event should be reset since it is now signaled.
              ResetEvent( stOverlapped.hEvent );
            }
          }
          break;
        }

        default:
        {
          // Decode any other errors codes.
          errMsg = ErrorMessage( dwError );
          printf( "ReadFile GLE unhandled (%d): %s\n", dwError, errMsg );
          LocalFree( (LPVOID) errMsg );
          break;
        }
      }
    }
    else
    {
      // EOF demo did not trigger for the given file.
      // Note that system caching may cause this condition on most files
      // after the first read. CreateFile can be called using the
      // FILE_FLAG_NOBUFFERING parameter but it would require reads are
      // always aligned to the volume's sector boundary. This is beyond
      // the scope of this example. See comments in the main() function.

      printf( "ReadFile completed synchronously\n" );
    }

    // The following operation assumes the file is not extremely large, otherwise 
    // logic would need to be included to adequately account for very large
    // files and manipulate the OffsetHigh member of the OVERLAPPED structure.

    stOverlapped.Offset += dwBytesRead;
    if ( stOverlapped.Offset < dwFileSize )
      bContinue = TRUE;
  }

  return stOverlapped.Offset;
}

void __cdecl _tmain(int argc, TCHAR *argv[])

// To force an EOF condition, execute this application specifying a
// zero-length file. This is because the offset (file pointer) must be
// at or beyond the end-of-file marker when ReadFile is called. For
// more information, see the comments for the AsyncTestForEnd routine.

{
  HANDLE hEvent;
  HANDLE hFile;
  DWORD dwReturnValue;

  printf("\n");
  if( argc != 2 )
  {
    printf("ERROR:\tIncorrect number of arguments\n\n");
    printf("%s <file_name>\n", argv[0]);
    return;
  }

  hFile = CreateFile(argv[1],                // file to open
      GENERIC_READ,// open for reading
      FILE_SHARE_READ,// share for reading
      NULL,// default security
      OPEN_EXISTING,// existing file only
      FILE_FLAG_OVERLAPPED,// overlapped operation
      NULL);// no attr. template

  if (hFile == INVALID_HANDLE_VALUE)
  {
    DWORD dwError = GetLastError();
    LPCTSTR errMsg = ErrorMessage(dwError);
    printf("Could not open file (%d): %s\n", dwError, errMsg);
    LocalFree((LPVOID)errMsg);
    return;
  }

  hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

  if (hEvent == NULL)
  {
    DWORD dwError = GetLastError();
    LPCTSTR errMsg = ErrorMessage(dwError);
    printf("Could not CreateEvent: %d %s\n", dwError, errMsg);
    LocalFree((LPVOID)errMsg);
    return;
  }

  dwReturnValue = AsyncTestForEnd(hEvent, hFile);

  printf( "\nRead complete. Bytes read: %d\n", dwReturnValue);

  CloseHandle(hFile);
  CloseHandle(hEvent);
}

怎样使用overlapped I/O:

进行I/O操作时,指定overlapped方式
使用CreateFile (),将其第6个参数指定为FILE_FLAG_OVERLAPPED,
就是准备使用overlapped的方式构造或打开文件;
如果采用 overlapped,那么ReadFile()、WriteFile()的第5个参数必须提供一个指针,
指向一个OVERLAPPED结构。 OVERLAPPED用于记录了当前正在操作的文件一些相关信息。

//功能:从指定文件的1500位置读入300个字节

int main( )
{
  BOOL rc;
  HANDLE hFile;
  DWORD numread;
  OVERLAPPED overlap;
  char buf[ 512 ];
  char szPath = ”x:\\xxxx\xxxx”;

  //检查系统,确定是否支持overlapped,(NT以上操作系统支持OVERLAPPED)
  CheckOsVersion( );
  // 以overlapped的方式打开文件
  hFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );

  // OVERLAPPED结构实始化为0
  memset( &overlap, 0, sizeof( overlap ) );
  //指定文件位置是1500;
  overlap.Offset = 1500;

  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 );
  return EXIT_SUCCESS;
}

若有几个操作同一个文件时,怎么办?
我们可以利用OVERLAPPED结构中提供的event来解决上面遇到的问题。

注意,你所使用的event对象必须是一个MANUAL型的;否则,可能产生竞争条件。

//当读操作完成以后,gOverlapped[nIndex].hEvent会系统被激发
int QueueRequest( int nIndex, DWORD dwLocation, DWORD dwAmount )
{
  //构造一个MANUAL型的event对象
  ghEvents[ nIndex ] = CreateEvent( NULL, TRUE, FALSE, NULL );
  //将此event对象置入OVERLAPPED结构
  gOverlapped[ nIndex ].hEvent = ghEvents[ nIndex ];
  gOverlapped[ nIndex ].Offset = dwLocation;
  for ( i = 0; i < MAX_TRY_COUNT; i++ )
  {
    //文件ghFile唯一
    rc = ReadFile( ghFile, gBuffers[ nIndex ], &dwNumread,
      &gOverlapped[ nIndex ] );
    if ( rc )
      return TRUE;
    err = GetLastError( );
    if ( err == ERROR_IO_PENDING )
    {
      //当错误是ERROR_IO_PENDING,那意味着读文件的操作还在进行中
      return TRUE;
    }
    // 处理一些可恢复的错误
    if ( err == ERROR_INVALID_USER_BUFFER || err == ERROR_NOT_ENOUGH_QUOTA
      || err == ERROR_NOT_ENOUGH_MEMORY )
    {
      sleep( 50 );
      continue; //重试
    }
    // 如果GetLastError()返回的不是以上列出的错误,放弃
    break;
  }

  return -1;
}

int main( )
{
  int i;
  BOOL rc;
  char szPath = ”x:\\xxxx\xxxx”;
  // 以overlapped的方式打开文件
  ghFile = CreateFile( szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );
  for ( i = 0; i < MAX_REQUESTS; i++ )
  {
  //将同一文件按几个部分按overlapped方式同时读
  //注意看QueueRequest函数是如何运做的,每次读16384个块
    QueueRequest( i, i * 16384, READ_SIZE );
  }
  
  // 等候所有操作结束;
  //隐含条件:当一个操作完成时,其对应的event对象会被激活
  WaitForMultipleObjects( MAX_REQUESTS, ghEvents, TRUE, INFINITE );
  // 收尾操作
  for ( i = 0; i < MAX_REQUESTS; i++ )
  {
    DWORD dwNumread;
    rc = GetOverlappedResult( ghFile, &gOverlapped[ i ], &dwNumread, FALSE );
    CloseHandle( gOverlapped[ i ].hEvent );
  }
  
  CloseHandle( ghFile );
  return EXIT_SUCCESS;
}

 

posted @ 2013-04-19 13:53  IAmAProgrammer  阅读(4250)  评论(0编辑  收藏  举报