剖析CSerialPort类

用两天时间从头至尾仔细看了CserialPort类,一句一句的分析,终于搞明白了,以下是源程序和注释。

SerialPort.h文件代码如下:

#ifndef __SERIALPORT_H__

#define __SERIALPORT_H__


#define WM_COMM_BREAK_DETECTED     WM_USER+1 // A break was detected on input.

#define WM_COMM_CTS_DETECTED                WM_USER+2 // The CTS (clear-to-send) signal changed state.

#define WM_COMM_DSR_DETECTED                WM_USER+3 // The DSR (data-set-ready) signal changed state.

#define WM_COMM_ERR_DETECTED                WM_USER+4 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.

#define WM_COMM_RING_DETECTED              WM_USER+5 // A ring indicator was detected.

#define WM_COMM_RLSD_DETECTED              WM_USER+6 // The RLSD (receive-line-signal-detect) signal changed state.

#define WM_COMM_RXCHAR                                          WM_USER+7 // A character was received and placed in the input buffer.

#define WM_COMM_RXFLAG_DETECTED  WM_USER+8 // The event character was received and placed in the input buffer.

#define WM_COMM_TXEMPTY_DETECTED      WM_USER+9 // The last character in the output buffer was sent.


class CSerialPort

{                                                                                            

public:

CSerialPort();// 构造函数

//析构函数虚拟化了,是为了不同端口对象被消灭的时候,可以进行不同的收尾工作  virtual            ~CSerialPort();

//端口初始化函数,设定了宿主窗口,以及一些默认参数

BOOL         InitPort(CWnd* pPortOwner, UINT portnr = 1, UINT baud = 19200, char parity = 'N', UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR | EV_CTS, UINT nBufferSize = 512);

BOOL         StartMonitoring();// COM口监视控制函数,开,重启,关

BOOL         RestartMonitoring();

BOOL         StopMonitoring();

DWORD            GetWriteBufferSize();//获取写缓冲区的大小

DWORD            GetCommEvents();//获取COM口的事件类型

DCB           GetDCB();//获取设备控制块信息,返回块结构类型

//向端口写数据函数

void            WriteToPort(const char* string);//这里加了个const  原来没有  但是 加了就不报错了


protected:

// protected memberfunctions

void            ProcessErrorMessage(char* ErrorText);//处理错误消息

static UINT CommThread(LPVOID pParam);//监视线程函数

static void   ReceiveChar(CSerialPort* port, COMSTAT comstat);//读

static void   WriteChar(CSerialPort* port);//写,前面还定义了一个WriteToPort(const char* string);函数是用来将数据发送到串口

CWinThread*            m_Thread; // 创建线程对象,这是要进行线程处理的

CRITICAL_SECTION      m_csCommunicationSync; //声明临界区结构对象用来线程同步


BOOL         m_bThreadAlive;   //线程的状态 死,活

HANDLE                         m_hShutdownEvent; // 关闭线程的事件句柄

HANDLE                         m_hComm;//串口句柄

HANDLE                         m_hWriteEvent;//写句柄

// One element is used for each event. There are two event handles for each port.

// A Write event and a receive character event which is located in the overlapped structure (m_ov.hEvent).

// There is a general shutdown when the port is closed.

HANDLE                         m_hEventArray[3]; // 放事件的数组.

// 几个结构体  

OVERLAPPED                 m_ov;

COMMTIMEOUTS          m_CommTimeouts;//超时

DCB                                                     m_dcb;

CWnd*                                   m_pOwner; // owner window

UINT                              m_nPortNr; // misc

char*                               m_szWriteBuffer;

DWORD                          m_dwCommEvents;

DWORD                          m_nWriteBufferSize;

};

#endif __SERIALPORT_H__


SerialPort.cpp文件代码如下:

#include "stdafx.h"

#include "SerialPort.h"

#include <assert.h>

CSerialPort::CSerialPort()//构造函数 完成成员变量初始化

{

m_hComm = NULL; //串口文件的文件句柄

m_ov.Offset = 0; //初始化Overlapped结构体,包括文件读写位置,还有IO事件

m_ov.OffsetHigh = 0;

m_ov.hEvent = NULL; //这就是串口文件读写IO事件句柄,就是读

m_hWriteEvent = NULL;//写句柄

m_hShutdownEvent = NULL;//控制线程生死的句柄

m_szWriteBuffer = NULL;//缓冲区的大小

m_bThreadAlive = FALSE;//线程是否活着

}

CSerialPort::~CSerialPort()//析构函数,杀线程,清内存

{

do

{

SetEvent(m_hShutdownEvent);//将线程置成有信号,释放任意等待线程

} while (m_bThreadAlive);//线程还活着的话

TRACE("Thread ended\n");// TRACE 宏有点象我们以前在C语言中用的Printf函数,使程序在运行过程中输出一些调试信息,使我们能了解程序的一些状态。

delete [] m_szWriteBuffer;//释放内存空间,析构时需要释放内存

}

// 初始化端口,1-4,只能监测4个,使用了很多API

BOOL CSerialPort::InitPort(CWnd* pPortOwner,    // the owner (CWnd) of the port (receives message)

UINT  portnr,          // portnumber (1..4)

UINT  baud,                   // baudrate

char  parity,             // parity

UINT  databits,        // databits

UINT  stopbits,        // stopbits

DWORD dwCommEvents,       // EV_RXCHAR, EV_CTS etc

UINT  writebuffersize)    // size to the writebuffer

{

assert(portnr > 0 && portnr < 10);/ /编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。对串口号的限制,意思是从COM1到COM9

assert(pPortOwner != NULL);

if (m_bThreadAlive) // if the thread is alive: Kill先将线程杀死

{

do

{

SetEvent(m_hShutdownEvent); //设置事件的状态为有标记,释放任意等待线程。

} while (m_bThreadAlive);

TRACE("Thread ended\n");

}

if (m_ov.hEvent != NULL)//m_ov.hEvent是串口文件读写IO事件句柄,即 读

ResetEvent(m_ov.hEvent);//这个函数把指定的事件对象设置为无信号状态,不让线程处理

m_ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//创建事件对象

if (m_hWriteEvent != NULL)

ResetEvent(m_hWriteEvent);

m_hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

if (m_hShutdownEvent != NULL)

ResetEvent(m_hShutdownEvent);

m_hShutdownEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

// initialize the event objects

m_hEventArray[0] = m_hShutdownEvent;      // highest priority杀死线程 读 写 优先级最高以下一次次之(杀死线程后读和写也就都处理不了了)

m_hEventArray[1] = m_ov.hEvent;

m_hEventArray[2] = m_hWriteEvent;

InitializeCriticalSection(&m_csCommunicationSync); //初始化临界区控制  ,这是将这个临界区对象有效。在头文件中用CRITICAL_SECTION声明了m_csCommunicationSync为临界区结构对象

m_pOwner = pPortOwner; // set buffersize for writing and save the owner

if (m_szWriteBuffer != NULL)

delete [] m_szWriteBuffer;//如果内存空间没被释放,则释放内存空间

m_szWriteBuffer = new char[writebuffersize];//分配内存空间

m_nPortNr = portnr;//portnr为串口号参数,将初始化函数中的串口号送给代表串口号的全局变量m_nPortNr(在头文件中定义)以下两个条语句都是送给全局变量。


m_nWriteBufferSize = writebuffersize;

m_dwCommEvents = dwCommEvents;

BOOL bResult = FALSE;//代表串口是否有事件发生的布尔值。后面的程序中: bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);

char *szPort = new char[50];//分配内存空间

char *szBaud = new char[50];

// 进入临界区,代表的是可以对每个端口进行不同设置,因为临界区的作用是防止多个线程访问同一段代码

EnterCriticalSection(&m_csCommunicationSync);

// 查看串口文件IO句柄,便可得知端口状态

if (m_hComm != NULL)

{

CloseHandle(m_hComm);

m_hComm = NULL;

}

sprintf(szPort, "COM%d", portnr); //准备端口号信息,和波特率信息

sprintf(szBaud, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopbits);

m_hComm = CreateFile(          // 获取端口文件的句柄,记住,这是文件形式的

szPort,                                                                                                   // communication port string (COMX)

GENERIC_READ | GENERIC_WRITE,          // read/write types

0,                                                                                                                 // comm devices must be opened with exclusive access

NULL,                                                                                                   // no security attributes

OPEN_EXISTING,                                                                   // comm devices must use OPEN_EXISTING

FILE_FLAG_OVERLAPPED,   // Async I/O允许对文件进行重叠操作,即异步操作

// template must be 0 for comm devices

);                                                                                                        

//判断是否有效 ,如果无效会自动产生INVALID_HANDLE_VALUE消息,无效则释放内存

if (m_hComm == INVALID_HANDLE_VALUE)

{

// port not found

delete [] szPort;

delete [] szBaud; //释放第181和182行

char *szPort = new char[50];

char *szBaud = new char[50];

分配的内存空间

return FALSE;

}


// 设置超时。

在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。如果在指定的时间内没有读出或写入指定数量的字符,那么ReadFile或WriteFile的操作就会结束。

COMMTIMEOUTS结构处理超时,头文件中 第96行 定义 超时结构的变量:“ COMMTIMEOUTS          m_CommTimeouts;//超时”

要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。

m_CommTimeouts.ReadIntervalTimeout = 1000;

m_CommTimeouts.ReadTotalTimeoutMultiplier = 1000;

m_CommTimeouts.ReadTotalTimeoutConstant = 1000;

m_CommTimeouts.WriteTotalTimeoutMultiplier = 1000;

m_CommTimeouts.WriteTotalTimeoutConstant = 1000;

// 开始配置。超时、事件使能、配置波特率参数。就是把前面的各种参数写入到DCB结构中并使之生效

if (SetCommTimeouts(m_hComm, &m_CommTimeouts))

{                                      

if (SetCommMask(m_hComm, dwCommEvents))//一旦这个句柄对象上获取这个特定的事件,就去处理。SetCommMask函数可以设置一组在应用程序中检测通信设备通信的事件

{

if (GetCommState(m_hComm, &m_dcb))//获取设备表信息,然后SET之。GetCommState函数可以获取通信设备中当前正在发生的通信事件

{

m_dcb.fRtsControl = RTS_CONTROL_ENABLE;          // set RTS bit high!

if (BuildCommDCB(szBaud, &m_dcb))//设备控制块DCB填充函数

{

if (SetCommState(m_hComm, &m_dcb)); // normal operation... continue配置串口函数

else

ProcessErrorMessage("SetCommState()");//处理错误消息

}

else

ProcessErrorMessage("BuildCommDCB()");

}

else

ProcessErrorMessage("GetCommState()");

}

else

ProcessErrorMessage("SetCommMask()");

}

else

ProcessErrorMessage("SetCommTimeouts()");


delete [] szPort;//释放内存空间

delete [] szBaud;

//清空端口的各个缓冲区

PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);//清空缓冲区,后面的消息依次为:终止字符输入 终止字符输出 清除输入缓冲区 清除输出缓冲区

//离开临界区,端口已经安全配置

LeaveCriticalSection(&m_csCommunicationSync);

TRACE("Initialisation for communicationport %d completed.\nUse Startmonitor to communicate.\n", portnr);

return TRUE;

}


//  监视线程  这是最重要的函数 负责监视串口发生的各种事件并进行处理

UINT CSerialPort::CommThread(LPVOID pParam)

{

// Cast the void pointer passed to the thread back to

// a pointer of CSerialPort class

CSerialPort *port = (CSerialPort*)pParam;

// Set the status variable in the dialog class to

// TRUE to indicate the thread is running.

port->m_bThreadAlive = TRUE;       // Misc. variables

DWORD BytesTransfered = 0;

DWORD Event = 0;

DWORD CommEvent = 0;

DWORD dwError = 0;

COMSTAT comstat;//COMSTAT结构 通信设备状态信息室友该结构来存放的

memset(&comstat,0,sizeof(comstat));//初始化,将里面清零

BOOL  bResult = TRUE; // Clear comm buffers at startup

if (port->m_hComm)        // check if the port is opened

PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); //清空缓冲区

// begin forever loop.  This loop will run as long as the thread is alive.

for (;;) //死循环

{

// Make a call to WaitCommEvent().  This call will return immediatly

// because our port was created as an async port (FILE_FLAG_OVERLAPPED

// and an m_OverlappedStructerlapped structure specified).  This call will cause the

// m_OverlappedStructerlapped element m_OverlappedStruct.hEvent, which is part of the m_hEventArray to

// be placed in a non-signeled state if there are no bytes available to be read,

// or to a signeled state if there are bytes available.  If this event handle

// is set to the non-signeled state, it will be set to signeled when a

// character arrives at the port.


// we do this for each port!

//WaitCommEven等待端口事件 ,那些已经被set的事件类型  。异步方式,立即返回。

WaitCommEvent的同步或异步方式取决于句柄m_hComm,在前面第211行m_hComm设置成了异步方式,所以此处WaitCommEvent工作于异步方式

WaitCommEvent函数将和后面的WaitForMultipleObjects函数配合监视串口。

WaitCommEvent有错误的话,检查错误并清理错误,无错误  事件发生时用WaitForMultipleObjects确认具体事件。后面会详细说明。

bResult = WaitCommEvent(port->m_hComm, &Event, &port->m_ov);//调用成功返回非零值,失败返回0

if (!bResult)  //如果调用失败

{

// If WaitCommEvent() returns FALSE, process the last error to determin

// the reason..

switch (dwError = GetLastError()) // GetLastError检查错误类型

{

case ERROR_IO_PENDING:   //如果错误码是EROR_IO_PENDING(#define ERROR_IO_PENDING 997),表示操作转到后台运行。

{

// This is a normal return value if there are no bytes

// to read at the port.

// Do nothing and continue

break;

}

case 87:

{

// Under Windows NT, this value is returned for some reason.

// I have not investigated why, but it is also a valid reply

// Also do nothing and continue.

break;

}

default:

{

// All other error codes indicate a serious error has

// occured.  Process this error.

port->ProcessErrorMessage("WaitCommEvent()");//处理错误消息

break;

}

}

}

else//如果调用成功

{

// If WaitCommEvent() returns TRUE, check to be sure there are

// actually bytes in the buffer to read.

//

// If you are reading more than one byte at a time from the buffer

// (which this program does not do) you will have the situation occur

// where the first byte to arrive will cause the WaitForMultipleObjects()

// function to stop waiting.  The WaitForMultipleObjects() function

// resets the event handle in m_OverlappedStruct.hEvent to the non-signelead state

// as it returns.

//

// If in the time between the reset of this event and the call to

// ReadFile() more bytes arrive, the m_OverlappedStruct.hEvent handle will be set again

// to the signeled state. When the call to ReadFile() occurs, it will

// read all of the bytes from the buffer, and the program will

// loop back around to WaitCommEvent().

//

// At this point you will be in the situation where m_OverlappedStruct.hEvent is set,

// but there are no bytes available to read.  If you proceed and call

// ReadFile(), it will return immediatly due to the async port setup, but

// GetOverlappedResults() will not return until the next character arrives.

//

// It is not desirable for the GetOverlappedResults() function to be in

// this state.  The thread shutdown event (event 0) and the WriteFile()

// event (Event2) will not work if the thread is blocked by GetOverlappedResults().

//

// The solution to this is to check the buffer with a call to ClearCommError().

// This call will reset the event handle, and if there are no bytes to read

// we can loop back through WaitCommEvent() again, then proceed.

// If there are really bytes to read, do nothing and proceed.

//检测缓冲区,清楚错误

bResult = ClearCommError(port->m_hComm, &dwError, &comstat);

if (comstat.cbInQue == 0)// cbInQue是COMSTAT结构体(存放通信设备状态信息)中的一个参数,表示串行设备接收到的字节数。接收到的字节数为0,则继续等待事件触发,继续该循环

continue;

}  // end if bResult

// Main wait function.  This function will normally block the thread

// until one of nine events occur that require action

//等待事件发生,这是最主要的等待,等待的是m_hEventArray事件数组(第163 165 166行)中的三个事件的发生,如果一个都不发生,线程会被阻塞,一直在这里等待事件。

事件数组:

m_hEventArray[0] = m_hShutdownEvent;    

m_hEventArray[1] = m_ov.hEvent;

m_hEventArray[2] = m_hWriteEvent;

Event代表数组中事件的序号 0即m_hShutdownEvent

Event = WaitForMultipleObjects(3, port->m_hEventArray, FALSE, INFINITE);

switch (Event)

{

case 0:       // 0即m_hShutdownEvent 检测到杀死线程的事件

{

// Shutdown event.  This is event zero so it will be

// the higest priority and be serviced first.

port->m_bThreadAlive = FALSE;

// Kill this thread.  break is not needed, but makes me feel better.

AfxEndThread(100);//结束线程

break;

}

case 1: // read event

{  //获取与m_hComm关联的事件

GetCommMask(port->m_hComm, &CommEvent);// GetCommMask函数可以获取通信设备中当前正在发生的通信事件,以下EV_......... 均为串行通信事件

port是第282行CSerialPort *port = (CSerialPort*)pParam;定义的指针,m_pOwner头文件中定义CWnd* m_pOwner定义的指针

if (CommEvent & EV_CTS)   //CTS信号发生变化。SendMessage中的消息(如WM_COMM_CTS_DETECTED等)在头文件中有定义

::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_CTS_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);

if (CommEvent & EV_RXFLAG)//输入缓冲区收到事件字符

::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RXFLAG_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);

if (CommEvent & EV_BREAK)  // 检测到一个输入中断

::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_BREAK_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);

if (CommEvent & EV_ERR)  //发生行状态错误

::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_ERR_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);

if (CommEvent & EV_RING)   //检测到振铃信号

::SendMessage(port->m_pOwner->m_hWnd, WM_COMM_RING_DETECTED, (WPARAM) 0, (LPARAM) port->m_nPortNr);

if (CommEvent & EV_RXCHAR)   //输入缓冲区接收到新字符,其实这是检测的最主要的事件

// Receive character event from port.

ReceiveChar(port, comstat);//若接收到新字符 则接收字符

break;

}

case 2: // write event

{

// Write character event from port

WriteChar(port);//这个函数是写不是发送  而后面的WriteToPort是将字符发送出去

break;

}

} // end switch

} // close forever loop

return 0;

}//监视线程结束

//以下三个函数是控制监视线程的开始 恢复 停止

BOOL CSerialPort::StartMonitoring()// 开始监视端口,这里调用的正式上面的线程函数

{

if (!(m_Thread = AfxBeginThread(CommThread, this)))// AfxBeginThread函数创建工作者线程,其第一个函数是线程入口函数  即上面的监视线程函数CommThread

return FALSE;

TRACE("Thread started\n");

return TRUE;    

}

BOOL CSerialPort::RestartMonitoring()// 线程恢复

{

TRACE("Thread resumed\n");

m_Thread->ResumeThread();  //线程挂起后 用ResumeThread将线程恢复

return TRUE;    

}

BOOL CSerialPort::StopMonitoring()//停止线程(只是挂起 , 还可以恢复的,并不是杀死)

{

TRACE("Thread suspended\n");

m_Thread->SuspendThread();   // 用SuspendThread将线程挂起

return TRUE;    

}

//处理错误消息函数

void CSerialPort::ProcessErrorMessage(char* ErrorText)

{

char *Temp = new char[200];

LPVOID lpMsgBuf;

//将错误信息的错误代号装换成字符信息,并打印到 lpMsgBuf中

FormatMessage(

FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,

NULL,

GetLastError(),

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language

(LPTSTR) &lpMsgBuf,

0,

NULL

);

sprintf(Temp, "WARNING:  %s Failed with the following error: \n%s\nPort: %d\n", (char*)ErrorText, lpMsgBuf, m_nPortNr);

MessageBox(NULL, Temp, "Application Error", MB_ICONSTOP);  //这个函数在VS下要改变成_T("Application Error")

LocalFree(lpMsgBuf);

delete[] Temp;

}

void CSerialPort::WriteChar(CSerialPort* port) //端口操作函数,写字符

{

BOOL bWrite = TRUE;

BOOL bResult = TRUE;

DWORD BytesSent = 0;

ResetEvent(port->m_hWriteEvent); //先将写事件对象设置成无信号状态

EnterCriticalSection(&port->m_csCommunicationSync); //  Gain ownership of the critical section进入临界区

if (bWrite)

{

port->m_ov.Offset = 0; // Initailize variables

port->m_ov.OffsetHigh = 0;

// Clear buffer

PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);   //清除缓冲区

//调用写文件函数,向端口写数据

bResult = WriteFile(port->m_hComm,                                                // Handle to COMM Port

port->m_szWriteBuffer,                                 // Pointer to message

strlen((char*)port->m_szWriteBuffer),

&BytesSent,       // Where to store the number of bytes sent

&port->m_ov);   // Overlapped structure



// deal with any error codes

if (!bResult)  //如果WriteFile调用失败

{

DWORD dwError = GetLastError(); //查询错误类型

switch (dwError)

{

case ERROR_IO_PENDING: //ERROR_IO_PENDING(#define ERROR_IO_PENDING 997),表示操作转到后台运行

{

// continue to GetOverlappedResults()

BytesSent = 0;

bWrite = FALSE;   //置成FALSE

break;   //若跳出  执行548行

}

default:

{

// all other error codes

port->ProcessErrorMessage("WriteFile()");

}

}

}

else

{

LeaveCriticalSection(&port->m_csCommunicationSync);  //离开临界区

}

} // end if(bWrite)

if (!bWrite)

{

bWrite = TRUE;

// GetOverlappedResults()是异步IO操作结果获取函数 可以得到异步IO操作结果,第一个参数为句柄 第二个参数是 异步IO操作启动是指定的OVERLAPPED结构 第三个参数是 指向一个32为变量,该变量值为一个读写操作实际传输的字节数  第四个参数为若TRUE则函数要等到异步IO操作完成后才返回

bResult = GetOverlappedResult(port->m_hComm,  // Handle to COMM port

&port->m_ov,           // Overlapped structure

&BytesSent,              // Stores number of bytes sent

TRUE);                    // Wait flag


LeaveCriticalSection(&port->m_csCommunicationSync);

if (!bResult)  // deal with the error code 这个bResult 是bResult = GetOverlappedResult

{

port->ProcessErrorMessage("GetOverlappedResults() in WriteFile()");

}

} // end if (!bWrite)

// Verify that the data size send equals what we tried to send

if (BytesSent != strlen((char*)port->m_szWriteBuffer)) //VS中要改为if(BytesSent!= port->m_nWriteSize)

{

TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, strlen((char*)port->m_szWriteBuffer));

//VS中要改为

TRACE("WARNING: WriteFile() error.. Bytes Sent: %d; Message Length: %d\n", BytesSent, port->m_nWriteSize);

}

}

// Character received. Inform the owner

//接收数据

void CSerialPort::ReceiveChar(CSerialPort* port, COMSTAT comstat)

{

BOOL  bRead = TRUE;

BOOL  bResult = TRUE;

DWORD dwError = 0;

DWORD BytesRead = 0;

unsigned char RXBuff;

for (;;)

{

// Gain ownership of the comm port critical section.

// This process guarantees no other part of this program

// is using the port object.

//这就是线程同步了,不会有多个线程接收数据,那样就乱套了。

EnterCriticalSection(&port->m_csCommunicationSync);  //进入临界区 保证线程同步

// ClearCommError() will update the COMSTAT structure and

// clear any other errors.

//读缓冲区

bResult = ClearCommError(port->m_hComm, &dwError, &comstat); //清除错误

LeaveCriticalSection(&port->m_csCommunicationSync);  //离开临界区

// start forever loop.  I use this type of loop because I

// do not know at runtime how many loops this will have to

// run. My solution is to start a forever loop and to

// break out of it when I have processed all of the

// data available.  Be careful with this approach and

// be sure your loop will exit.

// My reasons for this are not as clear in this sample

// as it is in my production code, but I have found this

// solutiion to be the most efficient way to do this.

if (comstat.cbInQue == 0) //缓冲区输入字符为零  即没受到字符

{

// break out when all bytes have been read

break;

}

EnterCriticalSection(&port->m_csCommunicationSync);

if (bRead)

{

bResult = ReadFile(port->m_hComm,             // Handle to COMM port

&RXBuff,                        // RX Buffer Pointer

1,                             // Read one byte

&BytesRead,                    // Stores number of bytes read

&port->m_ov);          // pointer to the m_ov structure

// deal with the error code

if (!bResult)   //若调用失败

{

switch (dwError = GetLastError())

{

case ERROR_IO_PENDING:  

{

// asynchronous i/o is still in progress

// Proceed on to GetOverlappedResults();

bRead = FALSE;

break;

}

default:

{

// Another error has occured.  Process this error.

port->ProcessErrorMessage("ReadFile()");

break;

}

}

}

else

{

// ReadFile() returned complete. It is not necessary to call GetOverlappedResults()

bRead = TRUE;

}

}  // close if (bRead)


if (!bRead)

{

bRead = TRUE;

//获取异步IO操作结果

bResult = GetOverlappedResult(port->m_hComm,  // Handle to COMM port

&port->m_ov,           // Overlapped structure

&BytesRead,             // Stores number of bytes read

TRUE);                    // Wait flag

// deal with the error code

if (!bResult)

{

port->ProcessErrorMessage("GetOverlappedResults() in ReadFile()");

}

}  // close if (!bRead)


LeaveCriticalSection(&port->m_csCommunicationSync);

// notify parent that a byte was received

::SendMessage((port->m_pOwner)->m_hWnd, WM_COMM_RXCHAR, (WPARAM) RXBuff, (LPARAM) port->m_nPortNr);

} // end forever loop

}

//向写缓冲区写数据,这里是发送,触发写事件


void CSerialPort::WriteToPort(char* string)

{        

assert(m_hComm != 0);

memset(m_szWriteBuffer, 0, sizeof(m_szWriteBuffer));  //清零

strcpy(m_szWriteBuffer, string);

// set event for write

SetEvent(m_hWriteEvent);

}



//返回设备控制表,这是一个结构

DCB CSerialPort::GetDCB()

{

return m_dcb;

}

// 返回通讯事件

DWORD CSerialPort::GetCommEvents()

{

return m_dwCommEvents;

}

//返回写缓冲区大小

DWORD CSerialPort::GetWriteBufferSize()

{

return m_nWriteBufferSize;

}


你可能感兴趣的:(CSerialPort类)