1. 序
确定基本功能:
1.自动寻找串口,并自动添加到下拉框中共选择;
2.有波特率、数据位、停止位、校验位的选择设置;
3.串口打开控制按钮;
4.发送、清除按钮;
5.接收是自动实现的;
6.有定时自动发送功能;
7.有传送文件功能;
8.有状态栏显示,指示串口状态,设置参数和发送接收显示。
下面就一步步实现,本人纯业余,只是记录下来这个学习过程,请勿拍砖。 开发平台Visual Studio 2019社区版,
2. 创建MFC项
2.1 打开Visual Studio 2019->创建新项目
2.2 选择 MFC应用程序->下一步
2.3 填写项目名称: commassist ,选择项目目录 ,点击 创建
2.4 配置工程
应用程序类型选择:基本对话框
选择最小化,最大化,勾选两个选项
生成类选择:Dlg
基类:CDialogEx ,其他选项默认即可,点击 完成
项目创建完毕,进入项目。
点击进入 “资源视图” 界面,
删除界面上确定和取消按钮以及静态文字。
3 创建界面
3.1 添加Group Box控件
并且修改两个控件的Caption属性,
上面的GroupBox控件Caption属性改为:接收区
下面的GroupBox控件Caption属性改为:发送区
3.2 添加Static Text控件
如图,从上到下添加5个Static Text控件,并且修改这5个控件的Caption属性
从上到下数:
第1个Static Text控件的Caption属性为:串口号
第2个Static Text控件的Caption属性为:波特率
第3个Static Text控件的Caption属性为:数据位
第4个Static Text控件的Caption属性为:校验位
第5个Static Text控件的Caption属性为:停止位
3.3 添加Combo Box控件
在5个Static Text控件的后面分别添加一个Combo Box控件,并且设置属性如下:
从上到下数:
第1个串口号的Combo Box控件:
ID属性:IDC_COMLIST
Sort属性:False
Type属性:下拉列表
第2个波特率的Combo Box控件:
ID属性:IDC_BAUD
Type属性:下拉列表
Sort属性:False
Data属性:110;300;600;1200;2400;4800;9600;14400;19200;38400;56000;57600;115200;128000;256000;
第3个数据位的Combo Box控件:
ID属性:IDC_BDATA
Type属性:下拉列表
Sort属性:False
Data属性:5;6;7;8;
第4个校验位的Combo Box控件:
ID属性:IDC_CAL
Type属性:下拉列表
Sort属性:False
Data属性:None;Odd;Even;Mark;Space;
第5个停止位的Combo Box控件:
ID属性:IDC_BSTOP
Type属性:下拉列表
Sort属性:False
Data属性:1;1.5;2;
3.4 添加Edit Control控件
在接收区GroupBox控件和发送区Group Box控件中分别添加一个Edit Control。
如图,添加两个Edit Control控件
接收区的Edit Control控件:
ID属性:IDC_EDIT_RX
Multiline属性:True //可多行显示
Want Return属性:True //可输入回车换行
Auto HScroll属性:False
Auto VScroll属性:True
Modal Frame属性:True
Vertical Scroll属性:True
发送区的Edit Control控件:
ID属性:IDC_EDIT_TX
Multiline属性:True
Want Return属性:True
Auto HScroll属性:True
Auto VScroll属性:True
Modal Frame属性:True
Vertical Scroll属性:True
3.5 添加按钮控件
添加7个按钮控件,根据图配置按钮控件。
第1个按钮控件:
ID属性:IDC_COMCONTROL
Caption属性:打开串口
第2个按钮控件:
ID属性:IDC_BTN_CLRRX
Caption属性:清空显示区
第3个按钮控件:
ID属性:IDC_BTN_HANDSEND
Caption属性:手动发送
第4个按钮控件:
ID属性:IDC_BTN_CLRTX
Caption属性:清空发送区
第5个按钮控件:
ID属性:IDC_BTN_AUTOSEND
Caption属性:自动发送
第6个按钮控件:
ID属性:IDC_BTN_SELCTFILE
Caption属性:选择文件
第7个按钮控件:
ID属性:IDC_BTN_SENDFILE
Caption属性:发送文件
3.6 添加Check Box控件
如图,添加两个Check Box控件
上面的Check Box控件:
Caption属性:十六进制显示
下面的Check Box控件:
Caption属性:十六进制发送
3.7 添加三个自动发送的控件
第1个控件:Static Text控件
Caption属性:每隔
第2个控件:Edit Control控件
ID属性:IDC_EDIT_TIMER
第3个控件:Static Text控件
Caption属性:毫秒
3.8 添加选择文件的一个控件
如图,添加一个Edit Control控件
ID属性:IDC_EDIT_FILEPATH
3.9 添加状态显示Edit Control控件
ID属性:IDC_EDIT_STATUS
Read Only属性:True
3.10 添加Picture Control控件
ID属性:IDC_STATIC_ICON
Type属性:Icon
Image属性:IDR_MAINFRAME
3.11 修改外框,添加最小化控件
选中外框,
Minimize Box属性:True
3.12 添加Icon图标
在 资源视图->Icon目录下->添加两个Icon图标
分别命名为IDI_ICON_CLOSE、IDI_ICON_OPEN
修改IDR_MAINFRAME图标:
3.13 效果图
最终效果图
运行时效果图:
4 开始写代码
4.1 基本思路
因为串口通信部分代码我可能用在以后的单片机上位机上,因此考虑单独形成CPP和H文件,定义为comm.cpp和comm.h。在comm.cpp中编写串口创建、打开、关闭以及串口监听线程(用于自动接收)的代码,同时加入进制转换或显示的函数,这些在comm.h文件中申明,
在主对话框中包含comm.h即可。想修改按钮样式,在网上搜了一圈,结果不轻松,最后确定创建新类来实现。
4.2 创建自定义按钮类
(1)右击选中项目名->类向导
(2)点击 添加类
(3)类名MyButton,基类选择CButton,点击 确定。
(4)在头文件 MyButton.h 中全部替换为以下变量和函数定义:
#if !defined(AFX_MYBUTTON_H__B834D0A9_C834_4584_BC86_9AD8264EB109__INCLUDED_)
#define AFX_MYBUTTON_H__B834D0A9_C834_4584_BC86_9AD8264EB109__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// MyButton.h : header file
//
/
// MyButton window
class MyButton : public CButton
{
private:
int m_Style; //按钮形状(0-正常,1-当前,2-按下,3-锁定)
bool b_InRect; //鼠标进入标志
CString m_strText; //按钮文字
COLORREF m_ForeColor;//文本颜色
COLORREF m_MouseInColor;//鼠标进入时文本颜色
COLORREF m_BackColor;//背景颜色
COLORREF m_LockForeColor; //锁定按钮的文字颜色
CRect m_ButRect; //按钮尺寸
CFont* p_Font; //字体
void DrawButton(CDC* pDC); //画正常按钮
// Construction
public:
MyButton();
void SetText(CString str); //设置文字
void SetForeColor(COLORREF color); //设置文本颜色
void SetBkColor(COLORREF color); //设置背景颜色
void SetTextFont(int FontHight, LPCTSTR FontName); //设置字体
// Attributes
public:
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(MyButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~MyButton();
// Generated message map functions
protected:
//{{AFX_MSG(MyButton)
afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point);
afx_msg void OnNcMButtonDown(UINT nHitTest, CPoint point);
afx_msg void OnNcMButtonUp(UINT nHitTest, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MYBUTTON_H__B834D0A9_C834_4584_BC86_9AD8264EB109__INCLUDED_)
(5)在 MyButton.cpp 中全部替换为下面的代码:
#include "pch.h"
#include "stdafx.h"
#include "commassist.h"
#include "MyButton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// MyButton
MyButton::MyButton()
{
m_Style = 1; //m_Style = 0; //按钮形状风格
b_InRect = false; //鼠标进入标志
m_strText = _T(""); //按钮文字(使用默认文字)
m_ForeColor = RGB(200, 0, 0); //文字颜色(黑色)
m_MouseInColor = RGB(0, 0, 255); //鼠标进入时文字颜色(蓝色)
m_BackColor = RGB(200, 200, 230); //m_BackColor = RGB(243,243,243); //背景色(灰白色)
m_LockForeColor = GetSysColor(COLOR_GRAYTEXT); //锁定按钮的文字颜色
p_Font = NULL; //字体指针
}
MyButton::~MyButton()
{
}
BEGIN_MESSAGE_MAP(MyButton, CButton)
//{{AFX_MSG_MAP(MyButton)
ON_WM_NCMOUSEMOVE()
ON_WM_NCMBUTTONDOWN()
ON_WM_NCMBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// MyButton message handlers
void MyButton::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
ModifyStyle(0, BS_OWNERDRAW); //设置按钮属性为自画式
//PreSubclassWindow()在按钮创建前自动执行,所以我们可以在其中做一些初始工作。
//这里只做了一项工作,就是为按钮设置属性为"自绘"式,这样,用户在添加按钮后,就不需设置"Owner draw"属性了。
CButton::PreSubclassWindow();
}
void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
m_ButRect = lpDrawItemStruct->rcItem; //获取按钮尺寸
if (m_strText.IsEmpty())
GetWindowText(m_strText); //获取按钮文本
int nSavedDC = pDC->SaveDC();
VERIFY(pDC);
DrawButton(pDC); //绘制按钮
pDC->RestoreDC(nSavedDC);
}
void MyButton::OnNcMouseMove(UINT nHitTest, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (!b_InRect || GetCapture() != this) //鼠标进入按钮
{
b_InRect = true; //设置进入标志
SetCapture(); //捕获鼠标
m_Style = 2; //m_Style = 1; //设置按钮状态
Invalidate(); //重绘按钮
}
else
{
if (!m_ButRect.PtInRect(point)) //鼠标离开按钮
{
b_InRect = false; //清除进入标志
ReleaseCapture(); //释放捕获的鼠标
m_Style = 1; //m_Style = 0; //设置按钮状态
Invalidate(); //重绘按钮
}
}
CButton::OnNcMouseMove(nHitTest, point);
}
void MyButton::OnNcMButtonDown(UINT nHitTest, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Style = 2;
Invalidate(); //重绘按钮
CButton::OnLButtonDown(nHitTest, point);
}
void MyButton::OnNcMButtonUp(UINT nHitTest, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Style = 1;
Invalidate(); //重绘按钮
CButton::OnNcMButtonUp(nHitTest, point);
}
void MyButton::DrawButton(CDC* pDC)
{
//调整状态
if (m_Style == 3) m_Style = 0;
if (GetStyle() & WS_DISABLED)
m_Style = 3; //禁止状态
//根据状态调整边框颜色和文字颜色
COLORREF bColor, fColor; //bColor为边框颜色,fColor为文字颜色
switch (m_Style)
{
case 0: bColor = RGB(192, 192, 192); fColor = m_ForeColor; break; //正常按钮
case 1: bColor = RGB(255, 255, 255); fColor = m_ForeColor; break; //鼠标进入时按钮
case 2: bColor = RGB(192, 192, 192); fColor = m_MouseInColor; break; //按下的按钮
case 3: bColor = m_BackColor; fColor = m_LockForeColor; break; //锁定的按钮
}
//绘制按钮背景
CBrush Brush;
Brush.CreateSolidBrush(m_BackColor); //背景刷
pDC->SelectObject(&Brush);
CPen Pen;
Pen.CreatePen(PS_SOLID, 3, bColor);
pDC->SelectObject(&Pen);
pDC->RoundRect(&m_ButRect, CPoint(10, 10)); //画圆角矩形
//绘制按钮按下时的边框
if (m_Style != 2)
{
CRect Rect;
Rect.SetRect(m_ButRect.left + 1, m_ButRect.top + 1, m_ButRect.right, m_ButRect.bottom);
pDC->DrawEdge(&Rect, BDR_RAISEDINNER, BF_RECT); //画边框
}
//绘制按钮文字
pDC->SetTextColor(fColor); //画文字
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(m_strText, &m_ButRect, DT_SINGLELINE | DT_CENTER
| DT_VCENTER | DT_END_ELLIPSIS);
//绘制拥有焦点按钮的虚线框
if (GetFocus() == this)
{
CRect Rect;
Rect.SetRect(m_ButRect.left + 3, m_ButRect.top + 2, m_ButRect.right - 3, m_ButRect.bottom - 2);
pDC->DrawFocusRect(&Rect); //画拥有焦点的虚线框
}
}
//设置按钮文本
void MyButton::SetText(CString str)
{
m_strText = _T("");
SetWindowText(str);
}
//设置文本颜色
void MyButton::SetForeColor(COLORREF color)
{
m_ForeColor = color;
Invalidate();
}
//设置背景颜色
void MyButton::SetBkColor(COLORREF color)
{
m_BackColor = color;
Invalidate();
}
//设置字体(字体高度、字体名)
void MyButton::SetTextFont(int FontHight, LPCTSTR FontName)
{
if (p_Font) delete p_Font; //删除旧字体
p_Font = new CFont;
p_Font->CreatePointFont(FontHight, FontName); //创建新字体
SetFont(p_Font); //设置字体
}
///由于新字体由 new 生成,必须显式回收,这项工作可以在 CMyButton类 的析构函数中进行:
/*CMyButton::~CMyButton()
{
if ( p_Font ) delete p_Font; //删除字体
}
*/
//这样一个可设置颜色、字体的按钮类就做好了。使用时,先在对话框中放置好按钮,再用 ClassWizard 为按钮添加控制变量,
//并且将变量的类型设置为 CMyButton。之后,可以用该变量调用接口函数设置按钮颜色和字体。
OK,自定义按钮完成。
4.3 实现过程及代码
4.3.1给相应控件添加变量
现在可以对按钮,EDIT框等控件添加变量,文字描述麻烦,上图:
4.3.2 新建comm.cpp
编写内容如下:
#include "pch.h"
#include "stdafx.h"
#include "commassist.h"
#include "commassistDlg.h"
#include "comm.h"
char ConvertHexChar(char ch);
HANDLE hCom; //串口句柄
CString strcomname; //串口名,如"COM1"
bool ComIsOK; //串口打开状态标识,为真表示已打开,否则未打开
//============自动寻找串口函数================================= //
//函数功能:通过扫描注册表来找出当前所有物理串口
//输入参数:无
//返回类型:无
//说 明:若搜索成功,则每搜到一个串口便发送消息通知主对话框,并将串口号以WPARAM传递
void FindComm()
{
//枚举当前系统中的串口
LONG result = 0;
HKEY key = NULL;
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, //需要打开的主键的名称
"HARDWARE\\DEVICEMAP\\SERIALCOMM",
//需要打开的子键的名称,设备串口
0, //保留,必须设置为0
KEY_READ, //安全访问标记,也就是权限
&key); //得到的将要打开键的句柄,当不再需要句柄,
//必须调用 RegCloseKey 关闭它
if( result )
{
AfxMessageBox("无法获取串口,请确认是否安装并连接串口!");
return;
}
TCHAR portname[250]; //串口名
TCHAR data[250];
DWORD portnamelen = 0; //串口名长度
DWORD datalen = 0;
int index = 0;
while(1) //找完COM后跳出
{
portnamelen = 255;
datalen = 255;
result = RegEnumValue(key,
//Long,一个已打开项的句柄,或者指定一个标准项名
index++,
//Long,欲获取值的索引。注意第一个值的索引编号为零
portname,
//String,用于装载位于指定索引处值名的一个缓冲区
&portnamelen,
//Long,用于装载lpValueName缓冲区长度的一个变量。
//一旦返回,它会设为实际载入缓冲区的字符数量
NULL,
//Long,未用;设为零
NULL,
//Long,用于装载值的类型代码的变量
(LPBYTE)data, //Byte,用于装载值数据的一个缓冲区
&datalen); //Long,用于装载lpData缓冲区长度的一个变量。
//一旦返回,它会设为实际载入缓冲区的字符数量
if( result )
break;
//发送消息,WM_USER+1为自定义消息,即找到串口的,并将串口号"COMx"通过WPARA M参数传送给主对话框窗口
//::AfxGetMainWnd()->m_hWnd,获得主对话框句柄
//(WPARAM)(LPCTSTR)data,类型转换
::SendMessage(::AfxGetMainWnd()->m_hWnd,WM_FOUNDCOMM,(WPARAM)(LPCTSTR)data,0);
}
RegCloseKey(key); //调用 RegCloseKey 关闭打开键的句柄
}
//==========串口打开函数===========================
//功 能:打开串口,将已打开的串口句柄赋值给hCom,给出串口打开状态ComIsOK,完成串口状态 设置
//输入参数:波特率,数据位,停止位,校验位
//返回类型:无
void OpenComm(int nBaud, int nData, int nStop, int nCal) {
hCom = CreateFile(strcomname, //串口号
GENERIC_READ | GENERIC_WRITE, //允许读或写
0, //独占方式
NULL,
OPEN_EXISTING, //打开而不是创建
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,//重叠方式,用于异步通信
NULL );
if (hCom == INVALID_HANDLE_VALUE)
{
AfxMessageBox(_T("打开COM失败,串口不存在或已被占用!"));
ComIsOK = false; return;
}
ComIsOK = true;
SetCommMask(hCom, EV_TXEMPTY | EV_RXCHAR); //设置事件掩码,暂时没用上
SetupComm(hCom,1024,1024); //设置输入缓冲区和输出缓冲区的大小都是1024
COMMTIMEOUTS TimeOuts; //设定读超时
TimeOuts.ReadIntervalTimeout = MAXDWORD;
TimeOuts.ReadTotalTimeoutConstant = 0;
TimeOuts.ReadTotalTimeoutMultiplier = 0; //设定写超时
TimeOuts.WriteTotalTimeoutConstant = 500;
TimeOuts.WriteTotalTimeoutMultiplier = 100;
if(SetCommTimeouts(hCom,&TimeOuts) == false)
{
CloseHandle(hCom);
ComIsOK = false; return;
} //串口属性配置
DCB dcb;
GetCommState(hCom,&dcb);
dcb.BaudRate=nBaud; //dcb.BaudRate=9600; //波特率为9600
dcb.ByteSize=nData; //dcb.ByteSize=8; //每个字节为8位
dcb.StopBits=nStop; //dcb.StopBits=ONESTOPBIT; //1位停止位
dcb.Parity=nCal; //dcb.Parity=NOPARITY; //无奇偶检验位
SetCommState(hCom, &dcb);
PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);
if (SetCommState(hCom, &dcb) == false)
{
CloseHandle(hCom);
ComIsOK = false;
return;
}
return;
}
//==========串口关闭控制函数=====================
void CloseComm()
{
CloseHandle(hCom);
hCom = NULL;
ComIsOK = false;
}
//==========串口监听线程函数======================
UINT ThreadFunc(LPVOID pParam)
{
// CCommassistDlg* pdlg = (CCommassistDlg*)pParam; //定义指针指向主对话框
COMSTAT ComStat;
DWORD dwErrorFlags;
while(ComIsOK)
{
DWORD dwBytesRead = 100;
ClearCommError(hCom,&dwErrorFlags,&ComStat);
dwBytesRead = min(dwBytesRead,(DWORD)ComStat.cbInQue);
if(!dwBytesRead)
{
Sleep(10);//continue;//使用continue时,打开串口后CPU占用率非常高
} else ::SendMessage(::AfxGetMainWnd()->m_hWnd,WM_READCOMM,1,0); //发送消息,已读到
}
return 0;
}
//=================字符串转16进制显示==========
//字符串转16进制显示的函数
//传入参数Data为字符串
//Blank_allow为空格允许标志,为真则代表允许加入空格
//函数返回为CString的结果sResult
CString DisplayCString2Hex(CString Data, bool Blank_allow)
{
CString sResult;
CString sTemp;
int Data_Length;
Data_Length = Data.GetLength();
if (Data_Length == 0)
return "";
char *pchar = new char[Data_Length+1]; //用了new分配内存空间,要记得释放
strncpy_s(pchar, Data_Length+1,Data,Data_Length);//此处使用strncpy_s(char * str2, int size2, char * str1, int size1);
//这里多了一个长度,就是被复制的str2的长度,我们可以用sizeof(str2)来表示这个长度
for(int i=0; i<Data_Length; i++)
{
sTemp.Format("%02X",pchar[i]);
if(Blank_allow)
{
if(i == Data_Length -1)
sResult = sResult + sTemp; //去掉最后一个空格
else
sResult = sResult + sTemp+" ";
}
else sResult = sResult + sTemp;
}
delete pchar; //释放内存空间
return sResult;
}
char ConvertHexChar(char ch)
{
//将一个字符转换为相应的十六进制
if ((ch >= '0') && (ch <= '9'))
return ch - 48;//0x30;
else if ((ch >= 'A') && (ch <= 'F'))
return ch - 'A' + 10;
else if ((ch >= 'a') && (ch <= 'f'))
return ch - 'a' + 10;
else return (-1);
}
//=================16进制转字符串======================
//16进制转字符串,输入16进制的字符串,输出转换为16进制码
//传入参数str为字符串,判断输入是否按照16进制格式输入
int ConvertHexC2String(CString str, CByteArray& senddata)
{
//先判断输入字符串是否2个字符一组
int str_Length,iLength;
int hexdata, l_data;
char hstr,lstr;
char cTemp; str_Length = str.GetLength();
iLength = 0;
senddata.SetSize(str_Length/2); //预先设置数组长度,不设置时,允许有错
char *ppchar = new char[str_Length+1];
strncpy_s(ppchar, str_Length+1,str,str_Length);
for(int i=0; i<str_Length; )
{
cTemp = ppchar[i];
if(cTemp == ' ')
{
//iLength--;
i++;
continue; //如检测到空格则跳过,继续下一次循环
}
else
{
hstr = ppchar[i]; //取出字符作为16进制高位
i++;
lstr = ppchar[i]; //取出下一个字符作为16进制低位
if(lstr == ' ') //若取出的低位为空格,则不符合16进制2个一组的格式,终止循环
{
AfxMessageBox("请按照16进制每2个字符一组的方式输入", MB_ICONERROR); break;
}
else
{
hexdata = ConvertHexChar(hstr); //高位转换为相应的0进制
l_data = ConvertHexChar(lstr); //低位转换为相应的10进制
if( (hexdata == -1) || (l_data == -1) )
{
AfxMessageBox("请按照16进制字符要求输入",MB_ICONERROR);
break;
}
else
hexdata = hexdata*16 + l_data; //安装16进制方式高位低位合并
senddata[iLength] = (char)hexdata; //int整型数转换为char字符型,并存入数组senddata[]
i++; //进入下一次循环
iLength++; //成功转换一组(2个)字符,记录长度加1
}
}
}
senddata.SetSize(iLength);
delete ppchar;
return iLength;
}
//=================16进制转字符串显示=====================
//16进制转字符串显示的函数
//传入参数Data为16进制的字符串
//函数返回为CString的结果sResult
CString DisplayHex2CString(CString Data)
{
CString sResult;
CString sTemp;
int Data_Length;
Data_Length = Data.GetLength();
if (Data_Length == 0)
return "";
char* pchar = new char[Data_Length+1]; //用了new分配内存空间,要记得释放
strncpy_s(pchar, Data_Length+1,Data,Data_Length);
for (int i = 0; i < Data_Length; i++)
{
sTemp.Format("%02X", pchar[i]);
sResult = sResult + sTemp;
}
delete pchar; //释放内存空间
return sResult;
}
4.2.3 新建comm.h
编写如下:
#pragma once
#define WM_FOUNDCOMM WM_USER + 1 //自定义消息WM_FOUNDCOMM,收到该消息表示串口已经找到
#define WM_READCOMM WM_USER + 2 //自定义消息WM_READCOMM,收到该消息缓冲区有数据,可以读取
extern void FindComm(); //申明为外部函数
extern void OpenComm(int nBaud, int nData, int nStop, int nCal);
extern void CloseComm(); extern UINT ThreadFunc(LPVOID pParam); //申明全局线程处理函数
extern CString DisplayCString2Hex(CString Data, bool Blank_allow);
extern CString DisplayHex2CString(CString Data);
extern int ConvertHexC2String(CString str, CByteArray &senddata);
extern bool ComIsOK; //申明为外部变量
extern HANDLE hCom;
extern CString strcomname;
4.2.4 在commassistDlg.h中替换为如下代码
// commassistDlg.h: 头文件
//
#pragma once
#include "MyButton.h"
// CcommassistDlg 对话框
class CcommassistDlg : public CDialogEx
{
// 构造
public:
CcommassistDlg(CWnd* pParent = nullptr); // 标准构造函数
CWinThread* pReceiveThread;
void ShowStatus();
int m_intTxCnt;
int m_intRxCnt;
BOOL m_bAutoSend;
DWORD ReadComm();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_COMMASSIST_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnEnChangeEdit1();
CComboBox m_baud;
CComboBox m_bdata;
CComboBox m_bstop;
CComboBox m_cal;
MyButton m_autosend;//MyButton
MyButton m_clrrx;//MyButton
MyButton m_clrtx;//MyButton
MyButton m_handsend;//MyButton
MyButton m_selfile;//MyButton
MyButton m_sendfile;//MyButton
BOOL m_check_hexrx;
BOOL m_check_hextx;
MyButton m_comcontrol;//MyButton
CComboBox m_comlist;
CString m_strFilePath;
CString m_strStatus;
CEdit m_CEditStatus;
CString m_strTimer;
CString m_strOut;
CStatic m_ctrlIcon;
DWORD HandSendNum;
CRect m_rect;
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
afx_msg void OnComcontrol();
afx_msg void OnClose();
afx_msg void OnBtnHandsend();
afx_msg void OnBtnClrrx();
afx_msg void OnBtnClrtx();
afx_msg void OnTimer(UINT_PTR nIDEvent);
afx_msg void OnBtnAutosend();
afx_msg void OnCheckHexrx();
afx_msg void OnCheckHextx();
afx_msg void OnBtnSelctfile();
afx_msg void OnBtnSendfile();
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
afx_msg void ChangeSize(CWnd* pWnd, int cx, int cy, int deviate);
//afx_msg void OnCbnSelchangeBaud();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnCbnSelchangeComlist();
};
4.2.5 在commassistDlg.cpp中替换为如下代码
// commassistDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "commassist.h"
#include "commassistDlg.h"
#include "afxdialogex.h"
#include "comm.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CString strIn;
CString strOut;
CString m_strFile;
// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV 支持
// 实现
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange *pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CcommassistDlg 对话框
CcommassistDlg::CcommassistDlg(CWnd *pParent /*=nullptr*/)
: CDialogEx(IDD_COMMASSIST_DIALOG, pParent)
, m_check_hexrx(FALSE)
, m_check_hextx(FALSE)
, m_strFilePath(_T(""))
, m_strStatus(_T(""))
, m_strTimer(_T(""))
, m_strOut(_T(""))
{
//下面就是自己添加的变量初始化
m_strTimer = "1000";
m_strFilePath = "请选择要发送的文件";
m_intTxCnt = 0;
m_intRxCnt = 0;
m_bAutoSend = 0;
strIn = "";
strOut = "";
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CcommassistDlg::DoDataExchange(CDataExchange *pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BAUD, m_baud);
DDX_Control(pDX, IDC_BDATA, m_bdata);
DDX_Control(pDX, IDC_BSTOP, m_bstop);
DDX_Control(pDX, IDC_BTN_AUTOSEND, m_autosend);
DDX_Control(pDX, IDC_BTN_CLRRX, m_clrrx);
DDX_Control(pDX, IDC_BTN_CLRTX, m_clrtx);
DDX_Control(pDX, IDC_BTN_HANDSEND, m_handsend);
DDX_Control(pDX, IDC_BTN_SELCTFILE, m_selfile);
DDX_Control(pDX, IDC_BTN_SENDFILE, m_sendfile);
DDX_Control(pDX, IDC_CAL, m_cal);
DDX_Check(pDX, IDC_CHECK_HEXRX, m_check_hexrx);
DDX_Check(pDX, IDC_CHECK_HEXTX, m_check_hextx);
DDX_Control(pDX, IDC_COMCONTROL, m_comcontrol);
DDX_Control(pDX, IDC_COMLIST, m_comlist);
DDX_Text(pDX, IDC_EDIT_FILEPATH, m_strFilePath);
DDX_Text(pDX, IDC_EDIT_STATUS, m_strStatus);
DDX_Control(pDX, IDC_EDIT_STATUS, m_CEditStatus);
DDX_Text(pDX, IDC_EDIT_TIMER, m_strTimer);
DDX_Text(pDX, IDC_EDIT_TX, m_strOut);
DDX_Control(pDX, IDC_STATIC_ICON, m_ctrlIcon);
}
BEGIN_MESSAGE_MAP(CcommassistDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_COMCONTROL, &CcommassistDlg::OnComcontrol)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_BTN_HANDSEND, &CcommassistDlg::OnBtnHandsend)
ON_BN_CLICKED(IDC_BTN_CLRRX, &CcommassistDlg::OnBtnClrrx)
ON_BN_CLICKED(IDC_BTN_CLRTX, &CcommassistDlg::OnBtnClrtx)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_BTN_AUTOSEND, &CcommassistDlg::OnBtnAutosend)
ON_BN_CLICKED(IDC_CHECK_HEXRX, &CcommassistDlg::OnCheckHexrx)
ON_BN_CLICKED(IDC_CHECK_HEXTX, &CcommassistDlg::OnCheckHextx)
ON_BN_CLICKED(IDC_BTN_SELCTFILE, &CcommassistDlg::OnBtnSelctfile)
ON_BN_CLICKED(IDC_BTN_SENDFILE, &CcommassistDlg::OnBtnSendfile)
ON_WM_CTLCOLOR()/**/
//ON_CBN_SELCHANGE(IDC_BAUD, &CcommassistDlg::OnCbnSelchangeBaud)
ON_WM_SIZE()
END_MESSAGE_MAP()
// CcommassistDlg 消息处理程序
BOOL CcommassistDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu *pSysMenu = GetSystemMenu(FALSE);
if(pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if(!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
SetWindowText("MyCommassit--Mr Wang");
// TODO: 在此添加额外的初始化代码
//AfxGetApp()->SetWindowText("你要显示的东西如果不想显示置空就行");
m_comcontrol.SetForeColor(RGB(255, 0, 0));
FindComm(); //调用自动找串口函数
m_comlist.SetCurSel(0); //设置串口号下拉框默认值为第一个
m_baud.SetCurSel(6); //设置波特率下拉框默认值为9600
m_bdata.SetCurSel(3); //设置数据位下拉框默认值为8位
m_bstop.SetCurSel(0); //设置停止位下拉框默认值为1
m_cal.SetCurSel(0); //设置校验位下拉框默认值为None无
GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(false); //设置手动发送按钮不可用
GetDlgItem(IDC_BTN_AUTOSEND)->EnableWindow(false); //设置自动发送按钮不可用
GetDlgItem(IDC_EDIT_TIMER)->EnableWindow(false); //设置发送间隔按钮不可用
GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false); //设置选择文件按钮不可用
GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(false); //设置发送文件按钮不可用
ShowStatus();
//下面语句用于解决程序运行后初始化EDIT框内容被默认自动选中状态
//返回值需更改为FALSE
GetFocus(); //获取焦点
SetFocus(); //设置焦点
m_CEditStatus.SetSel(-1, -1, FALSE); //设置
// PostMessage(EM_SETSEL,-1,0);
return FALSE; // return TRUE unless you set the focus to a control return TRUE;
// 除非将焦点设置到控件,否则返回 TRUE
}
void CcommassistDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CcommassistDlg::OnPaint()
{
if(IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CcommassistDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
LRESULT CcommassistDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: 在此添加专用代码和/或调用基类
switch(message)
{
case WM_FOUNDCOMM:
{
//已找到串口,串口号以字符串形式由wParam传递
m_comlist.AddString((LPCTSTR)wParam); //用AddString添加一个字符串即COM号到m_comlist列表框中
break;
}
case WM_READCOMM:
{
//读串口消息
ReadComm();
this->SendDlgItemMessage(IDC_EDIT_RX, WM_VSCROLL, SB_BOTTOM, 0); //滚动条始终在底部
break;
}
}
return CDialogEx::WindowProc(message, wParam, lParam);
}
void CcommassistDlg::OnComcontrol()
{
// TODO: 在此添加控件通知处理程序代码
int nBaud, nData, nStop, nCal, nTemp;
CString sTemp, siTemp;
//波特率下拉框设置=================
nTemp = m_baud.GetCurSel();
switch(nTemp)
{
case 0:
nBaud = CBR_110;
break;
case 1:
nBaud = CBR_300;
break;
case 2:
nBaud = CBR_600;
break;
case 3:
nBaud = CBR_1200;
break;
case 4:
nBaud = CBR_2400;
break;
case 5:
nBaud = CBR_4800;
break;
case 6:
nBaud = CBR_9600;
break;
case 7:
nBaud = CBR_14400;
break;
case 8:
nBaud = CBR_19200;
break;
case 9:
nBaud = CBR_38400;
break;
case 10:
nBaud = CBR_56000;
break;
case 11:
nBaud = CBR_57600;
break;
case 12:
nBaud = CBR_115200;
break;
case 13:
nBaud = CBR_128000;
break;
case 14:
nBaud = CBR_256000;
break;
}
//数据位下拉框设置=================
nTemp = m_bdata.GetCurSel();
switch(nTemp)
{
case 0:
nData = 5;
break;
case 1:
nData = 6;
break;
case 2:
nData = 7;
break;
case 3:
nData = 8;
break;
}
//停止位下拉框设置=================
nTemp = m_bstop.GetCurSel();
switch(nTemp)
{
case 0:
nStop = ONESTOPBIT;
break;
case 1:
nStop = ONE5STOPBITS;
break;
case 2:
nStop = TWOSTOPBITS;
break;
}
//校验位下拉框设置=================
nTemp = m_cal.GetCurSel();
switch(nTemp)
{
case 0:
nCal = NOPARITY;
break;
case 1:
nCal = ODDPARITY;
break;
case 2:
nCal = EVENPARITY;
break;
case 3:
nCal = MARKPARITY;
break;
case 4:
nCal = SPACEPARITY;
break;
}
int commnum_buf;
commnum_buf = m_comlist.GetCurSel();
if(commnum_buf < 0)
{
MessageBox("获取串口错误", "错误", MB_ICONERROR);
ComIsOK = FALSE;
return;
}
m_comlist.GetLBText(commnum_buf, strcomname);
if(!ComIsOK)
{
OpenComm(nBaud, nData, nStop, nCal); //调用打开串口函数OpenComm()
if(ComIsOK)
pReceiveThread = AfxBeginThread(ThreadFunc, this, THREAD_PRIORITY_LOWEST);
//启动接收线程
ShowStatus();
if(!ComIsOK)
m_comcontrol.SetWindowText("打开串口");
else
{
m_comcontrol.SetText("关闭串口"); //按钮显示状态改变
m_comcontrol.SetForeColor(RGB(0, 155, 0)); //串口打开后文本颜色变绿
m_ctrlIcon.SetIcon((HICON)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON_OPEN), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0)); //显示打开icon
m_comlist.EnableWindow(false); //设置串口号下拉框不可用
m_baud.EnableWindow(false); //设置波特率下拉框不可用
m_bdata.EnableWindow(false); //设置数据位下拉框不可用
m_bstop.EnableWindow(false); //设置停止位下拉框不可用
m_cal.EnableWindow(false); //设置校验位下拉框不可用
GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(true); //设置手动发送按钮不可用
GetDlgItem(IDC_BTN_AUTOSEND)->EnableWindow(true); //设置自动发送按钮可用
GetDlgItem(IDC_EDIT_TIMER)->EnableWindow(true); //设置发送间隔按钮可用
GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(true); //设置选择文件按钮可用
GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(true); //设置发送文件按钮可用
}
return;
}
else
{
CloseComm(); //调用关闭串口函数CloseComm()
// TerminateThread(pReceiveThread,0);
ShowStatus();
m_comcontrol.SetText("打开串口");
m_comcontrol.SetForeColor(RGB(255, 0, 0));
m_ctrlIcon.SetIcon((HICON)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDI_ICON_CLOSE), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CXICON), 0)); //显示关闭icon
m_comlist.EnableWindow(true); //设置串口号下拉框可用
m_baud.EnableWindow(true); //设置波特率下拉框可用
m_bdata.EnableWindow(true); //设置数据位下拉框可用
m_bstop.EnableWindow(true); //设置停止位下拉框可用
m_cal.EnableWindow(true); //设置校验位下拉框可用
GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(false); //设置手动发送按钮不可用
GetDlgItem(IDC_BTN_AUTOSEND)->EnableWindow(false); //设置自动发送按钮不可用
GetDlgItem(IDC_EDIT_TIMER)->EnableWindow(false); //设置发送间隔按钮不可用
GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false); //设置选择文件按钮不可用
GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(false); //设置发送文件按钮不可用
return;
}
}
DWORD CcommassistDlg::ReadComm()
{
CString strTemp;
OVERLAPPED m_osRead;
memset(&m_osRead, 0, sizeof(OVERLAPPED));
m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
char lpInBuffer[1024];
DWORD dwBytesRead = 1024;
BOOL bReadStatus;
bReadStatus = ReadFile(hCom, lpInBuffer, dwBytesRead, &dwBytesRead, &m_osRead);
if(!bReadStatus) //如果ReadFile函数返回FALSE
{
if(GetLastError() == ERROR_IO_PENDING) //GetLastError()函数返回ERROR_IO_PENDING,表明串口正在进行读操作
{
WaitForSingleObject(m_osRead.hEvent, 2000); //使用WaitForSingleObject函数等待,直到读操作完成或延时已达到2000ms
//当串口读操作进行完毕后,m_osRead的hEvent事件会变为有信号
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
return dwBytesRead;
}
return 0;
}
lpInBuffer[dwBytesRead] = NULL;
strTemp = lpInBuffer;
m_intRxCnt += strTemp.GetLength(); //接收到字节数统计
// GetDlgItemText(IDC_EDIT_RX,strIn);
strIn += strTemp;
OnCheckHexrx();
ShowStatus();
return 1;
}
void CcommassistDlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
TerminateThread(pReceiveThread, 0); //程序退出时,关闭串口监听线程
WaitForSingleObject(pReceiveThread, INFINITE);
CDialogEx::OnClose();
}
void CcommassistDlg::OnBtnHandsend()
{
// TODO: 在此添加控件通知处理程序代码
if(ComIsOK == FALSE)
{
MessageBox("请先打开串口", "提示", MB_ICONINFORMATION);
return;//return 0;
}
BOOL bWriteStat;
UpdateData(TRUE);
CString str, sTemp;
DWORD dwBytesWritten = 1024;
OVERLAPPED m_osWrite;
memset(&m_osWrite, 0, sizeof(OVERLAPPED));
m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
COMSTAT ComStat;
DWORD dwErrorFlags;
// dwBytesWritten = OnCheckHextx();
GetDlgItem(IDC_EDIT_TX)->GetWindowText(strOut);
if(m_check_hextx)
{
int i, n;
CString strTemp;
CByteArray hexdata;
// GetDlgItem(IDC_EDIT_TX)-> GetWindowText(strOut);
dwBytesWritten = ConvertHexC2String(strOut, hexdata);
n = hexdata.GetSize();
for(i = 0; i < n; i++)
{
str.Format("%c", hexdata[i]);
strTemp += str;
}
// SetDlgItemText(IDC_EDIT_TX,strTemp);
strOut = strTemp;
}
else
{
GetDlgItem(IDC_EDIT_TX)->GetWindowText(str);
SetDlgItemText(IDC_EDIT_TX, "");
sTemp = DisplayHex2CString(str);
dwBytesWritten = str.GetLength();
SetDlgItemText(IDC_EDIT_TX, strOut);
}
UpdateData();
if(dwBytesWritten == 0)
{
MessageBox("请在发送区内输入要发送的内容", "提示", MB_ICONINFORMATION);
//HandSendNum = 0;//return 0;
return;
}
m_intTxCnt += dwBytesWritten;
ShowStatus();
ClearCommError(hCom, &dwErrorFlags, &ComStat);
bWriteStat = WriteFile(hCom, strOut, dwBytesWritten, &dwBytesWritten, &m_osWrite);
if(!bWriteStat)
{
if(GetLastError() == ERROR_IO_PENDING)
{
WaitForSingleObject(m_osWrite.hEvent, 1000);
HandSendNum = dwBytesWritten;//return dwBytesWritten;
}
//HandSendNum = 0;//return 0;
return;
}
ShowStatus();
PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
//HandSendNum= dwBytesWritten;//return dwBytesWritten;
return;
}
void CcommassistDlg::ShowStatus()
{
//状态栏显示状态
CString strTXcnt;
CString strRXcnt;
CString sTemp;
CString comnum;
CString strBaud, strStop, strData, strCal;
UpdateData(true);
if(ComIsOK)
{
m_comlist.GetLBText(m_comlist.GetCurSel(), sTemp);
comnum = sTemp + "已打开";
}
else
comnum = "未打开串口";
strTXcnt.Format("发送:%d", m_intTxCnt);
strRXcnt.Format("接收:%d", m_intRxCnt);
m_baud.GetLBText(m_baud.GetCurSel(), strBaud);
m_bstop.GetLBText(m_bstop.GetCurSel(), strStop);
m_bdata.GetLBText(m_bdata.GetCurSel(), strData);
m_cal.GetLBText(m_cal.GetCurSel(), strCal);
m_strStatus = "串口: " + comnum + " " + "状态: " + strTXcnt + ", " + strRXcnt + ", " + "波特率: " + strBaud +
", " + "数据位: " + strData + ", " + "停止位: " + strStop + ", " + "校验位: " + strCal;
UpdateData(FALSE);
}
void CcommassistDlg::OnBtnClrrx()
{
// TODO: 在此添加控件通知处理程序代码
GetDlgItem(IDC_EDIT_RX);
SetDlgItemText(IDC_EDIT_RX, "");
m_intRxCnt = 0;
m_intTxCnt = 0;
strIn = "";
ShowStatus();
}
void CcommassistDlg::OnBtnClrtx()
{
// TODO: 在此添加控件通知处理程序代码
GetDlgItem(IDC_EDIT_TX);
SetDlgItemText(IDC_EDIT_TX, "");
}
void CcommassistDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if(nIDEvent == 1)
OnBtnHandsend();
else if(nIDEvent == 2)
return;
CDialogEx::OnTimer(nIDEvent);
}
void CcommassistDlg::OnBtnAutosend()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
m_bAutoSend = !m_bAutoSend;// m_bAutoSend
if(!m_strOut.GetLength())
{
MessageBox("请先输入要发送的内容", "提示", MB_ICONINFORMATION);
m_bAutoSend = !m_bAutoSend;
}
else
{
if(m_bAutoSend)
{
SetTimer(1, atoi(m_strTimer.GetBuffer(m_strTimer.GetLength())), NULL); //设置定时
m_autosend.SetText("停止");
GetDlgItem(IDC_COMCONTROL)->EnableWindow(false);
GetDlgItem(IDC_BTN_CLRTX)->EnableWindow(false);
GetDlgItem(IDC_BTN_CLRRX)->EnableWindow(false);
GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(false);
GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false);
GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(false);
}
else
{
KillTimer(1);
m_autosend.SetText("自动发送");
GetDlgItem(IDC_COMCONTROL)->EnableWindow(true);
GetDlgItem(IDC_BTN_CLRTX)->EnableWindow(true);
GetDlgItem(IDC_BTN_CLRRX)->EnableWindow(true);
GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(true);
GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(true);
GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(true);
}
}
}
void CcommassistDlg::OnCheckHexrx()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
CString hexIn;
CString sTemp;
if(m_check_hexrx)
{
hexIn = DisplayCString2Hex(strIn, true);
GetDlgItem(IDC_EDIT_RX)->SetWindowText(hexIn);
//将hexIn内容放入IDC_EDIT_RX框内,即为显示转换
}
else
GetDlgItem(IDC_EDIT_RX)->SetWindowText(strIn);
//将strIn内容放入IDC_EDIT_RX框内,即为显示不转换
return;
}
void CcommassistDlg::OnCheckHextx()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();
CString hexOut;
CString str, sTemp;
GetDlgItem(IDC_EDIT_TX)->GetWindowText(strOut);
if(m_check_hextx)
{
str = DisplayCString2Hex(strOut, true);
strOut = str;
}
else
{
int i, n;
CString strTemp;
CByteArray hexdata;
ConvertHexC2String(strOut, hexdata);
n = hexdata.GetSize();
for(i = 0; i < n; i++)
{
str.Format("%c", hexdata[i]);
strTemp += str;
}
strOut = strTemp;
}
GetDlgItem(IDC_EDIT_TX)->SetWindowText(strOut);
//将strOut内容放入IDC_EDIT_TX框内,即为不转换
UpdateData();
return;
}
void CcommassistDlg::OnBtnSelctfile()
{
// TODO: 在此添加控件通知处理程序代码
CFile file;
m_strFile.Empty();
CFileDialog FileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "文本文件(*.txt)|*.txt||");
if(FileDlg.DoModal() == IDOK) //打开文件对话框
m_strFilePath = FileDlg.GetPathName(); //得到文件路经
else
return;
file.Open(m_strFilePath, CFile::modeRead | CFile::typeBinary);//打开这个文件
file.Read(m_strFile.GetBuffer(file.GetLength()), file.GetLength()); //读文件
m_strFile.ReleaseBuffer();
if(m_strFile.GetLength() >= 2048)
{
AfxMessageBox("文件的长度超过2k字节!", MB_ICONINFORMATION);
}
else
{
m_strOut += m_strFile; //文件内容加入发送框变量内
UpdateData(false); //更新发送框内容
}
file.Close();
}
void CcommassistDlg::OnBtnSendfile()
{
// TODO: 在此添加控件通知处理程序代码
COMSTAT state;
DWORD errors;
CString sTemp;
int iTemp;
ClearCommError(hCom, &errors, &state); //清除串口错误、得到当前状态
iTemp = m_strOut.GetLength(); //写入串口的字符串长度,由EDIT控件内字符串数决定
iTemp += iTemp;
OnBtnHandsend(); //调用发送函数
GetDlgItem(IDC_EDIT_FILEPATH)->SetDlgItemText(IDC_EDIT_FILEPATH, "");
UpdateData(false); //更新发送框内容;
}
HBRUSH CcommassistDlg::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
// TODO: 在此更改 DC 的任何特性
if(nCtlColor == CTLCOLOR_DLG) //所有对话框
{
HBRUSH brush = CreateSolidBrush(RGB(220, 250, 250));
return brush;
}
if(nCtlColor == CTLCOLOR_STATIC)
{
pDC->SetTextColor(RGB(50, 50, 50));
//pDC->SetBkColor(RGB(128,128,128));//设置文本背景色
//pDC->SetTextColor(RGB(55,55,66));
pDC->SetBkMode(TRANSPARENT);//设置背景透明
}
switch(pWnd->GetDlgCtrlID())
{
//针对ID为IDC_CTL1、IDC_CTL2和IDC_CTL3的控件进行同样的设置
case IDC_EDIT_RX:
{
//pDC->SetBkMode(TRANSPARENT);//背景色透明
//pDC->SetTextColor(RGB(250,0,0));// 设置字体颜色为红色
pDC->SetTextColor(RGB(0, 0, 255));// 设置字体颜色为 //255
pDC->SetBkColor(RGB(255, 255, 255)); // 改为背景颜色即可
hbr = CreateSolidBrush(RGB(255, 255, 255));//背景
//Invalidate(false);
break;
}
case IDC_EDIT_TX:
{
pDC->SetTextColor(RGB(0, 0, 255));// 设置字体颜色为 //255
pDC->SetBkColor(RGB(255, 255, 255)); // 改为背景颜色即可
hbr = CreateSolidBrush(RGB(255, 255, 255));//背景
break;
}
default:
break;
}
// TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
}
void CcommassistDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
// nType == 1不可以省略,否则由最小化恢复为正常状态下的时候会出错
//nType是一个枚举类型,主要是指定所请求的不同的调整大小。这个参数可以是下列值之一:SIZE_MAXIMIZED 、SIZE_MINIMIZED 。SIZE_RESTORED , SIZE_MAXHIDE ,SIZE_MAXSHOW 其原型可以在msdn上查看
if(nType == 1) return; //最小化则什么都不做
CWnd *pWnd;
pWnd = GetDlgItem(IDC_EDIT_RX); //获取控件句柄
ChangeSize(pWnd, cx, cy, 20); //调用changesize()函数
pWnd = GetDlgItem(IDC_STATIC_RX); //获取控件句柄
ChangeSize(pWnd, cx, cy, 10);//调用changesize()函数
pWnd = GetDlgItem(IDC_EDIT_TX); //获取控件句柄
ChangeSize(pWnd, cx, cy, 20); //调用changesize()函数
pWnd = GetDlgItem(IDC_STATIC_TX); //获取控件句柄
ChangeSize(pWnd, cx, cy, 10);//调用changesize()函数
GetClientRect(&m_rect); //将变化后的对话框设置为旧大小
}
void CcommassistDlg::ChangeSize(CWnd *pWnd, int cx, int cy, int deviate)
{
if(pWnd)
{
CRect rect;
pWnd->GetWindowRect(&rect); //获取控件变化前的大小
ScreenToClient(&rect);//将控件大小转换为在对话框中的区域坐标
rect.right = cx - deviate;
pWnd->MoveWindow(rect);//设置控件大小
}
}
5 总结
至此,串口调试工具已经完成设计。