(一)Windows API串口通信編程概述
Windows環(huán)境下的串口編程與DOS環(huán)境下的串口編程有很大不同。Windows環(huán)境下的編程的最大特征之一就是設備無關(guān)性,它通過設備驅(qū)動程序?qū)?/span>Windows應用程序同不同的外部設備隔離。Windows封裝了Windows的通信機制,這種方式稱為通信API,Windows程序可以利用Windows通信API進行編程,不用對硬件直接進行操作。這種體系被稱為Windows開放式服務體系(WOSA,Windows Open Services Architectures)。
早期的Windows3.x與Windows 9x/NT/2000的通信API有很大不同,在16位的串行通信程序中,一般使用16位的Windows API通信函數(shù)。為使大家對串口通信有一全面的理解,下面簡單介紹一下16位的Windows API通信函數(shù):
(1) 打開和關(guān)閉串口
OpenComm()打開串口資源,并指定輸入、輸出緩沖區(qū)的大?。ㄒ宰止?jié)計);
CloseComm()關(guān)閉串口;
例:
int idComDev;
idComdev=OpenComm(“COM1”,1024,512);
CloseComm(idComDev);
(2) 初始化串口
BuildCommDCB()、setCommState()填寫設備控制塊DCB,然后對已打開的串口進行參數(shù)配置,例:
DCB dcb;
BuildCommDCB(〝COM1:2400,n,8,1〞,&dcb);
SetCommState(&dcb);
(3) 對串口進行讀寫
ReadComm、WriteComm()對串口進行讀寫操作,即數(shù)據(jù)的接收和發(fā)送。例:
char *m_pReceive; int count;
ReadComm(idComDev,m_pReceive,count);
Char wr[30]; int count2;
WriteComm(idComDev,wr,count2);
通過對以上的描述我們可以看出,16位以下的串口通信程序最大的特點就在于串口等外部設備的操作有自己特有的API函數(shù)。
Windows 9x/NT/2000中的API一般都支持32位的操作,因此又稱為Win32API。為了在上述系統(tǒng)中實現(xiàn)串行數(shù)據(jù)傳送,可以使用Win32通信API。Win32通信API基本上是一個串行端口API,不是很適合于局域網(wǎng)(LAN)通信。雖然在線路上發(fā)送數(shù)據(jù)之前,LAN通常將數(shù)據(jù)位串行化,這和窗口或調(diào)制解調(diào)器發(fā)送數(shù)據(jù)之前所作的工作一模一樣,但局域網(wǎng)使用的線路的位數(shù)通常比串口少,而且還使用與串口協(xié)議很少有類似之處的訪問、路由、安全性和糾錯協(xié)議。局域網(wǎng)通信所需要的協(xié)議層使得Win32通信API對于這些應用來說很不理想。因此,在網(wǎng)絡通信和連接方面,TCP/IP協(xié)議要比Win32通信API更適合一些。
Windows操作系統(tǒng)是一個可搶占式的操作系統(tǒng),所以Windows應用程序常常有被別的程序搶占時間片的可能,因此Win32通信API也不能用于實時通信。實時通信的質(zhì)量與時間密切相關(guān)。例如,數(shù)字化音頻數(shù)據(jù)是實時數(shù)據(jù),因為話音的質(zhì)量依賴于播放它的速率。在錄制音頻時,它就以某個速度被數(shù)字化了,該速度就是人們所熟知的采樣速率。聲音必須以相同的采樣率重放,否則聽起來就會太慢或太快。實際中的視頻播放,也不是實時播放,那僅僅是存放在緩沖中的那部分數(shù)據(jù)。因此,不需要許多協(xié)議層的交互式、非實時的通信可以采用Win32通信API來實現(xiàn)。Win32通信API把串口操作(以及并口等)和文件操作統(tǒng)一起來了,使用類似的操作來實現(xiàn)。
(二) Windows串口通信相關(guān)API函數(shù)
“工欲善其事,必先利其器”,這一節(jié)將從使用的角度出發(fā),對和串口通信相關(guān)的32位的Windows API函數(shù)進行介紹,力圖使你們對其有個全面、準確的認識。
2.1 打開和關(guān)閉串口
1. 打開串口
在32位的Windows系統(tǒng)中,串口和其它通信設備是作為文件處理的。串口的打開、關(guān)閉、讀取和寫入所用的函數(shù)與操作文件的函數(shù)完全一致。
通信會話以調(diào)用CreateFile()開始。CreateFile()為讀訪問、寫訪問或讀寫訪問“打開”串口。按照Windows的通常做法,CreateFile()返回一個句柄,隨后在打開的端口的操作中使用CreateFile()函數(shù)非常復雜,復雜性的原因之一是它是通用的??梢允褂?/span>CreateFile打開已存在的文件,創(chuàng)建新文件和打開根本就不是文件的設備,例如串口、并口和調(diào)制解調(diào)器。CreateFile()函數(shù)聲明如下:
HANDLE CreateFile(
LPCTSTR lpszName,
DWORD fdwAccess,
DWORD fdwShareMode,
LPSECURITY_ATTRIBUTES lpsa,
DWORD fdwCreate,
DWORD fdwAttrsAndFlags,
HANDLE hTemplateFile
)
CreateFile函數(shù)中的參數(shù)解釋如下:
·lpszName:指定要打開的串口邏輯名,用字符串表示,如“COM1”和“COM2”分別表示串口1和串口2。
·fdwAccess:用來指定串口訪問的類型。與文件一樣,串口也是可以被打開以供讀取、寫入或者兩者兼有。
GENERIC_READ位讀取訪問打開端口,GENERIC_READ位寫訪問打開端口。這兩個常數(shù)定義如下:
const GENERIC_READ = 0x80000000h;
const GENERIC_WRITE = 0x40000000h;
用戶可以用邏輯操作將這兩個標識符連接起來,為讀/寫訪問權(quán)限打開端口。因為大部分串口通信都是雙向的,因此常常在設置中將兩個標識符連接起來使用。如:
fdwAccess = GENERIC_READ | GENERIC_WRITE;
·fdwShareMode:指定該端口的共享屬性。該參數(shù)是為那些由許多應用程序共享的文件提供的。對于不能共享的串口,它必須設置為0。這就是文件與通信設備之間的主要差異之一。如果在當前的應用程序調(diào)用CreateFile()時,另一個應用程序已經(jīng)打開了串口,該函數(shù)就會返回錯誤代碼,原因是兩個應用程序不能共享一個端口。然而,同一個應用程序的多個線程可以共享由CreateFile()返回的端口句柄,并且根據(jù)安全性屬性設置,該句柄可以被打開端口的應用程序的子程序所繼承。
·Ipsa:引用安全性屬性結(jié)構(gòu)(SECURITY_ARRTIBUTES),該結(jié)構(gòu)定義了一些屬性,例如通信句柄如何被打開端口的應用程序的子程序所繼承。將該參數(shù)設置為NULL將為該端口分配缺省的安全性屬性。子應用程序所繼承的缺省屬性是該端口不能被繼承的。
安全屬性結(jié)構(gòu)SECURITY_ARRTIBUTES結(jié)構(gòu)聲明如下:
typedef struct_SECURITY_ARRTIBUTE {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ARRTIBUTE;
SECURITY_ARRTIBUTES結(jié)構(gòu)成員nLength指明該結(jié)構(gòu)的長度,lpSecurityDescriptor指向一個安全描述字符,bInheritHandle表明句柄是否能被繼承。
·fdwCreate:指定如果CreateFile()正在被已有的文件調(diào)用時應采取的動作。因為串口總是存在,fdwCreate必須設置成OPEN_EXISTING。該標志告訴Windows不用企圖創(chuàng)建新端口,而是打開已經(jīng)存在的端口。OPEN_EXISTING常數(shù)定義為:
const OPEN_EXISTING = 3;
·fdwAttrsAndFlags:描述了端口的各種屬性。對于文件來說,有可能具有很多屬性,但對于串口,唯一有意義的設置是FILE_FLAG_OVERLAPPED。當創(chuàng)建時指定該設置,端口I/O可以在后臺進行(后臺I/O也叫異步I/O)。FILE_FLAG_OVERLAPPED常數(shù)定義如下:
const FILE_FLAG_OVERLAPPED = 0x40000000h
·hTemplateFile:指向模板文件的句柄,當端口處于打開狀態(tài)時,不使用該參數(shù),因而必須置成0。
調(diào)用CreateFile()函數(shù)打開COM1串口操作的例子如下所示:
HANDLE hCom;
DWORD dwError;
hCom=CreateFile(“COM1”, // 文件名
GENERIC_READ | GENERIC_WRITE, // 允許讀和寫
0, // 獨占方式
NULL,
OPEN_EXISTING, // 打開而不是創(chuàng)建
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // 重疊方式
NULL
);
if(hCom = = INVALID_HANDLE_VALUE)
{
dwError=GetLastError(); // 處理錯誤
}
一旦端口處于打開狀態(tài),就可以分配一個發(fā)送緩沖區(qū)和接收緩沖區(qū),并且通過調(diào)用SetupComm()實現(xiàn)其它初始化工作。也可以不調(diào)用SetupComm()函數(shù),Windows系統(tǒng)也會分配缺省的發(fā)送和接收緩沖區(qū),并且初始化端口。但為了保證緩沖區(qū)的大小與實際需要的一致,最好還是調(diào)用該函數(shù)。SetupComm()函數(shù)聲明如下:
BOOL SetupComm(
HANDLE hFile, // 通信設備句柄
DWORD dwInQueue, // 輸入緩沖區(qū)大小
DWORD dwOutQueue // 輸出緩沖區(qū)大小
);
SetupComm()函數(shù)中各項含義說明如下:
·hFile: 由GreatFile()返回的指向已打開端口的句柄。
·dwInQueue和dwOutQueue: 接收緩沖區(qū)的大小和發(fā)送緩沖區(qū)的大小。這兩個定義并非是實際的緩沖區(qū)的大小,指定的大小僅僅是“推薦的”大小,而Windows可以隨意分配任意大小的緩沖區(qū)。Windows設備驅(qū)動程序可以獲得這兩個數(shù)據(jù),并不直接分配大小,而使用來優(yōu)化性能和避免緩沖區(qū)超限。
注意:當使用CreateFile()函數(shù)打開串口時:為實現(xiàn)調(diào)制解調(diào)器的排他性訪問,共享標識必須設為零;創(chuàng)建標識必須設為OPEN_EXISTING;模板句柄必須置為空。
2. 關(guān)閉串口
關(guān)閉串口比打開串口簡單得多,只需要調(diào)用CloseHandle()函數(shù)關(guān)閉由CreateHandle()函數(shù)返回得句柄即可。
CloseHandle函數(shù)聲明如下:
BOOL CloseHandle(
HANDLE hObject // 需關(guān)閉的設備句柄
);
使用串口時一般要關(guān)閉它,如果忘記關(guān)閉串口,串口就會始終處于打開狀態(tài),其它應用程序就不能打開并使用串口了。
2.2 串口配置和串口屬性
Windows 9x/NT/2000中配置串口提供了比Windows的早期版本更為強大的功能,當然相應也更加復雜。CreateFile函數(shù)打開串口后,系統(tǒng)將根據(jù)上次打開串口時設置的值來初始化串口,可以集成上次打開操作后的數(shù)值,包括設備控制塊(DCB)和超時控制結(jié)構(gòu)(COMMTIMEOUTS)。如果是首次打開串口,Windows操作系統(tǒng)就會使用缺省的配置。
1. 串口配置
Windows 9x/NT/2000使用GetCommState()函數(shù)獲取串口的當前配置,使用SetCommState()重新分配串口資源的各個參數(shù)。
GetCommState()函數(shù)聲明如下:
BOOL GetCommState(
HANDLE hFile, // 通信設備句柄
LPDCB lpDCB // 指向device-control block structure的指針
);
其中的參數(shù)說明如下:
·hFile:由CreateFile()函數(shù)返回的指向已打開串口的句柄。
·lpDCB:一個非常重要的結(jié)構(gòu)—設備控制塊DCB ( Device Control Block )。
DCB結(jié)構(gòu)的主要參數(shù)說明如下:
·DCBLength: 一字節(jié)為單位指定的DCB結(jié)構(gòu)的大小。
·Baudrate: 用于指定串口設備通信的數(shù)據(jù)傳輸速率,它可以是實際的數(shù)據(jù)傳輸速率數(shù)值,也可以是下列數(shù)據(jù)之一:CBR_110, CBR_19200, CBR_300, CBR_38400, CBR_600, CBR_56000, CBR_1200, CBR_57600, CBR_2400, CBR_115200, CBR_4800, CBR_12800, CBR_9600, CBR_25600, CBR_14400。
·fBinary: 指定是否允許二進制。Win32API不支持非二進制傳輸,因此這個參數(shù)必須設置為TRUE,如果設置為FALSE則不能正常工作。
·fParity: 指定是否允許奇偶校驗,如果這個參數(shù)設置為TRUE,則執(zhí)行奇偶校驗并報告錯誤信息。
·fOutxCtsFlow: 指定CTS是否用于檢測發(fā)送流控制。當該成員為TRUE,而CTS為OFF時,發(fā)送將被掛起,直到CTS置ON。
·fOutxDsrFlow: 指定DSR是否用于檢測發(fā)送流控制,當該成員為TRUE,而DSR為OFF時,發(fā)送將被掛起,直到DSR置ON。
·fDtrControl: 指定DTR流量控制,可以是表1中的任一值。
表1 DTR流量控制
值 | 功能描述 |
DTR_CONTROL_DISABLE | 禁止DTR線,并保持禁止狀態(tài) |
DTR_CONTROL_ENABLE | 允許DTR線,并保持允許狀態(tài) |
DTR_CONTROL_HANDSHAKE | 允許DTR握手,如果允許握手,則不允許應用程序使用EscapeCommFunction函數(shù)調(diào)整線路 |
·fDsrSensitivity: 指定通信驅(qū)動程序?qū)?/span>DTR信號線是否敏感,如果該位置設為TRUE時,DSR信號為OFF,接收的任何字節(jié)將被忽略。
·fTXContinueOnXoff: 指定當接收緩沖區(qū)已滿,并且驅(qū)動程序已經(jīng)發(fā)送出XoffChar字符時發(fā)送是否停止。當該成員為TRUE時,在接收緩沖區(qū)內(nèi)接收到了緩沖區(qū)已滿的字節(jié)XoffLim,并且驅(qū)動程序已經(jīng)發(fā)送出XoffChar字符終止接收字節(jié)之后,發(fā)送繼續(xù)進行。該成員為FALSE時,接收緩沖區(qū)接收到代表緩沖區(qū)已空的字節(jié)XonLim,并且驅(qū)動程序已經(jīng)發(fā)送出恢復發(fā)送的XonChar字符后,發(fā)送可以繼續(xù)進行。
·fOutX: 該成員為TRUE時,接收到XoffChar之后停止發(fā)送,接收到XonChar之后發(fā)送將重新開始。
·fInX: 該成員為TRUE時,接收緩沖區(qū)內(nèi)接收到代表緩沖區(qū)滿的字節(jié)XoffLim之后,XoffChar發(fā)送出去,接收緩沖區(qū)接收到代表緩沖區(qū)已空的字節(jié)XonLim之后,XonChar發(fā)送出去。
·fErrorChar: 當該成員為TRUE,并且fParity為TRUE時,就會用ErrorChar成員指定的字符來代替奇偶校驗錯誤的接收字符。
·fNull: 指明是否丟棄接收到的NULL( ASCII 0 )字符,該成員為TRUE時,接收時去掉空(零值)字節(jié);反之則不丟棄。
表2 RTS 流量控制
值 | 功能描述 |
RTS_CONTROL_DISABLE | 打開設備時禁止RTS線,并保持禁止狀態(tài) |
RTS_CONTROL_ENABLE | 打開設備時允許RTS線,并保持允許狀態(tài) |
DTR_CONTROL_HANDSHAKE | 允許握手。在接收緩沖區(qū)小于半滿時將RTS 置為ON,在接收緩沖區(qū)超過3/4時將RTS置為OFF。如果允許握手,則不允許應用程序使用EscapeCommFunction函數(shù)調(diào)整線路 |
DTR_CONTROL_TOGGLE | 當發(fā)送的字節(jié)有效,將RTS置為 ON,發(fā)送完緩沖區(qū)的所有字節(jié)后, RTS置為OFF |
·fRtsControl: 指定 RTS 流量控制,可以取表2中的值。0值和DTR_CONTROL_HANDSHAKE等價。
·fAbortOnError: 如果發(fā)送錯誤,指定是否可以終止讀、寫操作。如果該位為TRUE,當發(fā)生錯誤時,驅(qū)動程序以出錯狀態(tài)終止所有的讀寫操作。只有當應用程序調(diào)用ClearCommError()函數(shù)處理后,串口才能接收隨后的通信操作。
·fDummy2: 保留的位,沒有使用。
·wReserved:沒有使用,必須為零。
·XonLim: 指定在XOFF字符發(fā)送之前接收到緩沖區(qū)中可允許的最小字節(jié)數(shù)。
·XoffLim: 指定在XOFF字符發(fā)送之前緩沖區(qū)中可允許的最小可用字節(jié)數(shù)
·ByteSize: 指定端口當前使用的數(shù)據(jù)位數(shù)。
·Parity: 指定端口當前使用的奇偶校驗方法。它的可能值如表3所示。
·StopBits: 指定串口當前使用的停止位數(shù),可能值如表4所示。
表3 奇偶校驗方法
值 | 功能描述 |
EVENPARITY | 偶校驗 |
MARKPARITY | 標號校驗 |
NOPARITY | 無校驗 |
ODDPARITY | 奇校驗 |
SPACEPARITY | 空格效益 |
表4 停止位數(shù)描述
值 | 功能描述 |
ONESTOPBIT | 1位停止位 |
ONE5STOPBITS | 1.5位停止位 |
TWOSTOPBITS | 2位停止位 |
·XonChar: 指明發(fā)送和接收的XON字符值,它表明允許繼續(xù)傳輸。
·XoffChar: 指明發(fā)送和接收的XOFF字符值,它表示暫停數(shù)據(jù)傳輸。
·ErrorChar: 本字符用來代替接收到的奇偶校驗發(fā)生錯誤的字符。
·EofChar: 用來表示數(shù)據(jù)的結(jié)束。
·EvtChar: 事件字符。當接收到此字符的時候,會產(chǎn)生一個事件。
·wReserved1: 保留的位,沒有使用。
如果GetCommState()函數(shù)調(diào)用成功,則返回值不為零。若函數(shù)調(diào)用失敗,則返回值為零,如果想得到進一步的錯誤信息,可以調(diào)用GetLastError()函數(shù)來獲取。
GetLastError()函數(shù)也是Win32API函數(shù),它的聲明如下:
DWORD GetLastError(VOID);
如果應用程序只需要修改一部分配置的時候,可以通過GetCommState()函數(shù)獲得當前的DCB結(jié)構(gòu),然后更改DCB結(jié)構(gòu)中的參數(shù),調(diào)用SetCommState()函數(shù)配置修改過的DCB來配置端口。SetCommState()函數(shù)聲明如下:
BOOL SetCommState (
HANDLE hFile, // 已打開的串口的句柄
LPDCB lpDCB // 指向DCB結(jié)構(gòu)的指針
);
SetCommState()函數(shù)的第一參數(shù)hFile是由CreateFile()函數(shù)返回的已打開的串口的句柄,第二個參數(shù)也是指向DCB結(jié)構(gòu)的。如果函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。出錯時可以調(diào)用GetLastError()函數(shù)獲得進一步的出錯信息。SetCommState()函數(shù)調(diào)用的DCB結(jié)構(gòu)中的XonChar等價于XoffChar成員,則SetCommState()函數(shù)會調(diào)用失敗。
DCB最經(jīng)常改變的參數(shù)是數(shù)據(jù)傳輸速率、奇偶校驗的方法以及數(shù)據(jù)位和停止位數(shù)。Windows為改變這些設置提供了BuildCommDCB函數(shù),函數(shù)聲明如下:
BOOL BuildCommDCB(
LPCTSTR lpDef, // 設置的字符串
LPDCB lpDCB // 指向DCB結(jié)構(gòu)的指針
);
BuildCommDCB()參數(shù)包含新設置的字符串和一個DCB結(jié)構(gòu)的參數(shù),該設置將提供給DCB結(jié)構(gòu)。新設置的字符串與DOS系統(tǒng)或者Windows NT/2000系統(tǒng)中的Mode命令格式相同。如:
baud=1200 parity=N data=8 stop=1
這條語句將數(shù)據(jù)傳輸速率設置為1200bits/s,關(guān)閉奇偶校驗,數(shù)據(jù)位數(shù)設為8,停止位數(shù)設為1。與在DOS或Windows NT/2000系統(tǒng)中一樣,該字符串不包括串口的名稱,實際上這個函數(shù)并不改變端口的設置,因此沒有必要標識該串口,當然這個串口必須是有效的串口。新的設置只是簡單地拷貝到已提供好的DCB結(jié)構(gòu)中,要使新設置生效,還必須調(diào)用SetCommState()函數(shù)。BuildCommDCB()支持老的和新的各種版本的Mode命令,缺省情況下,BuildCommDCB()函數(shù)禁止XON/XOFF和硬件流的控制。如果使用硬件流控制,則必須設置DCB結(jié)構(gòu)的各個成員的值。如果這個函數(shù)調(diào)用成功,則返回值不為零。如果想得到進一步的錯誤信息,可以調(diào)用GetLastError()函數(shù)來獲取。
2. 緩沖區(qū)控制
Win32通信API除了提供SetupComm()函數(shù)實現(xiàn)初始化的緩沖區(qū)控制外,還提供了PurgeComm()函數(shù)和FlushFileBuffers()函數(shù)來進行緩沖區(qū)操作。
PurgeComm()函數(shù)的聲明如下:
BOOL PurgeComm(
HANDLE hFile, // 返回的句柄
DWORD dwFlags // 執(zhí)行的動作
);
參數(shù)hFile指向由CreateFile函數(shù)返回的句柄,dwFlags表示執(zhí)行的動作,這個參數(shù)可以是表表5中的任一個。參數(shù)hFile指向由CreateFile函數(shù)返回的句柄,可以調(diào)用GetLastError()函數(shù)獲得進一步的錯誤信息。
表5 停止位數(shù)和奇偶校驗位
值 | 描述 |
PURGE_TXABORT | 即使發(fā)送操作沒有完成,也終止所有的重疊發(fā)送操作,立即返回 |
PURGE_RXABORT | 即使接收操作沒有完成,也終止所有的重疊接收操作,立即返回 |
PURGE_TXCLEAR | 清除發(fā)送緩沖區(qū) |
PURGE_RXCLEAR | 清除接收緩沖區(qū) |
由上面的敘述可以看出,PurgeComm()函數(shù)可以在讀寫操作的同時,清空緩沖區(qū)。當應用程序在讀寫操作時調(diào)用PurgeComm()函數(shù),不能保證緩沖區(qū)內(nèi)的所有字符都被發(fā)送。如果要保證緩沖區(qū)的所有字符都被發(fā)送,應該調(diào)用FlushFileBuffer()函數(shù)。該函數(shù)只受流量控制的支配,不受超時控制的支配,它在所有的寫操作完成后才返回。
FlushFileBuffers()的函數(shù)聲明如下:
BOOL FlushFileBuffers(
HANDLE hFile // 函數(shù)打開的句柄
);
參數(shù)hFile指向由CreateFile函數(shù)打開的句柄,如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。出錯時可以調(diào)用GetLastError()函數(shù)獲得進一步的出錯信息。
2.3 讀寫串口
利用Win32通信API讀寫串口時,既可以同步執(zhí)行,也可以重疊(異步)執(zhí)行。在同步執(zhí)行時,函數(shù)直到操作完成后才返回。這意味著在同步執(zhí)行時線程會被阻塞,從而導致效率降低。在重疊執(zhí)行時,即使操作還未完成,調(diào)用的函數(shù)也會立即返回。費時的I/O操作在后臺進行,這樣線程就可以做其它工作。例如,線程可以在不同的句柄上同時執(zhí)行I/O操作,甚至可以在同一句柄上同時進行讀寫操作?!爸丿B”一詞的含義就在于此。
1. 讀串口操作
程序可以使用Win32API ReadFile()函數(shù)或者ReadFileEx()函數(shù)從串口中讀取數(shù)據(jù)。ReadFile()函數(shù)對同步或異步操作都支持,而ReadFileEx()只支持異步操作。這兩個函數(shù)都受到函數(shù)是否異步操作、超時操作等有關(guān)參數(shù)的影響和限定。
ReadFile()函數(shù)聲明如下:
BOOL ReadFile(
HANDLE hFile, // 指向標識的句柄
LPVOID lpBuffer, // 指向一個緩沖區(qū)
DWORD nNumberOfBytesToRead, // 讀取的字節(jié)數(shù)
LPDWORD lpNumberOfBytesRead, // 指向調(diào)用該函數(shù)讀出的字節(jié)數(shù)
LPOVERLAPPED lpOverlapped // 一個OVERLAPPED的結(jié)構(gòu)
);
其中主要參數(shù)介紹如下:
·hFile:指向標識的句柄。對串口來說,就是由CreateFile函數(shù)返回的句柄。該句柄必須擁有GENERIC_READ的權(quán)限。
·lpBuffer:指向一個緩沖區(qū),該緩沖區(qū)主要用來存放從串口設備中讀取的數(shù)據(jù)。
·nNumberOfBytesToRead:指定要從串口設備讀取的字節(jié)數(shù)。
·lpNumberOfBytesRead:指向調(diào)用該函數(shù)讀出的字節(jié)數(shù)。ReadFile()在讀操作前,首先將其設置為0。Windows NT/2000中當lpOverlapped沒有設置時,lpNumberOfBytesRead必須設置。當lpOverlapped設置時,lpNumberOfBytesRead可以不設置。這是可以調(diào)用GetOverlappedResult()函數(shù)獲取實際的讀取數(shù)值。Windows 9x中這個參數(shù)一定要設置。
·lpOverlapped:是一個OVERLAPPED的結(jié)構(gòu),該結(jié)構(gòu)將在后面介紹。如果hFile以FILE_FLAG_OVERLAPPED方式常見,則需要此結(jié)構(gòu);否則,不需要此結(jié)構(gòu)。
需要注意的是如果該函數(shù)因為超時而返回,那么返回值是TRUE。參數(shù)lpOverlapped 在操作時應該指向一個OVERLAPPED的結(jié)構(gòu),如果該參數(shù)為NULL ,那么函數(shù)將進行同步操作,而不管句柄是否是由 FILE_FLAG_OVERLAPPED 標志建立的。當ReadFile 返回FALSE時,不一定就是操作失敗,線程應該調(diào)用GetLastError函數(shù)分析返回的結(jié)果。例如,在重疊操作時如果操作還未完成函數(shù)返回,那么函數(shù)就返回FALSE,而且GetLastError函數(shù)返回ERROR_IO_PENDING。
2. 寫串口操作
可以使用Win32API函數(shù)WriteFile() 或者WriteFileEx()向串口中寫數(shù)據(jù)。WriteFile()函數(shù)對同步或異步操作都支持,而WriteFileEx()只支持異步操作。這兩個函數(shù)都受到函數(shù)是否異步操作、超時操作等有關(guān)參數(shù)的影響和限定。
WriteFile()函數(shù)聲明如下:
BOOL WriteFile(
HANDLE hFile, // 指向標識的句柄
LPCVOID lpBuffer, // 指向一個緩沖區(qū)
DWORD nNumberOfBytesToWrite, // 指定要向串口設備寫入的字節(jié)數(shù)
LPDWORD lpNumberOfBytesWritten, // 指向調(diào)用該函數(shù)已寫入的字節(jié)數(shù)
LPOVERLAPPED lpOverlapped // 一個OVERLAPPED的結(jié)構(gòu)
);
其中主要參數(shù)介紹如下:
·hFile:指向標識的句柄。對串口來說,就是由CreateFile函數(shù)返回的句柄。該句柄必須擁有GENERIC_WRITE的權(quán)限。
·lpBuffer:指向一個緩沖區(qū),該緩沖區(qū)主要用來存放待寫入串口設備的數(shù)據(jù)。
·nNumberOfBytesToWrite:指定要向串口設備寫入的字節(jié)數(shù)。
·lpNumberOfBytesWritten:指向調(diào)用該函數(shù)已寫入的字節(jié)數(shù)。WriteFile()在寫操作前,首先將其設置為0。Windows NT/2000中當lpOverlapped沒有設置時,lpNumberOfBytesWritten必須設置。當lpOverlapped設置時,lpNumberOfBytesWritten可以不設置。這是可以調(diào)用GetOverlappedResult()函數(shù)獲取實際的讀取數(shù)值。Windows 9x中這個參數(shù)一定要設置。
·lpOverlapped:是一個OVERLAPPED的結(jié)構(gòu),該結(jié)構(gòu)將在后面介紹。如果hFile以FILE_FLAG_OVERLAPPED方式常見,則需要此結(jié)構(gòu);否則,不需要此結(jié)構(gòu)。
如果函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
3. 異步I/O操作
異步(重疊)I/O操作是指應用程序可以在后臺讀或者寫數(shù)據(jù),而在前臺做其它事情,例如,用程序可以在開始時對10000個數(shù)據(jù)進行讀或?qū)?/span>操作,然后返回執(zhí)行其它的操作;在讀寫完成后,Windows就會產(chǎn)生一個信號,應用程序得到這個信號,便可以進行其它的讀寫操作。
要使用OVERLAPPED的結(jié)構(gòu),CreateFile()函數(shù)的dwFlagsAndAttributes參數(shù)必須設為FILE_FLAG_OVERLAPPED標識,讀寫串口函數(shù)必須指定OVERLAPPED結(jié)構(gòu)。異步I/O操作在Windows中使用廣泛。
OVERLAPPED結(jié)構(gòu)類型聲明如下:
typedef struct_OVERLAPPED { // 0
DWORD Internal;
DWORD InteralHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED;
其中主要參數(shù)如下:
·Internal:操作系統(tǒng)保留,指出一個和系統(tǒng)相關(guān)的狀態(tài)。當GetOverlappedResult()函數(shù)返回時,如果將擴展信息設置為 ERROR_IO_PENDING,該參數(shù)有效。
·InteralHigh:操作系統(tǒng)保留,指出發(fā)送或接收的數(shù)據(jù)長度,當GetOverlappedResult()函數(shù)返回值不為0時,該參數(shù)有效。
·Offset和OffsetHigh:指明文件傳送的開始位置和字節(jié)偏移量的高位字。當進行端口操作時,這兩個參數(shù)被忽略。
·hEvent:指定一個I/O操作完成后觸發(fā)的事件(信號)。在調(diào)用讀寫函數(shù)進行I/O操作之前,必須設置該參數(shù)。
在設置了異步I/O操作后,I/O操作和函數(shù)返回有以下兩種情況:
1 函數(shù)返回時I/O操作已經(jīng)完成:此時結(jié)果好像是同步執(zhí)行的,但實際上這是異步執(zhí)行的結(jié)果。
2 函數(shù)返回時I/O操作還沒完成:此時一方面,函數(shù)返回值為零,并且GetLastError()函數(shù)返回ERROR_IO_PENDING;另一方面,系統(tǒng)把OVERLAPPED中的信號事件設為無信號狀態(tài)。當I/O操作完成時,系統(tǒng)要把它設置為信號狀態(tài)。
異步I/O操作可以由GetOverlappedResult()函數(shù)來獲取結(jié)果,也可以使用Windows信號函數(shù)來處理。GetOverlappedResult()函數(shù)可聲明為:
BOOL GetOverlappedResult(
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPDWORD lpNumberOfBytesTransferred,
BOOL bWait
);
其中主要參數(shù)介紹如下:
·hFile:標識通信句柄,它應該和開始調(diào)用重疊結(jié)構(gòu)的ReadFile、WriteFile、WaitCommEvent函數(shù)的參數(shù)一致。
·lpOverlapped:在啟動異步操作時指定的OVERLAPPED結(jié)構(gòu)。
·lpNumberOfBytesTransferred:指向一個長整型變量,該變量接收有一個讀或?qū)?/span>操作實際傳遞的字節(jié)數(shù)。
·bWait:指定函數(shù)是否等待掛起的異步操作完成。如果該參數(shù)設為1,則該函數(shù)知道操作完成后才返回。如果該參數(shù)被設為0,同時處于被掛起狀態(tài),則該函數(shù)返回為0,并且GetLastError函數(shù)返回ERROR_IO_INCOMPLETE。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
Windows也使用等待函數(shù)來檢查事件對象的當前狀態(tài)或等待Windows狀態(tài)信號,在WaitForSingleObject()函數(shù), WaitForSingleObjectEx()函數(shù),以及WaitForMultipleObject() ,WaitForMultipleObjectsEx() 函數(shù)中指定OVERLAPPED結(jié)構(gòu)中的 hEvent,即可獲取函數(shù)返回事件。
4. 超時設置
Windows 9x/NT/2000中讀寫串口引入了超時結(jié)構(gòu)。超時結(jié)構(gòu)直接影響讀和寫的操作行為。當事先設定的超時間隔消逝時,ReadFile() 、ReadFileEx()、 WriteFile()和 WriteFileEx()操作仍未結(jié)束,那么超時設置將無條件結(jié)束讀寫操作,而不管是否已讀出或已寫入指定數(shù)量的字符。
在讀或?qū)懖僮髌陂g發(fā)生的超時將不按錯誤處理,即讀或寫操作返回指定成功的值。對于同步讀或?qū)?/span>操作,實際傳輸?shù)淖止?jié)數(shù)由ReadFile()和Write()函數(shù)報告。對于異步操作,則有OVERLAPPED結(jié)構(gòu)來獲取。
超時結(jié)構(gòu)定義如下:
typedef struct_COMMTIMEOUTS {
DWORD ReadIntervalTimeout;
DWORD ReadTotalTimeoutMultiplier;
DWORD ReadTotalTimeoutConstant;
DWORD WriteTotalTimeoutMultiplier;
DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;
其中主要參數(shù)如下:
·ReadIntervalTimeout:以ms為單位指定通信線路上兩個字符到達之間的最大時間間隔。在ReadFile()操作期間,從接收到第一個字符時開始計時。如果任意兩個字符到達之間的時間間隔超過這個最大值,則ReadFile()操作完成,并返回緩沖數(shù)據(jù)。如果被置為0,則表示不使用間隔超時。
·ReadTotalTimeoutMultiplier:以ms為單位指定一個系數(shù),該系數(shù)用來計算讀操作的總超時時間。
·ReadTotalTimeoutConstant:以ms為單位指定一個常數(shù),該常數(shù)也用來計算讀操作的總超時時間。
·WriteTotalTimeoutMultiplier:以ms為單位指定一個系數(shù),該系數(shù)用來計算寫操作的總超時時間。
·WriteTotalTimeoutConstant:以ms為單位指定一個常數(shù),該常數(shù)也用來計算寫操作的總超時時間。
超時有兩種類型。第一種類型叫區(qū)間超時(interval timeout),它僅適應于從端口讀取數(shù)據(jù)。它指定在讀取兩個字符之間要經(jīng)歷多長時間。接收一個字符時,Windows就啟動一個內(nèi)部計時器。在下一個字符到達之前,如果定時器超過了區(qū)間超時設定時間,讀函數(shù)就會放棄。第二種類型的超時叫做總超時( total timeout),它適于讀和寫端口。當讀或?qū)?/span>特定字節(jié)數(shù)需要的總時間超過某一閾值時,該超時即被觸發(fā)。
Windows使用下面的式子計算總超時時間:
ReadTotalTimeout=( ReadTotalTimeoutMultiplier*bytes_to_read )+ ReadTotalTimeoutConstant;
WriteTotalTimeout=( WriteTotalTimeoutMultiplier*bytes_to_write )+ WriteTotalTimeoutConstant;
該方程使總超時時間成為靈活的工具。總超時時間不是固定值,而是根據(jù)讀或?qū)?/span>的字節(jié)數(shù)而“漂浮不定”。應用程序通過設置系數(shù)為0而只是用常數(shù),和通過設置常數(shù)為0而只使用于系數(shù)。如果系數(shù)和常數(shù)都為0,則沒有總超時時間。
因此每個讀操作的總超時時間等于ReadTotalTimeoutMultiplier參數(shù)值乘以讀操作要讀取的字節(jié)數(shù)再加上ReadTotalTimeoutConstant參數(shù)值的和。如果將ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都設置為0,則表示讀操作不使用總超時時間。每個讀間隔超時參數(shù)ReadIntervalTimeout被設置為MAXDWORK,而且兩個讀總超時參數(shù)都被設置為0,那么標識只要一讀完接收緩沖區(qū)而不管得到什么字符就完成讀操作,即使它是空的。當接收中有間隔時,間隔超時將迫使讀操作返回。因此使用間隔超時的進程可以設置一個非常短的間隔超時參數(shù),這樣它可以實現(xiàn)對一個或一些字符的小的、孤立的數(shù)據(jù)作出反應。
每個寫操作的WriteTotalTimeoutConstant等于WriteTotalTimeoutMultiplier成員值乘以寫操作要寫的字節(jié)數(shù),再加上WriteTotalTimeoutConstant參數(shù)值的和。如果WriteTotalTimeoutMultiplier和WriteTotalTimeoutConstant參數(shù)值都設置為0則表示寫操作不使用WriteTotalTimeoutConstant。
在傳輸某種流量控制被阻塞時和調(diào)用SetCommBreak()函數(shù)把字符掛起,寫操作的超時可能有用。如果所有的讀超時參數(shù)都為0,即沒有使用讀超時,那么讀操作知道讀完要求的字節(jié)數(shù)或發(fā)生錯誤時位置。同樣,如果所有的寫超時參數(shù)都為0,那么寫操作知道要求的字節(jié)數(shù)或發(fā)生錯誤時為止。當打開通信資源時,超時參數(shù)將根據(jù)上次設備被打開時所設置的值設置。如果資源從未打開或調(diào)用SetComm函數(shù),那么所有的超時參數(shù)都設置為0。
如果欲獲得當前超時參數(shù),應用程序可以調(diào)用GetCommTimeouts()函數(shù)。該函數(shù)聲明如下:
BOOL GetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
其中主要參數(shù)如下:
·hFile:標識通信設備,CreateFile()函數(shù)返回該句柄;
·lpCommTimeouts:指向一個CommTIMEOUTS結(jié)構(gòu),返回超時信息。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
如果要設置或改變原來的超時參數(shù),應用程序可以調(diào)用SetCommTimeouts()函數(shù)。該函數(shù)聲明如下:
BOOL SetCommTimeouts(
HANDLE hFile,
LPCOMMTIMEOUTS lpCommTimeouts
);
其中主要參數(shù)如下:
·hFile:標識通信設備,CreateFile()函數(shù)返回該句柄;
·lpCommTimeouts:指向一個CommTIMEOUTS結(jié)構(gòu),返回超時信息。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
5. 通信狀態(tài)和通信錯誤
如果在串口通信中發(fā)生錯誤,如發(fā)生中斷,奇偶錯誤等,I/O操作將會終止。如果程序要進一步執(zhí)行I/O操作,必須調(diào)用ClearCommError()函數(shù)。ClearCommError()函數(shù)有兩個作用:第一個作用是清除錯誤條件;第二個作用是確定串口通信狀態(tài)。ClearCommError()函數(shù)的聲明如下:
BOOL ClearCommError(
HANDLE hFile,
LPDWORD lpErrors,
LPCOMSTAT lpStat
);
其中主要參數(shù)介紹如下:
·hFile :標識通信設備,CreateFile()函數(shù)返回該句柄。
·lpErrors:指向用一個指明錯誤類型的掩碼填充的32位變量。該參數(shù)可以是表6中各值的組合。
·lpStat:指向一個COMSTAT結(jié)構(gòu),該結(jié)構(gòu)接收設備的狀態(tài)信息。如果lpStat參數(shù)不設置,則沒有設備狀態(tài)信息被返回。
表6 通信錯誤列表
值 | 描述 |
CE_BREAK | 硬件檢測到一個中斷條件 |
CE_FRAME | 硬件檢測到一個幀出錯 |
CE_IOE | 發(fā)生I/O錯誤 |
CE_MODE | 模式出錯,或者是句柄無效 |
CE_OVERRUN | 超速錯誤 |
CE_RXOVER | 接收緩沖區(qū)超限,或者是輸入緩沖區(qū)中沒有空間,或者實在文件結(jié)束符(EOF)接收后接收到一個字符 |
CE_RXPARITY | 奇偶校驗錯誤 |
CE_TXFULL | 發(fā)送緩沖區(qū)滿 |
CE_DNS | 沒有檢測到并行設備 |
CE_OOP | 并行設備缺紙 |
CE_PTO | 并行設備發(fā)生超時錯誤 |
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。在同步操作時,可以調(diào)用ClearCommError()函數(shù)來確定串口的接收緩沖區(qū)處于等待狀態(tài)的字節(jié)數(shù),而后可以使用ReadFile()或者WriteFile()函數(shù)一次讀寫完。
COMSTAT結(jié)構(gòu)存放有關(guān)通信設備的當前信息。該結(jié)構(gòu)內(nèi)容由ClearCommError()函數(shù)填寫。COMSTAT結(jié)構(gòu)聲明如下:
typedef struct_COMSTAT(
DWORD fCtsHold: 1;
DWORD fDsrHold: 1;
DWORD fRlsdHold: 1;
DWORD fXoffSent: 1;
DWORD fEof: 1;
DWORD fTxim: 1;
DWORD fReserved: 25;
DWORD cbInQue;
DWORD cbOutQue;
} COMSTAT,*LPCOMSTAT;
其中主要參數(shù)介紹如下:
·fCtsHold:指明是否等待CRS信號,如果為1,則發(fā)送等待。
·fDsrHold:指明是否等到DRS信號,如果為1,則發(fā)送等待。
·fRlsdHold:指明是否等待RLSD信號,如果為1,則發(fā)送等待。
·fXoffSent:指明收到XOFF字符后發(fā)送是否等待。如果為1,則發(fā)送等待。如果把XOFF字符發(fā)送給一系統(tǒng)時,該系統(tǒng)就把下一個字符當成XON,而不管實際字符是什么,此時發(fā)送將停止。
·fEof:EOF字符送出。
·fTxim:指明字符是否正等待被發(fā)送,如果為1,則字符正等待被發(fā)送。
·fReserved:系統(tǒng)保留。
·cbInQue:指明串行設備接收到的字節(jié)數(shù)。并不是指ReadFile操作要求讀的字節(jié)數(shù)。
·cbOutQue:指明發(fā)送緩沖區(qū)尚未發(fā)送的字節(jié)數(shù)。如果進行不重疊寫操作時值為0。
2.4 通信事件
Windows進程中監(jiān)視發(fā)生在通信資源中的一組事件,這樣應用程序可以不檢查端口狀態(tài)就可以知道某些條件何時發(fā)生,這將是非常有用的。通過使用事件,應用程序不需要為接收字節(jié)而連續(xù)不斷地檢測端口,從而節(jié)省CPU時間。
1. 通信事件
Windows可以利用GetCommMask()函數(shù)和 SetCommMask函數(shù)來控制表7所示的通信事件。
表7 Windows通信事件列表
值 | 描述 |
EV_BREAK | 檢測到輸入的終止 |
EV_CTS | CTS(清除發(fā)送)信號改變狀態(tài) |
EV_DSR | DSR(數(shù)據(jù)設置就緒)信號改變狀態(tài) |
EV_ERR | 發(fā)生了線路狀態(tài)錯誤,線路狀態(tài)錯誤時CE_FRAME(幀錯誤)、CE_OVERRUN (接收緩沖區(qū)超限)和CE_RXPARITY(奇偶校驗錯誤) |
EV_RING | 檢測到振鈴 |
EV_RLSD | RLSD(接收到線路信號檢測)信號改變狀態(tài) |
EV_RXCHAR | 接收到一個字符,并放入輸入緩沖區(qū) |
EV_RXFLAG | 接收到事件字符(DCB結(jié)構(gòu)地EvtChar成員),并放入輸入緩沖區(qū) |
EV_TXEMPTY | 輸出緩沖區(qū)中最后一個字符發(fā)送出去 |
2. 操作通信事件
應用程序可以利用SetCommMask()函數(shù)簡歷事件掩模來監(jiān)視指定通信資源上的事件。SetCommMask函數(shù)的聲明如下:
BOOL SetCommMask(
HANDLE hFile,
DWORD dwEvtMask
);
其中主要參數(shù)介紹如下:
·hFile :標識通信設備,CreateFile()函數(shù)返回該句柄。
·dwEvtMask:事件掩模,標識將被監(jiān)視的通信事件。如果該參數(shù)設置為0,則表示禁止所有事件。如果不為0,則可以是表7中各種事件的組合。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
如果想獲取特定通信資源的當前事件掩模,可以使用GetCommMask()函數(shù)。GetCommMask()函數(shù)聲明如下:
BOOL GetCommMask(
HANDLE hFile,
LPDWORD lpEvtMask
);
其中主要參數(shù)介紹如下:
·hFile :標識通信設備,CreateFile()函數(shù)返回該句柄。
·dwEvtMask:事件掩模,標識將被監(jiān)視的通信事件,一個32位變量,可以是表7中各種事件的組合。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
3. 監(jiān)視通信事件
在用SetCommMask()指定了有用的事件后,應用程序就調(diào)用WaitCommEvent()函數(shù)來等待其中一個事件發(fā)生。WaitCommEvent()函數(shù)既可以同步使用,也可以異步使用。WaitCommEvent()函數(shù)聲明如下:
BOOL WaitCommEvent(
HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped,
);
其中主要參數(shù)介紹如下:
·hFile :標識通信設備,CreateFile()函數(shù)返回該句柄。
·dwEvtMask:指向一個32位變量,接收事件掩模,標識所發(fā)生的通信事件屬于何種類型??梢允潜?/span>7中各種事件的組合。
·lpOverlapped:指向一個OVERLAPPED結(jié)構(gòu),如果打開hFile表示的通信設備時,指定FILE_FLAG_OVERLAPPED標志,則該參數(shù)被忽略。如果不需要異步操作,則這個參數(shù)不用設置。
如果該函數(shù)調(diào)用成功,則返回值不為零;若函數(shù)調(diào)用失敗,則返回值為零。調(diào)用GetLastError()函數(shù)可以獲得進一步的出錯信息。
如果lpOverlapped參數(shù)不設置或打開hFile標識的通信設備是未指定FILE_FLAG_OVERLAPPED標志,則知道發(fā)生了指定時間或出錯時,WaitCommEvent()函數(shù)才返回。如果lpOverlapped參數(shù)指向一個OVERLAPPED結(jié)構(gòu),并且打開hFile標識的通信設備時指定了FILE_FLAG_OVERLAPPED標志,則WaitCommEvent()函數(shù)以異步操作實現(xiàn)。這種情況下,OVERLAPPED結(jié)構(gòu)中必須含有一個人工復位事件的句柄。和所有異步函數(shù)一樣,如果異步操作不能立即實現(xiàn),則該函數(shù)返回0,并且GetLastError()函數(shù)返回ERROR_IO_PENDING,以指示該操作正在后臺進行。此時WaitCommEvent()函數(shù)返回之前,系統(tǒng)將OVERLAPPED結(jié)構(gòu)中的hEvent參數(shù)設置為無信號狀態(tài);當發(fā)生了指定時間或出錯時,系統(tǒng)將其設置為有信號狀態(tài)。調(diào)用程序可使用等待函數(shù)確定事件對象的狀態(tài),然后使用GetOverlappedResult()函數(shù)確定WaitCommEvent()函數(shù)的操作結(jié)束。GetOverlappedResult()函數(shù)報告該操作成功或者失敗,并且lpEvtMask()函數(shù)所指向的變量被設置以指示所發(fā)生的事件。
如果一個進程在WaitCommEvent()函數(shù)操作進行期間使用SetCommMask()函數(shù)將立即返回,并且由EvtMask參數(shù)指向的變量被設置。
注意:WaitCommEvent()只檢測發(fā)生在等待開始后的事件。例如,如果指定EV_RXCHAR事件,則只有當收到函數(shù)字符并將字符放進接收緩沖區(qū)后才能滿足等待條件。WaitCommEvent()調(diào)用時已在接收緩沖區(qū)中的字符不符合等待條件。監(jiān)視CTS、DSR等信號狀態(tài)改變的事件時,WaitCommEvent()函數(shù)只報告信號的變動,但不報告當前的信號狀態(tài),如果要查詢這些信號狀態(tài),進程可以調(diào)用GetCommModemstatus()函數(shù)。