1. 程式人生 > >C/C++串列埠通訊(2)-重疊操作

C/C++串列埠通訊(2)-重疊操作

重疊操作時,操作還未完成函式就返回。
重疊I/O非常靈活,它也可以實現阻塞。有兩種方法可以等待操作完成:
一種方法是用WaitForSingleObject這樣的等待函式來等待OVERLAPPED結構的hEvent成員;
另一種方法是呼叫GetOverlappedResult函式等待。

//OVERLAPPED結構
typedef struct _OVERLAPPED { // o
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;//讀寫事件。
} OVERLAPPED;

在使用ReadFile和WriteFile重疊操作時,執行緒需要建立OVERLAPPED結構以供這兩個函式使用。
執行緒通過OVERLAPPED結構獲得當前的操作狀態,該結構最重要的成員是hEvent。

當串列埠使用非同步通訊時,函式返回時操作可能還沒有完成,程式可以通過檢查該事件得知是否讀寫完畢。
當呼叫ReadFile, WriteFile 函式的時候,該成員會自動被置為無訊號狀態;當重疊操作完成後,該成員變數會自動被置為有訊號狀態。

//GetOverlappedResult函式
BOOL GetOverlappedResult( HANDLE hFile, // 串列埠的控制代碼 ,指向重疊操作開始時指定的OVERLAPPED結構 
LPOVERLAPPED lpOverlapped, // 指向一個32位變數,該變數的值返回實際讀寫操作傳輸的位元組數。
LPDWORD lpNumberOfBytesTransferred, // 該引數用於指定函式是否一直等到重疊操作結束。
//如果該引數為TRUE,函式直到操作結束才返回。 //如果該引數為FALSE,函式直接返回, //這時如果操作沒有完成,通過呼叫GetLastError()函式會返回ERROR_IO_INCOMPLETE。 BOOL bWait );

該函式返回重疊操作的結果,用來判斷非同步操作是否完成,它是通過判斷OVERLAPPED結構中的hEvent是否被置位來實現的。

非同步讀串列埠的示例程式碼(1):
用WaitForSingleObject函式來等待OVERLAPPED結構的hEvent成員:

char lpInBuffer[1024];
DWORD dwBytesRead=1024;
COMSTAT ComStat;
DWORD dwErrorFlags;
OVERLAPPED m_osRead;
memset(&m_osRead,0
,sizeof(OVERLAPPED)); m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); ClearCommError(hCom,&dwErrorFlags,&ComStat); dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue); if(!dwBytesRead) return FALSE; BOOL bReadStatus; bReadStatus=ReadFile(hCom,lpInBuffer, dwBytesRead,&dwBytesRead,&m_osRead); if(!bReadStatus) //如果ReadFile函式返回FALSE { if(GetLastError()==ERROR_IO_PENDING) //GetLastError()函式返回ERROR_IO_PENDING,表明串列埠正在進行讀操作 { WaitForSingleObject(m_osRead.hEvent,2000); //使用WaitForSingleObject函式等待,直到讀操作完成或延時已達到2秒鐘 //當串列埠讀操作進行完畢後,m_osRead的hEvent事件會變為有訊號 PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); return dwBytesRead; } return 0; } PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); return dwBytesRead;

簡要說明:
在使用ReadFile 函式進行讀操作前,應先使用ClearCommError函式清除錯誤。
ClearCommError函式的原型如下:

BOOL ClearCommError( HANDLE hFile,// 串列埠控制代碼
 LPDWORD lpErrors, // 指向接收錯誤碼的變數
 LPCOMSTAT lpStat // 指向通訊狀態緩衝區 );

該函式獲得通訊錯誤並報告串列埠的當前狀態,同時,該函式清除串列埠的錯誤標誌以便繼續輸入、輸出操作。
引數lpStat指向一個COMSTAT結構,該結構返回串列埠狀態資訊。
COMSTAT結構 COMSTAT結構包含串列埠的資訊,結構定義如下:

typedef struct _COMSTAT { 
// cst 
DWORD fCtsHold : 1; // Tx waiting for CTS signal 
DWORD fDsrHold : 1; // Tx waiting for DSR signal 
DWORD fRlsdHold : 1; // Tx waiting for RLSD signal 
DWORD fXoffHold : 1; // Tx waiting, XOFF char rec''d 
DWORD fXoffSent : 1; // Tx waiting, XOFF char sent 
DWORD fEof : 1; // EOF character sent 
DWORD fTxim : 1; // character waiting for Tx 
DWORD fReserved : 25; // reserved 
DWORD cbInQue; // bytes in input buffer 
DWORD cbOutQue; // bytes in output buffer
 } COMSTAT, *LPCOMSTAT;

本文只用到了cbInQue成員變數,該成員變數的值代表輸入緩衝區的位元組數。
最後用PurgeComm函式清空串列埠的輸入輸出緩衝區。

非同步讀串列埠示例程式碼(2):
呼叫GetOverlappedResult函式等待

char lpInBuffer[1024];
DWORD dwBytesRead=1024;
BOOL bReadStatus;
DWORD dwErrorFlags;
COMSTAT ComStat;
OVERLAPPED m_osRead;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
if(!ComStat.cbInQue) return 0;
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
bReadStatus=ReadFile(hCom, lpInBuffer,dwBytesRead, &dwBytesRead,&m_osRead);
if(!bReadStatus) //如果ReadFile函式返回FALSE
{ 
    if(GetLastError()==ERROR_IO_PENDING)
    {
         GetOverlappedResult(hCom, &m_osRead,&dwBytesRead,TRUE);
        // GetOverlappedResult函式的最後一個引數設為TRUE,
        //函式會一直等待,直到讀操作完成或由於錯誤而返回。
        return dwBytesRead; 
    }
    return 0; 
}
return dwBytesRead;

非同步寫串列埠的示例程式碼:

char buffer[1024];
DWORD dwBytesWritten=1024;
DWORD dwErrorFlags;
COMSTAT ComStat;
OVERLAPPED m_osWrite;
BOOL bWriteStat;
bWriteStat=WriteFile(hCom,buffer,dwBytesWritten, &dwBytesWritten,&m_OsWrite);
if(!bWriteStat)
{ 
    if(GetLastError()==ERROR_IO_PENDING)
    { 
        WaitForSingleObject(m_osWrite.hEvent,1000);
        return dwBytesWritten;
    }
    return 0; 
}
return dwBytesWritten;

關閉串列埠

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