介绍如何使用win32API函数进行通信端口的编程,因为串口数据接收需要不停地监视串口和处理串口事件,因此,再本实例中将其封装为单个线程执行类。
创建串口线程类并初始化,由CThreadCom1对象的构造函数完成,它实现串口工作变量的初始化工作。
CThreadCom1::CThreadCom1(HANDLE hCom)
{
m_hCom=hCom;
m_bInit=false;
m_sCom="";
m_sError="No Error!";
m_hThread=NULL;
m_dwSendMsgToParent=0;
m_dwRecvMsgToParent=0;
m_pWndParent=NULL;
memset((unsigned char*)&m_overRead,0,sizeof(OVERLAPPED));
memset((unsigned char*)&m_overWrite,0,sizeof(OVERLAPPED));
m_overRead.hEvent=CreateEvent(NULL,true,false,NULL);
m_overWrite.hEvent=CreateEvent(NULL,true,false,NULL);
}
CThreadCom1::~CThreadCom1()
{
CloseHandle(m_overRead.hEvent);
CloseHandle(m_overWrite.hEvent);
}
BOOL CThreadCom1::InitInstance()
{
// TODO: 在此执行任意逐线程初始化
m_bAutoDelete=false;
m_bDone=false;
return TRUE;
}
int CThreadCom1::ExitInstance()
{
// TODO: 在此执行任意逐线程清理
BOOL bFlag=CloseCom();
return CWinThread::ExitInstance();
}
串口接收线程的处理工作由CThreadCom1对象的Run()函数完成。它实现
循环从串口读取数据,并将接收的数据发送给处理函数的功能。
int CThreadCom1::Run(void)//重载线程类的Run()运行函数
{
//return 0;
DWORD dwError,dwReadNum,dwByteRead,dwEvent;
COMSTAT ComStat;//串口状态变量
BYTE rBuf[MAXCOMINBUF];//输入缓冲区
while(!m_bDone)
{
while(m_hCom!=INVALID_HANDLE_VALUE)
{
if(::WaitCommEvent(m_hCom,&dwEvent,NULL))//等待注册的串口事件发生
{
dwByteRead=0;//初始化读字节数
if(dwEvent&EV_RXCHAR)
{
ClearCommError(m_hCom,&dwError,&ComStat);//清空当前串口事件
if(ComStat.cbInQue!=0)//输入列队中的数据个数
{
dwReadNum=ComStat.cbInQue;
dwByteRead=0;
if(dwReadNum>200)
dwReadNum=200;
memset(rBuf,0,sizeof(rBuf));//清空接收缓冲区变量
DWORD i=::ReadFile(m_hCom,rBuf,dwReadNum,&dwByteRead,&m_overRead);
for(i=dwByteRead;iSendMessage(m_dwRecvMsgToParent,(DWORD)rBuf,dwByteRead);//读取的数据发送到父窗口
}
}
Sleep(1000);
}
return CWinThread::Run();//调用基类的Run()函数
}
//打开串口,根据程序配置,打开指定工作串口的功能
BOOL CThreadCom1::OpenCom(CString strCom, CWnd* pWndParent, DWORD dwSendMsgToParent, DWORD dwRecvMsgToParent)
{
//return 0;
CloseCom();
CString strLog;
m_hCom=::CreateFile(strCom,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);//打开串口
if(m_hCom==INVALID_HANDLE_VALUE)
{
strLog.Format("Open %s Error",strCom);
AfxMessageBox(strLog);
return FALSE;
}
::SetupComm(m_hCom,MAXCOMINBUF,MAXCOMOUTBUF);//设置串口输入输出缓冲区
DCB dcb;//定义DCB结构
if(!GetCommState(m_hCom,&dcb))
{
AfxMessageBox("获取串口状态错误");
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;
return FALSE;
}
if(!SetCommState(m_hCom,&dcb))
{
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;
strLog.Format("set %s CommState Error!",strCom);
AfxMessageBox(strLog);
return FALSE;
}
//
m_sError="No Error";
m_pWndParent = pWndParent;//存储父窗口句柄
m_dwSendMsgToParent = dwSendMsgToParent;//存储接收发送消息的父窗口
m_dwRecvMsgToParent = dwRecvMsgToParent;//存储接收接收消息的父窗口
//
DWORD CommMask;
CommMask=0|EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_EVENT1|EV_EVENT2|EV_PERR|EV_RING|EV_RLSD|EV_RX80FULL|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY;
::SetCommMask(m_hCom,CommMask);//注册要处理的事件
::GetCommTimeouts(m_hCom,&m_Commtimeout);//读取超时时间设置
m_Commtimeout.ReadTotalTimeoutMultiplier=5;
m_Commtimeout.ReadTotalTimeoutConstant=100;
m_bInit=true;
return TRUE;
}
//关闭串口
BOOL CThreadCom1::CloseCom(void)
{
//return 0;
if(m_hCom!=INVALID_HANDLE_VALUE)
{
PurgeComm(m_hCom,PURGE_RXCLEAR);//释放串口事件
CloseHandle(m_hCom);
m_hCom=INVALID_HANDLE_VALUE;//设置句柄无效
}
m_bInit=false;
return true;
}
//发送数据
BOOL CThreadCom1::SendData(BYTE* s, DWORD dwLen)
{
//return 0;
if(!dwLen)
return false;
::GetCommTimeouts(m_hCom,&m_Commtimeout);
m_Commtimeout.WriteTotalTimeoutMultiplier=0;//设置读操作超时时间
m_Commtimeout.WriteTotalTimeoutConstant=2*dwLen;//设置读操作超时常量
::SetCommTimeouts(m_hCom,&m_Commtimeout);//设置超时时间设置
if(m_hCom!=INVALID_HANDLE_VALUE)
{
DWORD dwSend;
m_pWndParent->SendMessage(m_dwSendMsgToParent,(DWORD)s,dwLen);
//发送日志
if(!WriteFile(m_hCom,s,dwLen,&dwSend,&m_overWrite))//向串口发送数据
{
m_sError="串口发送数据失败";
return false;
}
return true;
}
else
{
m_sError="串口句柄无效";
return false;
}
}
串口线程类代码结束后。界面的处理,对话框类中,实现相关按钮触发事件。
对话框类头文件相关声明:
public:
// CString m_Log;
CString m_editSend;
CString m_editReceive;
CComboBox m_comboComm;
afx_msg void OnBnClickedButtonOpenserial();
CThreadCom1* pThreadCom;
BOOL m_bCom;
void WriteLog(CString log);
afx_msg void OnBnClickedButtonSend();
// void OnSendMsg(DWORD dwEvent, DWORD dwLen);
CEdit m_Log;
// void OnRecvMsg(DWORD dwEvent, DWORD dwLen);
//HRESULT OnSendMsg(DWORD dwEvent, DWORD dwLen);
HRESULT OnSendMsg(WPARAM dwEvent, LPARAM dwLen);
//HRESULT OnRecvMsg(DWORD dwEvent, DWORD dwLen);
HRESULT OnRecvMsg(WPARAM dwEvent, LPARAM dwLen);
CEdit m_edit_Receive1;
//打开串口的按钮
void CTxwtechWin32API_CommDlg::OnBnClickedButtonOpenserial()
{
// TODO: 在此添加控件通知处理程序代码
if(pThreadCom!=NULL) return;
CString str;
CString com;
//int com1;
//com1=m_comboComm.GetCurSel();
//com1+=1;
//com=m_comboComm.GetWindowText();
m_comboComm.GetWindowTextA(com);//获取组合框com的内容
//com.Format("%s",com1);
pThreadCom=(CThreadCom1*)AfxBeginThread(RUNTIME_CLASS(CThreadCom1));
pThreadCom->SetComStr(com);//设置串口线程的串口名称变量
if(pThreadCom->OpenCom(com,(CWnd*)this->GetSafeOwner(),WM_USER_COMSENDMESSAGE,WM_USER_COMRECVMESSAGE))
{
str.Format("打开串口%s成功",pThreadCom->GetComStr());
WriteLog(str);
}
else
{
str.Format(pThreadCom->m_sError+",请重新配置串口");
WriteLog(str);
pThreadCom->m_bInit=FALSE;
return;
}
m_bCom=TRUE;
m_comboComm.EnableWindow(FALSE);
return;
}
//发送按钮绑定的代码
void CTxwtechWin32API_CommDlg::OnBnClickedButtonSend()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(true);
int iLen=m_editSend.GetLength();
BYTE* s=new BYTE[iLen];
memset(s,0x00,iLen);//初始化数据缓冲区
memcpy(s,(LPCTSTR)m_editSend,iLen);//复制数据
pThreadCom->SendData((unsigned char*)s,iLen);
}
//发送数据通知
HRESULT CTxwtechWin32API_CommDlg::OnSendMsg(WPARAM dwEvent, LPARAM dwLen)
{
if(!dwLen)
return 0;
BYTE* temp=new BYTE[dwLen+1];
memset(temp,0x00,dwLen+1);
memcpy(temp,(const void*)dwEvent,dwLen);
CString log;
log.Format("\r\n发送数据%s",(LPCTSTR)temp);
if(m_Log)
{
CEdit* editLog=(CEdit*)FromHandle(m_Log);
if(editLog->GetWindowTextLength()>50000)
{
editLog->SetSel(0,-1);//把发送区的全部内容选中
editLog->Clear();//清空全部内容
editLog->SetSel(0,0);//设置光标到0字节处
editLog->ReplaceSel(log);//更新新的内容
}
else
{
editLog->SetSel(editLog->GetWindowTextLength(),editLog->GetWindowTextLength());//光标指向句末
editLog->ReplaceSel(log);//添加新内容
}
}
// return;
return E_NOTIMPL;
}
//接收消息通知
HRESULT CTxwtechWin32API_CommDlg::OnRecvMsg(WPARAM dwEvent, LPARAM dwLen)
{
if(!dwLen) return 0;
BYTE* temp=new BYTE[dwLen+1];
memset(temp,0x00,dwLen+1);
memcpy(temp,(const void*)dwEvent,dwLen);
CString log;
log.Format("\r\n接收数据=%s",(LPCTSTR)temp);
if(m_editReceive.GetLength()>50000)
m_editReceive="";
m_editReceive+=log;
CEdit* editLog=(CEdit*)FromHandle(m_edit_Receive1);
editLog->SetSel(editLog->GetWindowTextLength(),editLog->GetWindowTextLength());
editLog->ReplaceSel(log);
//UpdateData(false);
return E_NOTIMPL;
}
相关调试遇到的问题:
error C2511: “CThreadCom1::CThreadCom1(HANDLE)”:“CThreadCom1”中没有找到重载的成员函数 原因:头文件未定义。 CThreadCom1(HANDLE hCom=INVALID_HANDLE_VALUE); error C2668: “CThreadCom1::CThreadCom1”: 对重载函数的调用不明确 头文件中只有有一个重载函数,把默认的删除掉
TxwtechWin32API_Comm.exe 中的 0x100c14cf (msvcr100d.dll) 处有未经处理的异常: 0xC0000005: 读取位置 0x00000002 时发生访问冲突, rootcause:连接串口不能是数值,而是字符串COM1...
TxwtechWin32API_Comm.exe 中的 0x7898391c (mfc100d.dll) 处有未经处理的异常: 0xC0000005: 读取位置 0x00000020 时发生访问冲突 缺少如下两句://m_dwSendMsgToParent 没有获取父窗口的值 //m_dwSendMsgToParent = dwSendMsgToParent; m_dwRecvMsgToParent = dwRecvMsgToParent;
接收框中无法接收消息,注意消息的映射与宏定义
#define WM_USER_COMSENDMESSAGE WM_USER+200 #define WM_USER_COMRECVMESSAGE WM_USER+201
BEGIN_MESSAGE_MAP(CTxwtechWin32API_CommDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_OpenSerial, &CTxwtechWin32API_CommDlg::OnBnClickedButtonOpenserial)
ON_BN_CLICKED(IDC_BUTTON_Send, &CTxwtechWin32API_CommDlg::OnBnClickedButtonSend)
ON_MESSAGE(WM_USER_COMSENDMESSAGE,OnSendMsg)
ON_MESSAGE(WM_USER_COMRECVMESSAGE,OnRecvMsg)
END_MESSAGE_MAP()