欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【MFC 实战项目】No.1 串口调试助手

程序员文章站 2022-05-23 12:26:19
...

                                                 前  言

基于MFC对话框开发了一款串口调试助手,基础功能已经实现了,还有一些扩展功能没实现。文中代码注释已经很丰富,这里不做过多讲解了。

VS版本:

Microsoft Visual Studio 2008 9.0.30729.1 SP

 

                                                 目  录

1. 软件成果展示:

2.  源代码:

(1)头文件:SerialPortDlg.h

(2)源文件:SerialPortDlg.cpp

3. 说明:

4. 项目下载地址:


                                       1.  软件成果展示

        这里展示了与著名的铭心软体工作室出品的串口调试助手(V 3.7.2)进行通信,串口1和2是VSPD软件添加的虚拟串口。项目下载地址附文末,这里附上上述两个软件的下载地址:

                                              串口调试助手(CM精装版 V3.7.2)                  VSPD 6.9下载

【MFC 实战项目】No.1 串口调试助手图1  串口调试助手 V1.0成果展示

 

                                            2.  源代码:

(1)头文件:SerialPortDlg.h


// SerialPortDlg.h : 头文件
#ifndef SERIALPORTDLG_H
#define SERIALPORTDLG_H

#pragma once
#include "afxwin.h"
#include "BasCommPort.h"

// CSerialPortDlg 对话框
class CSerialPortDlg : public CDialog
{
// 构造
public:
	CSerialPortDlg(CWnd* pParent = NULL);	// 标准构造函数

// 对话框数据
	enum { IDD = IDD_SERIALPORT_DIALOG };

	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:
	/* 初始化菜单、控件 */
	void InitMenu();			// 初始化菜单栏
	void InitSerialSetting();	// 初始化串口设置
	void InitReciveSetting();	// 初始化接收区设置
	void InitSendSetting();		// 初始化发送区设置

	/* 其它辅助函数 */
	void FindCommPort(CComboBox *pComboBox);		// 查找计算机可用串口并将之设置到Combo Box控件上
	void SetButtonState(BOOL bOpen, UINT ID_ICON);	// 设置<打开/断开>按钮的文本
	void SetSerialStateStatus(BOOL bOpen = TRUE);	// 设置状态栏串口状态
	void SetSendNumStatus();						// 设置状态栏发送字节数
	void SetRecvNumStatus();						// 设置状态栏接收字节数
	long GetCommPortN();							// 获取Combo Box上的串口编号
	CString GetCommPortParameter();					// 获取串口参数
	void SetRecvData(CString strData);				// 设置接收区内容

	/* 文件IO */
	CString GetFilePathFromFileDlg(BOOL bOpenFileDlg, LPCTSTR lpszFileName);	// 从文件对话框创建文件,返回文件路径+文件名
	void SaveRecvData(const char *szFilePath, const char *szData);				// 保存接收区文件 
	CString ReadFileData(const char *szFilePath);								// 读取文件信息,返回读到的数据

	/* 基类成员函数重写 */
	afx_msg void OnClose();							// 串口关闭之前的处理

public:
	// 串口设置
	CComboBox m_cb_serNum;			// 串口号
	CComboBox m_cb_btRate;			// 波波特率
	CComboBox m_cb_ckBit;			// 校验位
	CComboBox m_cb_datBit;			// 数据位
	CComboBox m_cb_stpBit;			// 停止位
	CButton m_bt_onOff;				// 打开/关闭按钮
	CStatic m_picCtrl_OnOffFlag;	// 图片控件,标识串口状态
	afx_msg void OnBnClickedBtOnoff();	// 打开/断开串口

	// 发送区
	CButton m_ck_send1;		// 启用文件数据源
	CButton m_ck_send2;		// 自动发送附加位
	CButton m_ck_send3;		// 发送完自动清空
	CButton m_ck_send4;		// 按十六进制发送
	CButton m_ck_send5;		// 数据流循环发送
	CEdit m_et_time;		// 发送间隔
	CEdit m_et_dataSend;	// 数据输入框				
	afx_msg void OnBnClickedCkSend1();		// 启用文件数据源
	afx_msg void OnBnClickedBtLoadfile();	// 文件载入
	afx_msg void OnBnClickedBtClsend();		// 清除输入
	afx_msg void OnBnClickedBtSenddata();	// 发送数据

	// 接收区
	CButton m_ck_recv1;		// 接收转向至文件
	CButton m_ck_recv2;		// 自动换行显示
	CButton m_ck_recv3;		// 十六进制显示
	CButton m_ck_recv4;		// 暂停接收显示
	CEdit m_et_dataRecv;	// 串口数据接收框
	afx_msg void OnRecvAutoToFile();		// 接收转向至文件
	afx_msg void OnBnClickedBtSavedata();   // 保存数据
	afx_msg void OnBnClickedBtClrecv();		// 清除显示

	// 菜单栏
	afx_msg void OnMenuOnoff();	// 打开/断开
	afx_msg void OnMenuAbout();	// 关于
	afx_msg void OnMenuExit();	// 退出	

	// 模拟状态栏 Static Edit
	CStatic m_st_serState;		// 串口状态
	CStatic m_st_sendNum;		// 发送字节数
	CStatic m_st_recvNum;		// 接收字节数
	afx_msg void OnBnClickedBtClearnum();	// 复位按钮

public:
	CMenu m_menu;						// 菜单栏
	CBasCommPort m_CommPort;			// 对MSCOmm控件的封装
	ULONG m_nSendNum;					// 发送字节数
	ULONG m_nRecvNum;					// 接收字节数
	BOOL m_bIsOpen;						// 打开/关闭标识符
	CString m_strAutoSaveFilePath;		// 自动保存至文件时,传递的文件路径
	CString m_strFileDatSrcFilePath;	// 启用文件数据源时,传递的文件路径
	
}; //class CSerialPortDlg

#endif //SERIALPORTDLG_H

 

(2)源文件:SerialPortDlg.cpp


// SerialPortDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "SerialPort.h"
#include "SerialPortDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// 回调函数
void SetRecv(RecivedCallback pCallBack, void *pUserData);
//void CALLBACK EXPORT TimerProc(HWND hWnd, UINT nMsg, UINT nTimerId, DWORD dwTime);

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// 对话框数据
	enum { IDD = IDD_ABOUTBOX };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CSerialPortDlg 对话框



CSerialPortDlg::CSerialPortDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSerialPortDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSerialPortDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);

	DDX_Control(pDX, IDC_CB_SERNUM, m_cb_serNum);
	DDX_Control(pDX, IDC_CB_BOTRAT, m_cb_btRate);
	DDX_Control(pDX, IDC_CB_CKBT, m_cb_ckBit);
	DDX_Control(pDX, IDC_CB_DATBT, m_cb_datBit);
	DDX_Control(pDX, IDC_CB_STPBT, m_cb_stpBit);
	DDX_Control(pDX, IDC_BT_ONOFF, m_bt_onOff);
	DDX_Control(pDX, IDC_ET_TIME, m_et_time);
	DDX_Control(pDX, IDC_CK_RECV2, m_ck_recv2);
	DDX_Control(pDX, IDC_CK_RECV1, m_ck_recv1);
	DDX_Control(pDX, IDC_CK_RECV3, m_ck_recv3);
	DDX_Control(pDX, IDC_CK_RECV4, m_ck_recv4);
	DDX_Control(pDX, IDC_CK_SEND1, m_ck_send1);
	DDX_Control(pDX, IDC_CK_SEND2, m_ck_send2);
	DDX_Control(pDX, IDC_CK_SEND3, m_ck_send3);
	DDX_Control(pDX, IDC_CK_SEND4, m_ck_send4);
	DDX_Control(pDX, IDC_CK_SEND5, m_ck_send5);
	DDX_Control(pDX, IDC_ET_DATARECV, m_et_dataRecv);
	DDX_Control(pDX, IDC_ET_DATASEND, m_et_dataSend);
	DDX_Control(pDX, IDC_STATIC_PIC, m_picCtrl_OnOffFlag);
	DDX_Control(pDX, IDC_ST_SERSTATE, m_st_serState);
	DDX_Control(pDX, IDC_ST_SENDNUM, m_st_sendNum);
	DDX_Control(pDX, IDC_ST_RECVNUM, m_st_recvNum);
}

BEGIN_MESSAGE_MAP(CSerialPortDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_BT_SENDDATA, &CSerialPortDlg::OnBnClickedBtSenddata)
	ON_BN_CLICKED(IDC_BT_ONOFF, &CSerialPortDlg::OnBnClickedBtOnoff)
	ON_COMMAND(ID_MENU_EXIT, &CSerialPortDlg::OnMenuExit)
	ON_BN_CLICKED(IDC_BT_CLEARNUM, &CSerialPortDlg::OnBnClickedBtClearnum)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BT_SAVEDATA, &CSerialPortDlg::OnBnClickedBtSavedata)
	ON_BN_CLICKED(IDC_BT_CLRECV, &CSerialPortDlg::OnBnClickedBtClrecv)
	ON_BN_CLICKED(IDC_BT_LOADFILE, &CSerialPortDlg::OnBnClickedBtLoadfile)
	ON_BN_CLICKED(IDC_BT_CLSEND, &CSerialPortDlg::OnBnClickedBtClsend)
	ON_COMMAND(ID_ABOUT, &CSerialPortDlg::OnMenuAbout)
	ON_BN_CLICKED(IDC_CK_RECV1, &CSerialPortDlg::OnRecvAutoToFile)
	ON_COMMAND(ID_MENU_ONOFF, &CSerialPortDlg::OnMenuOnoff)
	ON_BN_CLICKED(IDC_CK_SEND1, &CSerialPortDlg::OnBnClickedCkSend1)
END_MESSAGE_MAP()


// CSerialPortDlg 消息处理程序

BOOL CSerialPortDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		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);		// 设置小图标

	/************************************************************************/
	/*                         添加的初始化变量/函数                    */
	/************************************************************************/

	m_nSendNum = 0;
	m_nRecvNum = 0;
	m_bIsOpen = FALSE;

	this->InitMenu();
	this->InitSerialSetting();
	this->InitReciveSetting();
	this->InitSendSetting();
	
	m_CommPort.SetRecCallBack(RecivedCallback(SetRecv), this);

	/************************************************************************/
	/*                               end                                    */
	/************************************************************************/
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CSerialPortDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CSerialPortDlg::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
	{
		CDialog::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CSerialPortDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

/************************************************************************/
/*                          回调函数                                */
/************************************************************************/

// 回调函数
void SetRecv(RecivedCallback pCallBack, void *pUserData)
{
	CSerialPortDlg *pDlg = NULL;
	pDlg = (CSerialPortDlg*)pUserData;

	// 将串口缓冲区数据读入buff
	char buff[1024] = {0};
	pDlg->m_CommPort.Read(buff, sizeof(buff));
	pDlg->m_CommPort.ClearInputBuffer(); // 清除接收缓冲区

	// 设置接收区内容
	CString strDataRecv;
	strDataRecv.Format("%s", buff);
	pDlg->SetRecvData(strDataRecv);

	// 设置接收字节数
	pDlg->m_nRecvNum += strDataRecv.GetLength();
	pDlg->SetRecvNumStatus();
}

/************************************************************************/
/*                          初始化菜单、控件                       */
/************************************************************************/

// 菜单栏设置
void CSerialPortDlg::InitMenu()
{
	m_menu.LoadMenu( IDR_MENU );
	this->SetMenu(&m_menu);
}

// 串口设置
void CSerialPortDlg::InitSerialSetting()
{
	// 串口号
	FindCommPort(&m_cb_serNum);
	for (int i = 1; i < 25; i++)
	{
		CString strNum;
		strNum.Format("%d", i);

		m_cb_serNum.AddString(_T("COM") + strNum);
	}
	m_cb_serNum.SetCurSel(0);

	// 波特率
	m_cb_btRate.AddString(_T("110"));
	m_cb_btRate.AddString(_T("300"));
	m_cb_btRate.AddString(_T("600"));
	m_cb_btRate.AddString(_T("1200"));
	m_cb_btRate.AddString(_T("2400"));
	m_cb_btRate.AddString(_T("4800"));
	m_cb_btRate.AddString(_T("9600"));
	m_cb_btRate.AddString(_T("14400"));
	m_cb_btRate.AddString(_T("19200"));
	m_cb_btRate.AddString(_T("38400"));
	m_cb_btRate.AddString(_T("56000"));
	m_cb_btRate.AddString(_T("57600"));
	m_cb_btRate.AddString(_T("115200"));
	m_cb_btRate.AddString(_T("128000"));
	m_cb_btRate.AddString(_T("256000"));
	m_cb_btRate.SetCurSel(6);

	// 校验位
	m_cb_ckBit.AddString(_T("NONE"));
	m_cb_ckBit.AddString(_T("ODD"));
	m_cb_ckBit.AddString(_T("EVEN"));
	m_cb_ckBit.AddString(_T("MARK"));
	m_cb_ckBit.AddString(_T("SPACE"));
	m_cb_ckBit.SetCurSel(0);

	// 数据位
	for (int i = 5; i <=8 ; i++)
	{
		CString strBit;
		strBit.Format("%d", i);
		m_cb_datBit.AddString(strBit);
	}
	m_cb_datBit.SetCurSel(3);

	// 停止位
	m_cb_stpBit.AddString(_T("1"));
	m_cb_stpBit.AddString(_T("1.5"));
	m_cb_stpBit.AddString(_T("2"));
	m_cb_stpBit.SetCurSel(0);
}

// 接收区设置
void CSerialPortDlg::InitReciveSetting()
{
	m_ck_recv2.SetCheck(FALSE);
}

// 发送区设置
void CSerialPortDlg::InitSendSetting()
{
	m_et_time.SetWindowText(_T("1000"));
	m_ck_send3.SetCheck(TRUE);
}

/************************************************************************/
/*                          其它辅助函数                            */
/************************************************************************/

// 查找计算机可用串口并将串口号设置到Combo Box控件上
void CSerialPortDlg::FindCommPort( CComboBox *pComboBox )
{
	HKEY hKey;

#ifdef _DEBUG
	ASSERT( pComboBox != NULL );
	pComboBox->AssertValid();
#endif

	LONG nRetVal = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
		"Hardware\\DeviceMap\\SerialComm", NULL, 
		KEY_READ, &hKey);
	if ( nRetVal == ERROR_SUCCESS )
	{
		int i = 0;
		char portName[256], commName[256];
		DWORD dwLong, dwSize;
		while ( 1 )
		{
			dwLong = dwSize = sizeof(portName);
			nRetVal = RegEnumValue(hKey, i, portName, &dwLong, NULL, NULL, (PUCHAR)commName, &dwSize);
			if( nRetVal == ERROR_NO_MORE_ITEMS ) // 枚举串口
				break;

			CString strCommName;
			strCommName.Format("%s", commName);
			pComboBox->AddString( strCommName ); // commName:串口名字
			i++;
		}
		if( pComboBox->GetCount() == 0 )
		{
			//AfxMessageBox("HKEY_LOCAL_MACHINE:Hardware\\DeviceMap\\SerialComm里找不到串口!!!" );
		}
		RegCloseKey(hKey);
	}
}

// 设置状态栏串口状态
void CSerialPortDlg::SetButtonState(BOOL bOpen, UINT ID_ICON)
{
	HICON hIcon;
	hIcon = AfxGetApp()->LoadIcon( ID_ICON );
	m_picCtrl_OnOffFlag.SetIcon(hIcon);

	if ( bOpen )
		m_bt_onOff.SetWindowText(_T("断开(&O)"));
	else
		m_bt_onOff.SetWindowText(_T("打开(&O)"));
}

// 设置发送字节数
void CSerialPortDlg::SetSendNumStatus()
{
	CString strNum;
	strNum.Format("%d", m_nSendNum);
	m_st_sendNum.SetWindowText(_T("发送字节数:") + strNum);
}

// 设置状态栏串口状态
void CSerialPortDlg::SetSerialStateStatus(BOOL bOpen)
{
	if ( bOpen )
		m_st_serState.SetWindowText(_T("串口状态:打开"));
	else
		m_st_serState.SetWindowText(_T("串口状态:关闭"));
}

// 设置接收字节数
void CSerialPortDlg::SetRecvNumStatus()
{
	CString strNum;
	strNum.Format("%d", m_nRecvNum);
	m_st_recvNum.SetWindowText(_T("接收字节数:") + strNum);
}

// 获取Combo Box控件上的COM数字
long CSerialPortDlg::GetCommPortN()
{
	CString strCommPort;
	m_cb_serNum.GetWindowText(strCommPort);
	strCommPort = strCommPort.Mid(3);
	return _ttol(strCommPort);
}

// 获取串口参数
CString CSerialPortDlg::GetCommPortParameter()
{
	CString strParameter = "";
	CString strBaudRate;	// 波特率
	CString strDataBit;		// 数据位:5, 6, 7, 8
	CString strCheckBit;	// 校验位:NONE, ODD, EVEN, MARK, SPACE
	CString strStopBit;		// 停止位:1, 1.5, 2

	m_cb_btRate.GetWindowText(strBaudRate);
	m_cb_datBit.GetWindowText(strDataBit);
	m_cb_ckBit.GetWindowText(strCheckBit);
	m_cb_stpBit.GetWindowText(strStopBit);
	strCheckBit = strCheckBit.Left(1);
	strCheckBit.MakeLower();

	strParameter += strBaudRate;
	strParameter += _T(",");
	strParameter += strDataBit;
	strParameter += _T(",");
	strParameter += strCheckBit;
	strParameter += _T(",");
	strParameter += strStopBit;

	return strParameter;
}

// 将接收到的内容设置到接收区控件上
void CSerialPortDlg::SetRecvData(CString strData)
{
	// 接收转向至文件
	if ( m_ck_recv1.GetCheck() == BST_CHECKED )
	{
		char *szFilePath = m_strAutoSaveFilePath.GetBuffer(0);
		m_strAutoSaveFilePath.ReleaseBuffer();

		char *szData = strData.GetBuffer(0);
		strData.ReleaseBuffer();

		SaveRecvData(szFilePath, szData);
		return;
	}

	CString strDataRecvOld;
	m_et_dataRecv.GetWindowText(strDataRecvOld);

	strDataRecvOld += strData;
	// 自动换行显示
	if ( m_ck_recv2.GetCheck() && m_CommPort.GetInputSize() == 0)
		strDataRecvOld += _T("\r\n");

	// 暂停接收显示
	if ( !m_ck_recv4.GetCheck() )
		m_et_dataRecv.SetWindowText(strDataRecvOld);

	// 设置滚动条一直在底部
	int nLineCount = m_et_dataRecv.GetLineCount();
	m_et_dataRecv.LineScroll(nLineCount - 1);
}

/************************************************************************/
/*                          文件IO                                      */
/************************************************************************/

// 从文件对话框获取文件名
CString CSerialPortDlg::GetFilePathFromFileDlg(BOOL bOpenFileDlg, LPCTSTR lpszFileName)
{
	char szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");
	CFileDialog fileDlg( bOpenFileDlg, _T("txt"), lpszFileName, 
		OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
		szFilter );

	CString strFilePath = _T("");
	if ( IDOK == fileDlg.DoModal() )
		strFilePath = fileDlg.GetPathName();

	return strFilePath;
}

// 保存文件
void CSerialPortDlg::SaveRecvData(const char *szFilePath, const char *szData)
{
	if (szFilePath == NULL)
	{
		MessageBox(_T("文件路径有误,请重新选择!"),
					_T("Error"),
					MB_ICONERROR | MB_OK);
		return;
	}

	CFile fileOpr;
	BOOL bOpen = fileOpr.Open( szFilePath, CFile::shareDenyNone 
		| CFile::modeWrite 
		| CFile::modeCreate 
		| CFile::modeNoTruncate   /* 文件存在,则不清空 */
		/*| CFile::typeBinary*/ );
	if ( bOpen )
	{
		fileOpr.SeekToEnd();
		fileOpr.Write(szData, strlen(szData));
		fileOpr.SeekToBegin();
		fileOpr.Close();
	}
	else
		MessageBox(_T("文件打开失败!"), _T("Error!"), MB_ICONERROR | MB_OK);
}

// 读取文件信息
CString CSerialPortDlg::ReadFileData(const char *szFilePath)
{
	CString strDataRet = _T("");
	if (szFilePath == NULL)
		return strDataRet;

	CFile fileOpr;
	BOOL bOpen = fileOpr.Open( szFilePath, CFile::shareDenyNone 
		| CFile::modeRead 
		| CFile::modeCreate 
		| CFile::modeNoTruncate  /* 文件存在,则不清空 */
		/*| CFile::typeBinary*/ ); 

	if ( bOpen ) // CFile::Open的返回值
	{
		ULONG nLen = (ULONG)fileOpr.GetLength();
		if (nLen < 1)
			return strDataRet;
		char *buff = new char[nLen+1];
		memset(buff, 0, nLen);
		fileOpr.Read(buff, nLen);
		buff[nLen] = '\0';
		strDataRet.Format("%s", buff);

		delete []buff;
		buff = NULL;
	}

	return strDataRet;
}

/************************************************************************/
/*                        基类成员函数重写                         */
/************************************************************************/

// 重写OnClose()函数,窗口关闭时关闭串口,防止一直占用
void CSerialPortDlg::OnClose()
{
	if ( m_CommPort.IsOpen() )
		m_CommPort.Close();

	CDialog::OnClose();
}

/************************************************************************/
/*                          串口设置                                */
/************************************************************************/

// 打开/关闭按钮
void CSerialPortDlg::OnBnClickedBtOnoff()
{
	/* 关闭串口 */
	if ( m_bIsOpen )
	{
		m_CommPort.Close();
		if ( !m_CommPort.IsOpen() ) // 已关闭
		{
			SetButtonState( FALSE, IDI_ICONOFF );
			SetSerialStateStatus(FALSE);

			CMenu *subMenu = AfxGetMainWnd()->GetMenu()->GetSubMenu(0); // 0:第一列菜单
			subMenu->ModifyMenu(0, MF_BYPOSITION, ID_MENU_ONOFF, _T("打开(&O)")); // 0:第一列菜单下第1个子菜单

			m_bIsOpen = FALSE;
		}
		else
		{
			MessageBox(_T("断开连接错误!"), _T("Error!"), MB_ICONERROR | MB_OK);
		}

		return;
	}//if

	/* 打开串口 */
	DWORD nCommPort;		// 串口号, ULONG型
	nCommPort = (unsigned)GetCommPortN();

	// 获取串口参数pszPara
	CString strPara = GetCommPortParameter();
	if (strPara.GetLength() <= 0)
		MessageBox(_T("参数获取错误!"), _T("Error"), MB_ICONERROR | MB_OK);
	char *pszPara = strPara.GetBuffer(0);
	strPara.ReleaseBuffer();

	m_CommPort.SetInitParam();	// 设置初始化参数
	if ( pszPara != NULL )
		m_CommPort.Open(nCommPort, pszPara); // 打开串口

	if ( m_CommPort.IsOpen() )
	{
		SetButtonState( TRUE, IDI_ICONON );
		SetSerialStateStatus(TRUE);

		CMenu *subMenu = AfxGetMainWnd()->GetMenu()->GetSubMenu(0); // 第一列菜单
		subMenu->ModifyMenu(0, MF_BYPOSITION, ID_MENU_ONOFF, _T("断开(&O)"));

		m_bIsOpen = TRUE;
	}
	else
	{
		MessageBox(_T("串口不存在或者被其他应用程序占用!"), _T("Information"), MB_ICONINFORMATION | MB_OK);
		CString strMsg;
		m_et_dataRecv.GetWindowText(strMsg);
		strMsg += _T("【ERROR】Cannot open COM port\r\n");
		m_et_dataRecv.SetWindowText(strMsg);
	}//else
}

/************************************************************************/
/*                           发送区                                 */
/************************************************************************/

// 启用文件数据源
void CSerialPortDlg::OnBnClickedCkSend1()
{
	if ( m_ck_send1.GetCheck() == BST_CHECKED )
	{
		CString strFilePath = GetFilePathFromFileDlg(TRUE, NULL);
		if (strFilePath == _T(""))
		{
			m_ck_send1.SetCheck(FALSE);
			return;
		}

		m_strFileDatSrcFilePath = strFilePath;

		char *szFilePath = strFilePath.GetBuffer(0);
		strFilePath.ReleaseBuffer();

		CString strShowSendEdStatic;
		strShowSendEdStatic = _T("启用外部文件数据源:\r\n");
		strShowSendEdStatic += strFilePath;
		m_et_dataSend.SetWindowText(strShowSendEdStatic);
		m_et_dataSend.SetReadOnly(TRUE);
	}
	else
	{
		OnBnClickedBtClsend(); // 清空发送区内容
		m_et_dataSend.SetReadOnly(FALSE);
	}
}

// 文件载入按钮
void CSerialPortDlg::OnBnClickedBtLoadfile()
{
	if ( m_ck_send1.GetCheck() == BST_CHECKED )
	{
		MessageBox(_T("启用外部数据源时,无法执行该操作!"), _T("Warning"), MB_ICONWARNING | MB_OK);
		return;
	}

	CString strFilePath = GetFilePathFromFileDlg(TRUE, NULL);

	char *szFilePath = strFilePath.GetBuffer(0);
	strFilePath.ReleaseBuffer();

	CString strFileData = ReadFileData(szFilePath);
	m_et_dataSend.SetWindowText(strFileData);
}

// 清除输入按钮
void CSerialPortDlg::OnBnClickedBtClsend()
{
	if ( m_ck_send1.GetCheck() == BST_CHECKED )
	{
		MessageBox(_T("启用外部数据源时,无法执行该操作!"), _T("Warning"), MB_ICONWARNING | MB_OK);
		return;
	}
	m_et_dataSend.SetWindowText(_T(""));
}

// 数据发送按钮
void CSerialPortDlg::OnBnClickedBtSenddata()
{
	if ( !m_bIsOpen )
	{
		MessageBox(_T("串口尚未连接!\r\n发送失败!"), _T("Warning"),
			MB_ICONERROR | MB_OK);
		return;
	}

	m_CommPort.ClearOutputBuffer(); // 清除发送缓存区

	CString strDataSend;

	// 启用文件数据源
	if ( m_ck_send1.GetCheck() == BST_CHECKED )
	{
		char *szFilePath = m_strFileDatSrcFilePath.GetBuffer(0);
		m_strFileDatSrcFilePath.ReleaseBuffer();

		strDataSend = ReadFileData(szFilePath);
	}
	else
		m_et_dataSend.GetWindowText(strDataSend);

	if (strDataSend == _T(""))
		return;

	char *pszDataSend = strDataSend.GetBuffer(0);
	strDataSend.ReleaseBuffer();
	long nLen = strlen(pszDataSend);
	m_CommPort.Write(pszDataSend, nLen); // 写串口,向串口中写入数据

	m_nSendNum += strlen(pszDataSend);
	this->SetSendNumStatus();

	// 发送完后自动清空
	if (m_ck_send3.GetCheck() == BST_CHECKED && m_ck_send1.GetCheck() != BST_CHECKED)
		m_et_dataSend.SetWindowText(_T(""));
}

/************************************************************************/
/*                          接收区                                  */
/************************************************************************/

// 接收自动保存至文件
void CSerialPortDlg::OnRecvAutoToFile()
{
	if ( m_ck_recv1.GetCheck() == BST_CHECKED )
	{
		CString strFilePath = GetFilePathFromFileDlg(FALSE, NULL);
		if (strFilePath == _T(""))
		{
			m_ck_recv1.SetCheck(FALSE);
			return;
		}
		m_strAutoSaveFilePath = strFilePath;
		CString strShowStatic = _T("接收转向至文件:\r\n");
		strShowStatic += strFilePath;
		m_et_dataRecv.SetWindowText(strShowStatic);
	}
	else if ( m_ck_recv1.GetCheck() == BST_UNCHECKED )
	{
		OnBnClickedBtClrecv();
	}
	else
	{
		return;
	}
}

// 保存数据按钮
void CSerialPortDlg::OnBnClickedBtSavedata()
{
	CString strData;
	m_et_dataRecv.GetWindowText(strData);
	char *szData = strData.GetBuffer(0);
	strData.ReleaseBuffer();

	CString strFilePath = GetFilePathFromFileDlg(FALSE, _T("ReciveData.txt"));
	char *szFilePath = strFilePath.GetBuffer(0);
	strFilePath.ReleaseBuffer();

	SaveRecvData(szFilePath, szData);
}

// 清除显示按钮
void CSerialPortDlg::OnBnClickedBtClrecv()
{
	m_et_dataRecv.SetWindowText(_T(""));
}

/************************************************************************/
/*                          菜单项                                  */
/************************************************************************/

// 菜单项关闭/打开
void CSerialPortDlg::OnMenuOnoff()
{
	OnBnClickedBtOnoff();
}

// 菜单栏关于
void CSerialPortDlg::OnMenuAbout()
{
	CAboutDlg AbtDlg;
	AbtDlg.DoModal();
}

// 菜单退出按钮
void CSerialPortDlg::OnMenuExit()
{
	this->OnOK();
}

/************************************************************************/
/*                          模拟状态栏                              */
/************************************************************************/

// 复位计数按钮
void CSerialPortDlg::OnBnClickedBtClearnum()
{
	m_nSendNum = 0;
	m_nRecvNum = 0;
	this->SetSendNumStatus();
	this->SetRecvNumStatus();
}

 

                                                3.  说明

CBasCommPort类是对MSCOmm控件的封装,该类由动态链接库导出,这里不提供源码,项目里有该动态链接库的头文件和相应的DLL和LIB文件,下载地址请转至文末查看。另外,本次开发的串口调试助手能够正确发送中文,但接收中文时乱码,十六进制的发送和接收消息转为十六进制还没实现。

 

                                        4.  项目下载地址

串口调试助手C++源码(VS 2008)https://download.csdn.net/download/wu9797/10556936

相关标签: MFC