基于VS2019的串口调试工具开发

MFC学习 专栏收录该内容
1 篇文章 0 订阅


 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.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 Control控件

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.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);//设置控件大小
    }
}

总结

         至此,串口调试工具已经完成设计。

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值