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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM