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

                                                 前  言

基于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下载

图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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页