Serial Communications in Win32

July 28th, 2008

Onsource: MSDN Library - October 2001

Allen Denver
Microsoft Windows Developer Support

December 11, 1995

Abstruct

Serial communications in Microsoft Win32 is significantly different from serial communications in 16-bit Microsoft Windows. Those familiar with 16-bit serial communications functions will have to relearn many parts of the system to program serial communications properly. This article will help to accomplish this. Those unfamiliar with serial communications will find this article a helpful foundation for development efforts.

This article assumes the reader is familiar with the fundamentals of multiple threading and synchronization in Win32.  In addition, a basic familiarity of the Win32 heap functions is useful to fully comprehend the memory management methods used by the sample, MTTTY, included with this article. For more information regarding these functions, consult the Platform SDK documentation, the Microsoft Win32 SDK Knowledge Base, or the Microsoft Developer Network Library. Application programming interfaces (APIs) that control user interface features of windows and dailog boxes, though not discussed here, are useful to know in order to fully comprehend the sample provided with this article. Readers unfamiliar with general Windows programming practices should learn some of the funcamentals of general Windows programming before taking on serial communications. In other words, get your feet web before diving in head first.

Introduction

The focus of this article is on application programming interface (APIs) and methods that are compatible with Microsoft Windows NT and Windows 95; therefore, APIs supported on both platforms are the only ones discussed. Windows 95 supports the Win32 Telephony API (TAPI) and Windows NT 3.x does not; therefore, this discussion will not include TAPI. TAPI does deserve mention, however, in that it very nicely implements modem interfacing and call controlling. A production application that works with modems and makes telephone calls should implement these features using the TAPI interface. This will allow seamless integration with the other TAPI-enabled applications that a user may have. Furthermore, this article does not discuss some of the configuration functions in Win32, such as GetCommProperties.

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).

The sample included with this article, MTTTY: Multithreaded TTY, implements many of the features discussed here. It uses three threads in its implementation: a user interface thread that does memory management, a writer thread that controls all writing, and a reader/status thread that reads data and handles status changes on the port. The sample employs a few different data heaps for memory management. It also makes extensive use of synchronization methods to facilitate communication between threads.

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_OVERAPPED,
0);
if (hComm == INVALID_HANDLE_VALUE)
// error opening port; abort

Removal of the FILE_FLAG_OVERLAPPED flag from the call to CreateFile specifies nonoverlapped operation. The next section discusses overlapped and nonoverlapped operations.

The Platform SDK documentation states that when opening a communications port, the call to CreateFile has the following requirements:

  • fdwShareMode must be zero. Communiciations ports cannot be shared in the same manner that files are shared. Applications using TAPI can use the TAPI functions to facilitate sharing resources between applications. For Win32 applications not using TAPI, handle inheritance or duplication is necessary to share the communications port. Handle duplication is beyond the scope of this article; please refer to the Platform SDK documentation for more information.
  • fdwCreate must specify the OPEN_EXISTING flag.
  • hTemplateFile parameter must be NULL.

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 they 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 specifiy 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.

Reading and Writing

Reading from and writing to communications ports in Win32 is very similar to file input/output (I/O) in Win32. In fact, the functions that accomplish file I/O are the same functions used for serial I/O. I/O in Win32 can be done either of two ways: overlapped or nonoverlapped. The Platform SDK documentation uses the terms asynchronous and synchronous to connote these types of I/O operations. This article, however, uses the terms overlapped and nonoverlapped.

Nonoverlapped I/O is familiar to most developers because this is the traditional form of I/O, where an operation is requested and is assumed to be complete when the function returns. In the case of overlapped I/O, the system may return to the caller immediately even when an operation is not finished and will signal the caller when the operation completes. The program may use the time bewteen the I/O request and its completion to perform some “background” work.

Reading and writing in Win32 is significantly different from reading and writing serial communications ports in 16-bit Windows. 16-bit Windows only has the ReadComm and WriteComm functions. Win32 reading and writing can involve many more functions and choices. These issues are discussed below.

Nonoverlapped I/O

Nonoverlapped I/O is very straightforward, though it has limitations. An operation takes place while the calling thread is blocked. Once the operation is complete, the function returns and the thread can continue its work. This type of I/O is useful for multithreaded applications because while one thread is blocked on an I/O operation, other threads can still perform work. It is the responsibility of the application to serialize access to the port correctly. If one thread is blocked waiting for its I/O operation to complete, all other threads that subsequently call a communications API will be blocked until the original operation completes. For instance, if one thread were waiting for a ReadFile function to return, any other thread that issued a WriteFile function would be blocked.

One of the many factors to consider when choosing between nonoverlapped and overlapped operations is portability. Overlapped operation is not a good choice because most operationg systems do not support it. Most operating systems support some form of multithreading, however, so multithreaded nonoverlapped I/O may be the best choice for portability reasons.

Overlapped I/O

A

DCB Structure

July 28th, 2008

DCB Structure

Defines the control setting for a serial communications device.

typedef struct _DCB {

DWORD DCBlength;
DWORD BaudRate;
DWORD fBinary  :1;
DWORD fParity  :1;
DWORD fOutXCtsFlow  :1;
DWORD fOutXDsrFlow  :1;
DWORD fDtrControl  :2;
DWORD fDsrSensitivity  :1;
DWORD fTXContinueOnXoff  :1;
DWORD fOutX  :1;
DWORD fInX  :1;
DWORD fErrorChar  :1;
DWORD fNull  :1;
DWORD fRtsControl  :2;
DWORD fAbortOnError  :1;
DWORD fDummy2  :17;
WORD wReserved;
WORD XonLim;
WORD XoffLim;
BYTE ByteSize;
BYTE Parity;
BYTE StopBits;
char XonChar;
char XoffChar;
char ErrorChar;
char EofChar;
char EvtChar;
WORD wReserved1;

} DCB,
*LPDCB;

Members

DCBlength

The length of the structure, in bytes. The caller must set this member to sizeof(DCB).

BaudRate

The baud rate at which the communications device operates. This member can be an actual baud rate value, or one of the following indexes.

more

AfxBeginThread

July 28th, 2008

AfxBeginThread

CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

Return Value

Pointer to the newly created thread object.

Parameters

pfnThreadProc

Points to the controlling function for the worker thread. Cannot be NULL. This function must be declared as follows:

UINT MyControllingFunction( LPVOID pParam );

pThreadClass

The RUNTIME_CLASS of an object derived from CWinThread.

pParam

Parameter to be passed to the controlling function as shown in the parameter to the function declaration in pfnThreadProc.

nPriority

The desired priority of the thread. If 0, the same priority as the creating thread will be used. For a full list and description of the available priorities, see SetThreadPriority in the Win32 Programmer’s Reference.

nStackSize

Specifies the size in bytes of the stack for the new thread. If 0, the stack size defaults to the same size stack as the creating thread.

dwCreateFlags

Specifies an additional flag that controls the creation of the thread. This flag can contain one of two values:

CREATE_SUSPENDED start the thread with a suspend count of one. The thread will not execute until ResumeThread is called.

0 start the thread immediately after creation.

lpSecurityAttrs

Points to a SECURITY_ATTRIBUTES structure that specifies the security attributes for the thread. If NULL, the same security attributes as the creating thread will be used. For more information on this structure, see the Win32 Programmer’s Reference.

Remarks

Call this function to create a new thread. The first form of AfxBeginThread creates a worker thread. The second form creates a user-interface thread.

AfxBeginThread creates a new CWinThread object, calls its CreateThread function to start executing the thread, and returns a pointer to the thread.  Checks are made throughout the procedure to make sure all objects are deallocated properly should any part of the creation fail. To end the thread, call AfxEndThread from within the thread, or return from the controlling function of the worker thread.

source: MSDN Library - October 2001