串口编程介绍 (英文版)
串口编程介绍 (英文版)
串口编程介绍 (英文版)
The article is broken into the following sections: Opening a port, reading
and writing (nonoverlapped and overlapped), serial status (events and
errors), and serial settings (DCB, flow control, and communications
time-outs).
Opening a Port
The CreateFile function opens a communications port. There are two ways
to call CreateFile to open the communications port: overlapped and
nonoverlapped. The following is the proper way to open a communications
resource for overlapped operation:
HANDLE hComm;
hComm = CreateFile( gszPort,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if (hComm == INVALID_HANDLE_VALUE)
// error opening port; abort
One thing to note about port names is that traditionally they have been
COM1, COM2, COM3, or COM4. The Win32 API does not provide any mechanism
for determining what ports exist on a system. Windows NT and Windows 95
keep track of installed ports differently from one another, so any one
method would not be portable across all Win32 platforms. Some systems even
have more ports than the traditional maximum of four. Hardware vendors
and serial-device-driver writers are free to name the ports anything they
like. For this reason, it is best that users have the ability to specify
the port name they want to use. If a port does not exist, an error will
occur (ERROR_FILE_NOT_FOUND) after attempting to open the port, and the
user should be notified that the port isn’t available.
Nonoverlapped I/O
Overlapped I/O
Overlapped I/O is the type of operation that the MTTTY sample uses. It
creates a thread that is responsible for reading the port’s data and
reading the port’s status. It also performs periodic background work.
The program creates another thread exclusively for writing data out the
port.
An overlapped I/O operation has two parts: the creation of the operation
and the detection of its completion. Creating the operation entails
setting up an OVERLAPPED structure, creating a manual-reset event for
synchronization, and calling the appropriate function (ReadFile or
WriteFile). The I/O operation may or may not be completed immediately.
It is an error for an application to assume that a request for an overlapped
operation always yields an overlapped operation. If an operation is
completed immediately, an application needs to be ready to continue
processing normally. The second part of an overlapped operation is to
detect its completion. Detecting completion of the operation involves
waiting for the event handle, checking the overlapped result, and then
handling the data. The reason that there is more work involved with an
overlapped operation is that there are more points of failure. If a
nonoverlapped operation fails, the function just returns an error-return
result. If an overlapped operation fails, it can fail in the creation of
the operation or while the operation is pending. You may also have a
time-out of the operation or a time-out waiting for the signal that the
operation is complete.
Reading
The ReadFile function issues a read operation. ReadFileEx also issues a
read operation, but since it is not available on Windows 95, it is not
discussed in this article. Here is a code snippet that details how to issue
a read request. Notice that the function calls a function to process the
data if the ReadFile returns TRUE. This is the same function called if
the operation becomes overlapped. Note the fWaitingOnRead flag that is
defined by the code; it indicates whether or not a read operation is
overlapped. It is used to prevent the creation of a new read operation
if one is outstanding.
DWORD dwRead;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};
if (osReader.hEvent == NULL)
// Error creating overlapped event; abort.
if (!fWaitingOnRead) {
// Issue read operation.
if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
// Error in communications; report it.
else
fWaitingOnRead = TRUE;
}
else {
// read completed immediately
HandleASuccessfulRead(lpBuf, dwRead);
}
}
Here is a code snippet that shows one way to detect the completion of an
overlapped read operation. Note that the code calls the same function to
process the data that was called when the operation completed immediately.
Also note the use of the fWaitingOnRead flag. Here it controls entry into
the detection code, since it should be called only when an operation is
outstanding.
DWORD dwRes;
if (fWaitingOnRead) {
dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);
switch(dwRes)
{
// Read completed.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))
// Error in communications; report it.
else
// Read completed successfully.
HandleASuccessfulRead(lpBuf, dwRead);
case WAIT_TIMEOUT:
// Operation isn't complete yet. fWaitingOnRead flag isn't
// changed since I'll loop back around, and I don't want
// to issue another read until the first one finishes.
//
// This is a good time to do some background work.
break;
default:
// Error in the WaitForSingleObject; abort.
// This indicates a problem with the OVERLAPPED structure's
// event handle.
break;
}
}
Writing
// Issue write.
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but isn't delayed. Report error and abort.
fRes = FALSE;
}
else
// Write is pending.
dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch(dwRes)
{
// OVERLAPPED structure's event has been signaled.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hComm, &osWrite, &dwWritten,
FALSE))
fRes = FALSE;
else
// Write operation completed successfully.
fRes = TRUE;
break;
default:
// An error has occurred in WaitForSingleObject.
// This usually indicates a problem with the
// OVERLAPPED structure's event handle.
fRes = FALSE;
break;
}
}
}
else
// WriteFile completed immediately.
fRes = TRUE;
CloseHandle(osWrite.hEvent);
return fRes;
}
Notice that the code above uses the WaitForSingleObject function with a
time-out value of INFINITE. This causes the WaitForSingleObject function
to wait forever until the operation is completed; this may make the thread
or program appear to be “hung?when, in fact, the write operation is simply
taking a long time to complete or flow control has blocked the transmission.
Status checking, discussed later, can detect this condition, but doesn’
t cause the WaitForSingleObject to return. Three things can alleviate this
condition:
// Issue write.
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but it isn't delayed. Report error and
abort.
fRes = FALSE;
}
else {
// Write is pending.
if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, TRUE))
fRes = FALSE;
else
// Write operation completed successfully.
fRes = TRUE;
}
}
else
// WriteFile completed immediately.
fRes = TRUE;
CloseHandle(osWrite.hEvent);
return fRes;
}
The only member of the OVERLAPPED structure that needs modifying for
serial communications is the hEvent member. The other members of the
OVERLAPPED structure should be initialized to zero and left alone.
Modifying the other members of the OVERLAPPED structure is not necessary
for serial communications devices. The documentation for ReadFile and
WriteFile state that the Offset and OffsetHigh members of the OVERLAPPED
structure must be updated by the application, or else results are
unpredictable. This guideline should be applied to OVERLAPPED structures
used for other types of resources, such as files.
Serial Status
Communications Events
Communications events can occur at any time in the course of using a
communications port. The two steps involved in receiving notification of
communications events are as follows:
DWORD dwStoredFlags;
EV_RXCHAR A new character was received and placed in the input buffer.
See the “Caveat?section below for a discussion of this flag.
EV_RXFLAG The event character was received and placed in the input
buffer. The event character is specified in the EvtChar
member of the DCB structure discussed later. The
“Caveat?section below also applies to this flag.
EV_TXEMPTY The last character in the output buffer was sent to the
serial port device. If a hardware buffer is used, this flag
only indicates that all data has been sent to the hardware.
There is no way to detect when the hardware buffer is empty
without talking directly to the hardware with a device
driver.
After specifying the event mask, the WaitCommEvent function detects the
occurrence of the events. If the port is open for nonoverlapped operation,
then the WaitCommEvent function does not contain an OVERLAPPED structure.
The function blocks the calling thread until the occurrence of one of the
events. If an event never occurs, the thread may block indefinitely.
Here is a code snippet that shows how to wait for an EV_RING event when
the port is open for nonoverlapped operation:
DWORD dwCommEvent;
if (!SetCommMask(hComm, EV_RING))
// Error setting communications mask
return FALSE;
Note The Microsoft Win32 SDK Knowledge Base documents a problem with
Windows 95 and the EV_RING flag. The above code never returns in Windows
95 because the EV_RING event is not detected by the system; Windows NT
properly reports the EV_RING event. Please see the Win32 SDK Knowledge
Base for more information on this bug.
As noted, the code above can be blocked forever if an event never occurs.
A better solution would be to open the port for overlapped operation and
wait for a status event in the following manner:
DWORD dwRes;
DWORD dwCommEvent;
DWORD dwStoredFlags;
BOOL fWaitingOnStat = FALSE;
OVERLAPPED osStatus = {0};
for ( ; ; ) {
// Issue a status event check if one hasn't been issued already.
if (!fWaitingOnStat) {
if (!WaitCommEvent(hComm, &dwCommEvent, &osStatus)) {
if (GetLastError() == ERROR_IO_PENDING)
bWaitingOnStatusHandle = TRUE;
else
// error in WaitCommEvent; abort
break;
}
else
// WaitCommEvent returned immediately.
// Deal with status event as appropriate.
ReportStatusEvent(dwCommEvent);
}
case WAIT_TIMEOUT:
// Operation isn't complete yet.
fWaitingOnStatusHandle flag
// isn't changed since I'll loop back around and I don't
want
// to issue another WaitCommEvent until the first one
finishes.
//
// This is a good time to do some background work.
DoBackgroundWork();
break;
default:
// Error in the WaitForSingleObject; abort
// This indicates a problem with the OVERLAPPED
structure's
// event handle.
CloseHandle(osStatus.hEvent);
return 0;
}
}
}
CloseHandle(osStatus.hEvent);
The code above very closely resembles the code for overlapped reading.
In fact, the MTTTY sample implements its reading and status checking in
the same thread using WaitForMultipleObjects to wait for either the read
event or the status event to become signaled.
Caveat
Using the EV_RXCHAR flag will notify the thread that a byte arrived at
the port. This event, used in combination with the ReadFile function,
enables a program to read data only after it is in the receive buffer,
as opposed to issuing a read that waits for the data to arrive. This is
particularly useful when a port is open for nonoverlapped operation
because the program does not need to poll for incoming data; the program
is notified of the incoming data by the occurrence of the EV_RXCHAR event.
Initial attempts to code this solution often produce the following
pseudocode, including one oversight covered later in this section:
DWORD dwCommEvent;
DWORD dwRead;
char chRead;
if (!SetCommMask(hComm, EV_RXCHAR))
// Error setting communications event mask.
for ( ; ; ) {
if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {
if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))
// A byte has been read; process it.
else
// An error occurred in the ReadFile call.
break;
}
else
// Error in WaitCommEvent.
break;
}
The above code waits for an EV_RXCHAR event to occur. When this happens,
the code calls ReadFile to read the one byte received. The loop starts
again, and the code waits for another EV_RXCHAR event. This code works
fine when one or two bytes arrive in quick succession. The byte reception
causes the EV_RXCHAR event to occur. The code reads the byte. If no other
byte arrives before the code calls WaitCommEvent again, then all is fine;
the next byte to arrive will cause the WaitCommEvent function to indicate
the occurrence of the EV_RXCHAR event. If another single byte arrives
before the code has a chance to reach the WaitCommEvent function, then
all is fine, too. The first byte is read as before; the arrival of the
second byte causes the EV_RXCHAR flag to be set internally. When the code
returns to the WaitCommEvent function, it indicates the occurrence of the
EV_RXCHAR event and the second byte is read from the port in the ReadFile
call.
The problem with the above code occurs when three or more bytes arrive
in quick succession. The first byte causes the EV_RXCHAR event to occur.
The second byte causes the EV_RXCHAR flag to be set internally. The next
time the code calls WaitCommEvent, it indicates the EV_RXCHAR event. Now,
a third byte arrives at the communications port. This third byte causes
the system to attempt to set the EV_RXCHAR flag internally. Because this
has already occurred when the second byte arrived, the arrival of the third
byte goes unnoticed. The code eventually will read the first byte without
a problem. After this, the code will call WaitCommEvent, and it indicates
the occurrence of the EV_RXCHAR event (from the arrival of the second byte).
The second byte is read, and the code returns to the WaitCommEvent function.
The third byte waits in the system’s internal receive buffer. The code
and the system are now out of sync. When a fourth byte finally arrives,
the EV_RXCHAR event occurs, and the code reads a single byte. It reads
the third byte. This will continue indefinitely.
The real solution to this problem is to read from the port until no bytes
are remaining. The following pseudocode solves the problem by reading in
a loop until zero characters are read. Another possible method would be
to call ClearCommError to determine the number of bytes in the buffer and
read them all in one read operation. This method requires more
sophisticated buffer management, but it reduces the number of reads when
a lot of data arrives at once.
DWORD dwCommEvent;
DWORD dwRead;
char chRead;
if (!SetCommMask(hComm, EV_RXCHAR))
// Error setting communications event mask
for ( ; ; ) {
if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {
do {
if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))
// A byte has been read; process it.
else
// An error occurred in the ReadFile call.
break;
} while (dwRead);
}
else
// Error in WaitCommEvent
break;
}
The above code does not work correctly without setting the proper
time-outs. Communications time-outs, discussed later, affect the
behavior of the ReadFile operation in order to cause it to return without
waiting for bytes to arrive. Discussion of this topic occurs later in the
“Communications Time-outs?section of this article.
The above caveat also applies to other events not related to character
reception. If other events occur in quick succession some of the
notifications will be lost. For instance, if the CTS line voltage starts
high, then goes low, high, and low again, an EV_CTS event occurs. There
is no guarantee of how many EV_CTS events will actually be detected with
WaitCommEvent if the changes in the CTS line happen quickly. For this
reason, WaitCommEvent cannot be used to keep track of the state of the
line. Line status is covered in the “Modem Status?section later in this
article.
COMSTAT comStat;
DWORD dwErrors;
BOOL fOOP, fOVERRUN, fPTO, fRXOVER, fRXPARITY, fTXFULL;
BOOL fBREAK, fDNS, fFRAME, fIOE, fMODE;
if (comStat.fDsrHold)
// Tx waiting for DSR signal
if (comStat.fRlsdHold)
// Tx waiting for RLSD signal
if (comStat.fXoffHold)
// Tx waiting, XOFF char rec'd
if (comStat.fXoffSent)
// Tx waiting, XOFF char sent
if (comStat.fEof)
// EOF character received
if (comStat.fTxim)
// Character waiting for Tx; char queued with TransmitCommChar
if (comStat.cbInQue)
// comStat.cbInQue bytes have been received, but not read
if (comStat.cbOutQue)
// comStat.cbOutQue bytes are awaiting transfer
The call to SetCommMask may include the flags EV_CTS, EV_DSR, EV_RING,
and EV_RLSD. These flags indicate changes in the voltage on the lines of
the serial port. There is no indication of the actual status of these lines,
just that a change occurred. The GetCommModemStatus function retrieves
the actual state of these status lines by returning a bit mask indicating
a 0 for low or no voltage and 1 for high voltage for each of the lines.
Please note that the term RLSD (Receive Line Signal Detect) is commonly
referred to as the CD (Carrier Detect) line.
Note The EV_RING flag does not work in Windows 95 as mentioned earlier.
The GetCommModemStatus function, however, does detect the state of the
RING line.
DWORD dwModemStatus;
BOOL fCTS, fDSR, fRING, fRLSD;
if (!GetCommModemStatus(hComm, &dwModemStatus))
// Error in GetCommModemStatus;
return;
Extended Functions
There are occasions when standard control lines are under the control of
the application instead of the serial communications driver. For instance,
an application may wish to implement its own flow control. The application
would be responsible for changing the status of the RTS and DTR lines.
EscapeCommFunction directs a communications driver to perform such
extended operations. EscapeCommFunction can make the driver perform some
other function, such as setting or clearing a BREAK condition. For more
information on this function, consult the Platform SDK documentation, the
Microsoft Win32 SDK Knowledge Base, or the Microsoft Developer Network
(MSDN) Library.
Serial Settings
DCB Settings
There are three ways to initialize a DCB structure. The first method is
to use the function GetCommState. This function returns the current DCB
in use for the communications port. The following code shows how to use
the GetCommState function:
if (!GetCommState(hComm, &dcb))
// Error getting current DCB settings
else
// DCB is ready for use.
DCB dcb;
Here is an explanation of each of the members of the DCB and how they affect
other parts of the serial communications functions.
Note Most of this information is from the Platform SDK documentation.
Because documentation is the official word in what the members actually
are and what they mean, this table may not be completely accurate if
changes occur in the operating system.
Member Description
Value Meaning
DTR_CONTROL_DISABLE Lowers the DTR line when the
device is opened. The
application can adjust the
state of the line with
EscapeCommFunction.
Value Meaning
Value Meaning
EVENPARITY Even
MARKPARITY Mark
NOPARITY No parity
ODDPARITY Odd
StopBits Specifies the number of stop bits to be used. This
member can be one of the following values:
Value Meaning
Flow Control
Hardware flow control uses voltage signals on control lines of the serial
cable to control whether sending or receiving is enabled. The DTE and the
DCE must agree on the types of flow control used for a communications
session. Setting the DCB structure to enable flow control just configures
the DTE. The DCE also needs configuration to make certain the DTE and DCE
use the same type of flow control. There is no mechanism provided by Win32
to set the flow control of the DCE. DIP switches on the device, or commands
sent to it typically establish its configuration. The following table
describes the control lines, the direction of the flow control, and the
line's effect on the DTE and DCE.
CTS DCE sets the line high to indicate that it can receive
(Clear To Send) data. DCE sets the line low to indicate that it cannot
Output flow receive data.
control
If the fOutxCtsFlow member of the DCB is TRUE, then the
DTE will not send data if this line is low. It will resume
sending if the line is high.
DSR DCE sets the line high to indicate that it can receive
(Data Set data. DCE sets the line low to indicate that it cannot
Ready) receive data.
Output flow
control If the fOutxDsrFlow member of the DCB is TRUE, then the
DTE will not send data if this line is low. It will resume
sending if the line is high.
If the fOutxDsrFlow member of the DCB is FALSE, then the
state of the line does not affect transmission.
DSR If the DSR line is low, then data that arrives at the
(Data Set port is ignored. If the DSR line is high, data that
Ready) arrives at the port is received.
Input flow
control This behavior occurs if the fDsrSensitivity member of
the DCB is set to TRUE. If it is FALSE, then the state
of the line does not affect reception.
The DCE will suspend transmission when the line goes low.
The DCE will resume transmission when the line goes high.
The DCE will suspend transmission when the line goes low.
The DCE will resume transmission when the line goes high.
The need for flow control is easy to recognize when the CE_RXOVER error
occurs. This error indicates an overflow of the receive buffer and data
loss. If data arrives at the port faster than it is read, CE_RXOVER can
occur. Increasing the input buffer size may cause the error to occur less
frequently, but it does not completely solve the problem. Input flow
control is necessary to completely alleviate this problem. When the driver
detects that the input buffer is nearly full, it will lower the input
flow-control lines. This should cause the DCE to stop transmitting, which
gives the DTE enough time to read the data from the input buffer. When
the input buffer has more room available, the voltage on flow-control
lines is set high, and the DCE resumes sending data.
A similar error is CE_OVERRUN. This error occurs when new data arrives
before the communications hardware and serial communications driver
completely receives old data. This can occur when the transmission speed
is too high for the type of communications hardware or CPU. This can also
occur when the operating system is not free to service the communications
hardware. The only way to alleviate this problem is to apply some
combination of decreasing the transmission speed, replacing the
communications hardware, and increasing the CPU speed. Sometimes
third-party hardware drivers that are not very efficient with CPU
resources cause this error. Flow control cannot completely solve the
problems that cause the CE_OVERRUN error, although it may help to reduce
the frequency of the error.
In order to enable software flow control, the fOutX and fInX members of
the DCB must be set to TRUE. The fOutX member controls output flow control.
The fInX member controls input flow control.
One thing to note is that the DCB allows the program to dynamically assign
the values the system recognizes as flow-control characters. The XoffChar
member of the DCB dictates the XOFF character for both input and output
flow control. The XonChar member of the DCB similarly dictates the XON
character.
For input flow control, the XoffLim member of the DCB specifies the minimum
amount of free space allowed in the input buffer before the XOFF character
is sent. If the amount of free space in the input buffer drops below this
amount, then the XOFF character is sent. For input flow control, the XonLim
member of the DCB specifies the minimum number of bytes allowed in the
input buffer before the XON character is sent. If the amount of data in
the input buffer drops below this value, then the XON character is sent.
Table 4 lists the behavior of the DTE when using XOFF/XON flow control.
Flow-control Behavior
character
XOFF sent from DTE XOFF is automatically sent by the DTE when the
receive buffer approaches full. The actual limit is
dictated by the XoffLim member of the DCB. The fInX
member of the DCB controls this behavior. DTE
transmission is controlled by the fTXContinueOnXoff
member of the DCB as described below.
XON sent from the XON is automatically sent by the DTE when the receive
DTE buffer approaches empty. The actual limit is
dictated by the XonLim member of the DCB. The fInX
member of the DCB controls this behavior.
There is no mechanism available in the Win32 API to cause the DTE to behave
the same way as these devices. The DCB structure contains no members for
specifying suspended transmission to resume when any character is
received. The XON character is the only character that causes transmission
to resume.
One other interesting note about software flow control is that reception
of XON and XOFF characters causes pending read operations to complete with
zero bytes read. The XON and XOFF characters cannot be read by the
application, since they are not placed in the input buffer.
A lot of programs on the market, including the Terminal program that comes
with Windows, give the user three choices for flow control: Hardware,
Software, or None. The Windows operating system itself does not limit an
application in this way. The settings of the DCB allow for Software and
Hardware flow control simultaneously. In fact, it is possible to
separately configure each member of the DCB that affects flow control,
which allows for several different flow-control configurations. The
limits placed on flow-control choices are there for ease-of-use reasons
to reduce confusion for end users. The limits placed on flow-control
choices may also be because devices used for communications may not
support all types of flow control.
Communications Time-outs
Another major topic affecting the behavior of read and write operations
is time-outs. Time-outs affect read and write operations in the following
way. If an operation takes longer than the computed time-out period, the
operation is completed. There is no error code that is returned by ReadFile,
WriteFile, GetOverlappedResult, or WaitForSingleObject. All indicators
used to monitor the operation indicate that it completed successfully.
The only way to tell that the operation timed out is that the number of
bytes actually transferred are fewer than the number of bytes requested.
So, if ReadFile returns TRUE, but fewer bytes were read than were requested,
the operation timed out. If an overlapped write operation times out, the
overlapped event handle is signaled and WaitForSingleObject returns
WAIT_OBJECT_O. GetOverlappedResult returns TRUE, but dwBytesTransferred
contains the number of bytes that were transferred before the time-out.
The following code demonstrates how to handle this in an overlapped write
operation:
// Issue write
if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
if (GetLastError() != ERROR_IO_PENDING) {
// WriteFile failed, but it isn't delayed. Report error.
fRes = FALSE;
}
else
// Write is pending.
dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
switch(dwRes)
{
// Overlapped event has been signaled.
case WAIT_OBJECT_0:
if (!GetOverlappedResult(hComm, &osWrite, &dwWritten,
FALSE))
fRes = FALSE;
else {
if (dwWritten != dwToWrite) {
// The write operation timed out. I now need to
// decide if I want to abort or retry. If I retry,
// I need to send only the bytes that weren't sent.
// If I want to abort, I would just set fRes to
// FALSE and return.
fRes = FALSE;
}
else
// Write operation completed successfully.
fRes = TRUE;
}
break;
default:
// An error has occurred in WaitForSingleObject. This
usually
// indicates a problem with the overlapped event handle.
fRes = FALSE;
break;
}
}
}
else {
// WriteFile completed immediately.
if (dwWritten != dwToWrite) {
// The write operation timed out. I now need to
// decide if I want to abort or retry. If I retry,
// I need to send only the bytes that weren't sent.
// If I want to abort, then I would just set fRes to
// FALSE and return.
fRes = FALSE;
}
else
fRes = TRUE;
}
CloseHandle(osWrite.hEvent);
return fRes;
}
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = 20;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 100;
if (!SetCommTimeouts(hComm, &timeouts))
// Error setting time-outs.
Note Once again, communications time-outs are not the same as time-out
values supplied in synchronization functions. WaitForSingleObject, for
instance, uses a time-out value to wait for an object to become signaled;
this is not the same as a communications time-out.
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(hComm, &timeouts))
// Error setting time-outs.
These settings are necessary when used with an event-based read described
in the “Caveat?section earlier. In order for ReadFile to return 0 bytes
read, the ReadIntervalTimeout member of the COMMTIMEOUTS structure is
set to MAXDWORD, and the ReadTimeoutMultiplier and ReadTimeoutConstant
are both set to zero.
Conclusion
Bibliography
Brain, Marshall. Win32 System Services: The Heart of Windows NT. Englewood
Cliffs, NJ: Prentice Hall, 1994.
Campbell, Joe. C Programmer’s Guide to Serial Communications. 2d ed.
Indianapolis, IN: Howard W. Sams & Company, 1994.