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



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

总结

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

### 回答1: 基于MFC串口调试代码vs2019.zip是一个用于串口通信调试的代码示例,适用于使用MFC框架进行程序开发开发人员。该代码示例包含了VS2019版本的项目文件,方便在该开发环境下进行调试和开发。 该代码示例主要包括以下几个部分的功能: 1. 串口通信的初始化:通过该代码示例,可以方便地进行串口的初始化操作,包括设置串口号、波特率、数据位、停止位、奇偶校验等参数。通过调用相应的函数,可以轻松地实现串口的打开、关闭等操作。 2. 数据的发送和接收:该代码示例提供了发送和接收数据的函数,可以实现从串口发送数据和接收数据的功能。通过调用相应的函数,可以将需要发送的数据写入到串口缓冲区,并通过串口发送出去;同时也可以接收从串口发送过来的数据,并将其读取到接收缓冲区进行处理。 3. 数据的显示和保存:该代码示例还提供了数据的显示和保存功能。通过调用相应的函数,可以实现将接收到的数据在界面上进行显示;同时也可以将接收到的数据保存到本地文件中,以便后续的分析和处理。 4. 错误处理和异常处理:该代码示例还包含了错误处理和异常处理的功能,可以检测串口通信过程中可能出现的错误,并进行相应的处理。 总之,基于MFC串口调试代码vs2019.zip提供了一个基础的串口通信调试框架,方便开发人员进行相关的串口调试工作。通过借鉴和使用该代码示例,开发人员可以更加便捷地完成串口通信的开发和调试工作。 ### 回答2: 基于MFC串口调试代码VS2019.zip是一个包含了使用MFC进行串口通信的示例代码的压缩文件。MFC是Microsoft Foundation Classes的缩写,是微软公司提供的Windows应用程序开发框架,可以方便地创建图形用户界面。 该示例代码涉及串口通信,通过串口与其他设备进行数据传输。串口通信是一种常见的设备间通信方式,被广泛应用于各种硬件设备的控制和数据交换。该示例使用了MFC提供的串口通信相关类,实现了串口的打开、关闭、发数据、收数据等基本操作。 通过该示例代码,可以学习到如何使用MFC进行串口通信的开发。代码中包含了串口类的封装,封装了串口的一些常用操作,并提供了相应的界面,使用户可以方便地进行串口调试和数据的发送与接收。 该示例代码的使用方法为先解压缩文件,然后打开VS2019开发环境,载入解压后的工程文件。接下来可以通过运行工程,在界面中选择串口号、波特率等参数,点击打开串口按钮,连接到指定的串口设备。在成功打开串口后,可以在界面中输入需要发送的数据,并点击发送按钮进行数据的发送。同时,可以点击接收按钮,实时显示接收到的数据。 通过该示例代码可以学习到使用MFC进行串口通信的基本方法,可以作为开发其他串口通信应用的参考。同时,也可以进行一些串口调试工作,对串口设备进行功能测试和故障排查。 总之,基于MFC串口调试代码VS2019.zip是一个能够帮助开发者学习串口通信开发和进行串口调试的示例代码文件。 ### 回答3: 基于MFC串口调试代码VS2019.zip是一个使用MFC(Microsoft Foundation Class)框架开发的用于串口调试的代码压缩文件。在该文件中,包含了一套用于创建串口调试程序的源代码和相关资源文件。 MFC是微软提供的一种C++类库,用于简化Windows应用程序的开发。通过使用MFC开发者可以更快速地构建Windows界面和处理用户交互。因此,基于MFC串口调试代码VS2019.zip提供了一个基于Windows操作系统的串口调试工具开发基础。 使用该代码,我们可以轻松地进行串口通信的设置、数据发送和接收。其主要功能包括打开和关闭串口、设置波特率、数据位、停止位和校验位等串口参数。同时,我们可以通过该代码向串口发送数据并接收串口返回的数据,实现串口数据的发送和接收。 此外,基于MFC串口调试代码VS2019.zip还提供了一套用户友好的界面,方便用户进行操作和展示串口通信的结果。通过这个界面,我们可以清晰地看到串口的状态、发送的数据和接收到的数据。 总结而言,基于MFC串口调试代码VS2019.zip提供了一个快速开发串口调试程序的基础,方便开发者进行串口通信的调试和测试。通过该代码,我们可以方便地设置串口参数,发送和接收数据,并通过用户友好的界面来展示相关结果。这对于需要进行串口通信的应用开发者来说,是一个非常有价值的工具。
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值