📄 apexcommctl.cpp
字号:
SetModifiedFlag();
}
//////////////////////////////////////////////////////////
// 函数: OpenPort
//
// 目的: 在相应的端口启动串口通讯
//
// 参数:
// hNewCommFile - 用于通讯的串口设备句柄
// 通过调用TAPI系统函数得到.
//
// 输出:
// Successful: 启动串口通讯.
// Failure: 产生异常事件.
//
// 注释:
//
// OpenPort 函数确保相应串口没有正在进行的通讯过程,
// 创建通讯用串口句柄, 创建读写线程. 同时为端口设置合适的参数.
//
// 不管何种原因导致OpenPort 实施失败, 那么调用该函数的相应用户程序要
// 对它进行响应的处理:关闭其它程序打开的端口或者其它操作.因为控件不能
// 随便关闭其它程序打开的通讯过程.
///////////////////////////////////////////////////////////////////
void CApexCommCtrl::OpenPort()
{
HANDLE hNewCommFile;
//已经有通讯过程在执行吗?
if (m_hCommFile != NULL)
throw "该端口已经打开!";
hNewCommFile = CreateFile( m_portID,
GENERIC_READ | GENERIC_WRITE,
0, //{! shared}
NULL, //{no security}
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL );//{template}
if (hNewCommFile == INVALID_HANDLE_VALUE) //得到非法值
throw "无法打开串口资源!";
// 这是一个合法的串口通讯句柄吗?
if (GetFileType( hNewCommFile ) != FILE_TYPE_CHAR)
{
CloseHandle( hNewCommFile );
throw "得到的文件句柄不为串口句柄!";
}
//设置输入输出缓冲区
if (! SetupComm( hNewCommFile, m_inputBufferSize, m_outputBufferSize ))
{
CloseHandle( hNewCommFile );
throw "无法设置输入输出缓冲区!";
}
// 成功创建串口通讯.
m_hCommFile = hNewCommFile;
//将相应的串口缓冲区(不管输入还是输出)清空;终止正在进行的输入输出操作
//并立即返回
PurgeComm( m_hCommFile, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
m_bSendDataEmpty = true;//设缓冲区空标志
// 查询并设置超时时间值
SetCommTimeState();
// 查询并设置通讯状态,包括波特率,字节数,停止位等.
SetPortState();
// 创建通知线程结束的消息.
m_hCloseEvent = CreateEvent( NULL, true, false, NULL );
//成功创建吗
if (m_hCloseEvent == 0)
{
CloseHandle( m_hCommFile );
m_hCommFile = NULL;
throw "无法创建线程结束消息事件!";
}
// 创建读线程.
try
{
ReadThread =new TReadThread;
if (ReadThread==NULL)
throw "无法生成新进程,程序将终止!";
if (!ReadThread->CreateThread(CREATE_SUSPENDED))
{
delete ReadThread;
throw "无法启动新进程,程序将终止!";
}
}
catch(char * ErrorString)
{
ReadThread = NULL;
CloseHandle( m_hCloseEvent );
CloseHandle( m_hCommFile );
m_hCommFile = NULL;
throw ErrorString;
}
ReadThread->m_hCommFile = m_hCommFile;
ReadThread->m_hCloseEvent = m_hCloseEvent;
ReadThread->m_handlemsg=m_hWnd;
// 串口通讯具有最高优先级.
// 如果不这样,那么串口通讯将丢失数据
ReadThread->SetThreadPriority(THREAD_PRIORITY_HIGHEST);
// 创建写线程.
try
{
WriteThread = new TWriteThread;
if (WriteThread==NULL)
throw "无法生成新进程,程序将终止!";
if (!WriteThread->CreateThread(CREATE_SUSPENDED))
{
delete WriteThread;
throw "无法启动新进程,程序将终止!";
}
}
catch(char * ErrorString)
{
CloseReadThread();
WriteThread = NULL;
CloseHandle( m_hCloseEvent );
CloseHandle( m_hCommFile );
m_hCommFile = NULL;
throw ErrorString;
}
WriteThread->m_hCommFile = m_hCommFile;
WriteThread->m_hCloseEvent = m_hCloseEvent;
WriteThread->m_handlemsg=m_hWnd;
WriteThread->m_pbSendDataEmpty = m_bSendDataEmpty;
WriteThread->SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
//启动线程
ReadThread->ResumeThread();
WriteThread->ResumeThread();
//至此串口通讯设置完毕,串口通讯准备就绪
}
//////////////////////////////////////////////////////
// 函数: ClosePort
//
// 目的: 停止串口通讯和读写线程
//
// 参数: 无
//
// 返回值: 无
//
// 注释:
//
// 要通知所有的通讯线程结束通讯,如果不能结束
// 要用户主动结束它
/////////////////////////////////////////////////////////
void CApexCommCtrl::ClosePort()
{
// 没有通讯当然不用结束通讯过程
if (m_hCommFile == NULL )
return;
// 关闭线程.
CloseReadThread();
CloseWriteThread();
// 停止消息传递.
CloseHandle( m_hCloseEvent );
// 关闭通讯串口.
CloseHandle( m_hCommFile );
m_hCommFile = NULL;
}
////////////////////////////////////////////////////////
// 函数: WriteComX(PChar, Word)
//
// 目的: 向串口写入字符串,该字符串等待写线程的传送.
//
// 参数:
// pszStringToWrite - 字符串.
// nSizeofStringToWrite - 字符串长度.
//
// 返回值:
// 如果消息成功发布,则返回true.
// 如果失败或者写线程不存在则返回false.
//
// 注释:
//
// 这是一个封装好的函数,用户不必知道写串口是通过向写线程发布消息实现的
// 使用这种方法可以加速对 UI的反应 (very little delay to
// 'write' a string) 如果写过程速度慢,那么还可以自动分配到缓冲区
// (ie: the messages just pile up in the message queue).
//
// Note that it is assumed that pszStringToWrite is allocated with
// LocalAlloc, and that if WriteComX succeeds, its the job of the
// Write thread to LocalFree it. If WriteComX fails, its
// the job of the calling function to free the string.
//////////////////////////////////////////////////////////////////////
BOOL CApexCommCtrl::WritePort(LPCTSTR pDataToWrite, long dwSizeofDataToWrite)
{
LPSTR Buffer;
if ((WriteThread != NULL) && (dwSizeofDataToWrite != 0))
{
Buffer = (LPSTR)(LocalAlloc( LPTR, dwSizeofDataToWrite+1 ));
strncpy(Buffer,pDataToWrite,dwSizeofDataToWrite);
Buffer[dwSizeofDataToWrite] = '\0';
if (PostThreadMessage( WriteThread->m_nThreadID, USER_WRITEPORT,WPARAM(dwSizeofDataToWrite), LPARAM(Buffer)))
{
m_bSendDataEmpty = false;
return true;
}
else
{
LocalFree(HLOCAL(Buffer));
}
}
return false;
}
void CApexCommCtrl::SetPortState()
{
DCB dcb;//保存通讯设置参数的结构体
COMMPROP commprop;//保存通讯设置参数状态的结构体
DWORD fdwEvtMask;
// Configure the comm settings.
// !E: Most Comm settings can be set through TAPI, but this means that
// the CommFile will have to be passed to this component.
if (!GetCommState(m_hCommFile, &dcb))
throw "无法取到串口配置";
GetCommProperties(m_hCommFile, &commprop );
GetCommMask(m_hCommFile, &fdwEvtMask );
// fAbortOnError is the only DCB dependancy in TapiComm.
// Can't guarentee that the SP will set this to what we expect.
//{dcb.fAbortOnError = false; NOT VALID}
dcb.BaudRate = m_baudRate;
dcb.fBinary = 1; // Enable fBinary
dcb.fParity = m_enableParity; // Enable parity check
dcb.fOutxCtsFlow =m_outxCtsFlow; // setup hardware flow control
dcb.fOutxDsrFlow = m_outxDsrFlow;
switch(m_dtrControl)
{
case 0:
dcb.fDtrControl=DTR_CONTROL_ENABLE;
break;
case 1:
dcb.fDtrControl=DTR_CONTROL_DISABLE;
break;
case 2:
dcb.fDtrControl=DTR_CONTROL_HANDSHAKE;
break;
}
dcb.fDsrSensitivity = m_dsrSensitivity;
dcb.fTXContinueOnXoff = m_txContinueOnXoff;
dcb.fOutX = m_outxXonXoffFlow;
dcb.fInX = m_inxXonXoffFlow;
dcb.fErrorChar = m_replaceWhenParityError;
dcb.fNull = m_ignoreNullChar;
switch(m_rtsControl)
{
case 0:
dcb.fRtsControl=RTS_CONTROL_ENABLE;
break;
case 1:
dcb.fRtsControl=RTS_CONTROL_DISABLE;
break;
case 2:
dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
break;
case 3:
dcb.fRtsControl=RTS_CONTROL_TOGGLE;
break;
}
dcb.XonLim = m_xonLimit;
dcb.XoffLim = m_xoffLimit;
dcb.ByteSize = (unsigned char)(m_byteSize + 5);
dcb.Parity = (unsigned char) m_parity;
dcb.StopBits = (unsigned char) m_stopBits;
dcb.XonChar = char(m_xonChar);
dcb.XoffChar = char(m_xoffChar);
dcb.ErrorChar = char(m_replaceChar);
if (!SetCommState( m_hCommFile, &dcb))
throw "无法配置串口!";
}
void CApexCommCtrl::SetCommTimeState()
{
COMMTIMEOUTS commtimeouts;
if (!GetCommTimeouts(m_hCommFile, &commtimeouts))
throw "无法得到串口时间配置!";
// The CommTimeout numbers will very likely change if you are
// coding to meet some kind of specification where
// you need to reply within a certain amount of time after
// recieving the last byte. However, If 1/4th of a second
// goes by between recieving two characters, its a good
// indication that the transmitting end has finished, even
// assuming a 1200 baud modem.
commtimeouts.ReadIntervalTimeout = m_readIntervalTimeout;
commtimeouts.ReadTotalTimeoutMultiplier = m_readTotalTimeoutMultiplier;
commtimeouts.ReadTotalTimeoutConstant = m_readTotalTimeoutConstant;
commtimeouts.WriteTotalTimeoutMultiplier = m_writeTotalTimeoutMultiplier;
commtimeouts.WriteTotalTimeoutConstant = m_writeTotalTimeoutConstant;
if (!SetCommTimeouts(m_hCommFile, &commtimeouts))
throw "无法配置串口时间!";
}
////////////////////////////////////////////////////////
// 函数: CloseReadThread
//
// 目的: Close the Read Thread.
//
// 参数:
// none
//
// 返回值:
// none
//
// 注释:
//
// Closes the Read thread by signaling the CloseEvent.
// Purges any outstanding reads on the comm port.
//
// Note that terminating a thread leaks memory.
// Besides the normal leak incurred, there is an event object
// that doesn't get closed. This isn't worth worrying about
// since it shouldn't happen anyway.
//
//
void CApexCommCtrl::CloseReadThread()
{
// If it exists...
if (ReadThread != NULL )
{
// Signal the event to close the worker threads.
SetEvent( m_hCloseEvent );
// Purge all outstanding reads
PurgeComm( m_hCommFile, PURGE_RXABORT + PURGE_RXCLEAR );
// Wait 10 seconds for it to return. Shouldn't happen.
//如果通过消息传递来关闭线程不能实现则主动删除
if (WaitForSingleObject(ReadThread->m_hThread, 10000) == WAIT_TIMEOUT)
TerminateThread(ReadThread->m_hThread,0);
ReadThread = NULL;
}
}
/////////////////////////////////////////////////////////
// 函数: CloseWriteThread
//
// 目的: 关闭写线程.
//
// 参数:
// none
//
// 返回值:
// none
/////////////////////////////////////////////////////////////////
void CApexCommCtrl::CloseWriteThread()
{
// If it exists...
if (WriteThread != NULL )
{
// Signal the event to close the worker threads.
SetEvent(m_hCloseEvent);
// Purge all outstanding writes.
PurgeComm(m_hCommFile, PURGE_TXABORT + PURGE_TXCLEAR);
m_bSendDataEmpty = true;
// Wait 10 seconds for it to return. Shouldn't happen.
if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT )
TerminateThread(WriteThread->m_hThread,0);
WriteThread = NULL;
};
}
////////////////////////////////////////////////////////////////////
//函数名:ReceiveData
//功能: 处理用户自定义消息:接收到数据
//参数: wParam数据长度,lParam字符串
//返回值:标准自定义消息返回值
//注释: 下面的函数形式相同
////////////////////////////////////////////////////////////////////
LRESULT CApexCommCtrl::ReceiveData(WPARAM wParam, LPARAM lParam )
{
LPSTR strdata;
WORD temp;
strdata=(LPSTR)lParam;
temp=wParam;
FireOnReceiveData(strdata,temp);
LocalFree((void*)lParam );
return 0;
}
LRESULT CApexCommCtrl::CommHangup(WPARAM wParam, LPARAM lParam )
{
WORD read_or_write;
read_or_write=HIWORD(lParam);
FireOnCommHangup(((read_or_write==0) ? 0:1));
return 0;
}
LRESULT CApexCommCtrl::ReceiveDataError(WPARAM wParam, LPARAM lParam )
{
long eventmask;
eventmask=(long)lParam;
FireReceiveDataError(eventmask);
return 0;
}
LRESULT CApexCommCtrl::DataSendFinished(WPARAM wParam, LPARAM lParam )
{
FireDataSendFinished();
return 0;
}
//只有这样才能让窗口接收消息,同时又不显示
BOOL CApexCommCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
LPCTSTR tt;
tt=AfxRegisterWndClass (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,AfxGetApp()->LoadCursor(IDC_NOTHING),0,0);
cs.lpszClass=tt;
if (AmbientUserMode())
cs.style = WS_CHILD | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX ;
else
cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_MAXIMIZEBOX;
cs.dwExStyle &=~WS_EX_NOPARENTNOTIFY;
return COleControl::PreCreateWindow(cs);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -