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

 

 序

确定基本功能:

1.自动寻找串口,并自动添加到下拉框中共选择;

2.有波特率、数据位、停止位、校验位的选择设置;

3.串口打开控制按钮;

4.发送、清除按钮;

5.接收是自动实现的;

6.有定时自动发送功能;

7.有传送文件功能;

8.有状态栏显示,指示串口状态,设置参数和发送接收显示。

下面就一步步实现,本人纯业余,只是记录下来这个学习过程,请勿拍砖。 开发平台Visual Studio 2019社区版,

 

2. 创建MFC项

2.1  打开Visual Studio 2019->创建新项目

基于VS2019的串口调试工具开发_第1张图片

 

2.2   选择  MFC应用程序->下一步

基于VS2019的串口调试工具开发_第2张图片

 

 

2.3  填写项目名称: commassist  ,选择项目目录 ,点击 创建

基于VS2019的串口调试工具开发_第3张图片

 

 

2.4  配置工程

应用程序类型选择:基本对话框

基于VS2019的串口调试工具开发_第4张图片

 

 

 

选择最小化,最大化,勾选两个选项

基于VS2019的串口调试工具开发_第5张图片

 

 

生成类选择:Dlg

基类:CDialogEx   ,其他选项默认即可,点击  完成

基于VS2019的串口调试工具开发_第6张图片

 

项目创建完毕,进入项目。

 

点击进入  “资源视图”  界面,

删除界面上确定和取消按钮以及静态文字。

基于VS2019的串口调试工具开发_第7张图片

基于VS2019的串口调试工具开发_第8张图片

 

 

 

 

 

 

3  创建界面

3.1 添加Group Box控件

并且修改两个控件的Caption属性,

上面的GroupBox控件Caption属性改为:接收区

下面的GroupBox控件Caption属性改为:发送区

基于VS2019的串口调试工具开发_第9张图片

基于VS2019的串口调试工具开发_第10张图片

 

 

 

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属性为:停止位

基于VS2019的串口调试工具开发_第11张图片

 

 

 

 

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;

基于VS2019的串口调试工具开发_第12张图片

 

 

 

 

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

基于VS2019的串口调试工具开发_第13张图片

 

 

 

 

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属性:发送文件

基于VS2019的串口调试工具开发_第14张图片

 

 

 

 

 

 

3.6  添加Check Box控件

如图,添加两个Check Box控件

上面的Check Box控件:

Caption属性:十六进制显示

 

下面的Check Box控件:

Caption属性:十六进制发送

基于VS2019的串口调试工具开发_第15张图片

 

 

3.7 添加三个自动发送的控件

第1个控件:Static Text控件

Caption属性:每隔

 

第2个控件:Edit Control控件

ID属性:IDC_EDIT_TIMER

 

第3个控件:Static Control控件

Caption属性:毫秒

基于VS2019的串口调试工具开发_第16张图片

 

 

 

3.8  添加选择文件的一个控件

如图,添加一个Edit Control控件

ID属性:IDC_EDIT_FILEPATH

基于VS2019的串口调试工具开发_第17张图片

 

 

3.9  添加状态显示Edit Control控件

ID属性:IDC_EDIT_STATUS

Read Only属性:True

基于VS2019的串口调试工具开发_第18张图片

 

 

3.10  添加Picture Control控件

ID属性:IDC_STATIC_ICON

Type属性:Icon

Image属性:IDR_MAINFRAME

基于VS2019的串口调试工具开发_第19张图片

 

 

 

 

 

3.11  修改外框,添加最小化控件

选中外框,

Minimize Box属性:True

基于VS2019的串口调试工具开发_第20张图片

 

 

 

3.12  添加Icon图标

在 资源视图->Icon目录下->添加两个Icon图标

分别命名为IDI_ICON_CLOSE、IDI_ICON_OPEN

基于VS2019的串口调试工具开发_第21张图片

 

基于VS2019的串口调试工具开发_第22张图片

 

 

 

修改IDR_MAINFRAME图标:

基于VS2019的串口调试工具开发_第23张图片

 

3.13  效果图

最终效果图

基于VS2019的串口调试工具开发_第24张图片

 

运行时效果图:

基于VS2019的串口调试工具开发_第25张图片

 

 

4  开始写代码

4.1  基本思路

因为串口通信部分代码我可能用在以后的单片机上位机上,因此考虑单独形成CPP和H文件,定义为comm.cpp和comm.h。在comm.cpp中编写串口创建、打开、关闭以及串口监听线程(用于自动接收)的代码,同时加入进制转换或显示的函数,这些在comm.h文件中申明,

在主对话框中包含comm.h即可。想修改按钮样式,在网上搜了一圈,结果不轻松,最后确定创建新类来实现。

 

 

 

4.2  创建自定义按钮类

(1)右击选中项目名->类向导

基于VS2019的串口调试工具开发_第26张图片

 

 

 

(2)点击 添加类

基于VS2019的串口调试工具开发_第27张图片

 

 

 

 

(3)类名MyButton,基类选择CButton,点击 确定。

基于VS2019的串口调试工具开发_第28张图片

基于VS2019的串口调试工具开发_第29张图片

 

 

 

 

 

(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框等控件添加变量,文字描述麻烦,上图:

基于VS2019的串口调试工具开发_第30张图片

 

基于VS2019的串口调试工具开发_第31张图片

 

 

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

    {

        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

    {

        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;

    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 OnCbnSelchangeBaud();

};

 

 

4.2.5  在commassistDlg.cpp中替换为如下代码

 

// 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(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(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          { 
             str.Format("%c", hexdata[i]); 
             strTemp += str;
         }
         // SetDlgItemText(IDC_EDIT_TX,strTemp); 
         strOut = strTemp; 
     } 
     else 
     { 
         GetDlgItem(IDC_EDIT_TX)-> GetWindowText(str); 
         SetDlgItemText(IDC_EDIT_TX,""); 
         sTemp = DisplayHex2CString(str); 
         dwBytesWritten = str.GetLength(); 
         SetDlgItemText(IDC_EDIT_TX,strOut); 
     } 
     UpdateData(); 
     if(dwBytesWritten==0) 
     {
         MessageBox("请在发送区内输入要发送的内容", "提示", MB_ICONINFORMATION); 
         //HandSendNum = 0;//return 0;
         return;
     }

     m_intTxCnt += dwBytesWritten; 
     ShowStatus(); 
     ClearCommError(hCom, &dwErrorFlags, &ComStat); 
     bWriteStat = WriteFile(hCom, strOut, dwBytesWritten, &dwBytesWritten, &m_osWrite); 
     if (!bWriteStat) 
     { 
         if (GetLastError() == ERROR_IO_PENDING) 
         { 
             WaitForSingleObject(m_osWrite.hEvent, 1000); 
             HandSendNum=dwBytesWritten;//return dwBytesWritten;
         } 
         //HandSendNum = 0;//return 0; 
         return;
     } 
     ShowStatus(); 
     PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); 
     //HandSendNum= dwBytesWritten;//return dwBytesWritten;
     return;
}


void CcommassistDlg::ShowStatus()
{
    //状态栏显示状态 
    CString strTXcnt;
    CString strRXcnt;
    CString sTemp;
    CString comnum;
    CString strBaud, strStop, strData, strCal;
    UpdateData(true);
    if (ComIsOK)
    {
        m_comlist.GetLBText(m_comlist.GetCurSel(), sTemp);
        comnum = sTemp + "已打开";
    }
    else
        comnum = "未打开串口";
    strTXcnt.Format("发送:%d", m_intTxCnt);
    strRXcnt.Format("接收:%d", m_intRxCnt);
    
    m_baud.GetLBText(m_baud.GetCurSel(), strBaud);
    m_bstop.GetLBText(m_bstop.GetCurSel(), strStop);
    m_bdata.GetLBText(m_bdata.GetCurSel(), strData);
    m_cal.GetLBText(m_cal.GetCurSel(), strCal);
    m_strStatus = "串口: " + comnum + "  " + "状态: " + strTXcnt + ", " + strRXcnt + ", " + "波特率: " + strBaud +
        ", " + "数据位: " + strData + ", " + "停止位: " + strStop + ", " + "校验位: " + strCal;
    UpdateData(FALSE);

}

void CcommassistDlg::OnBtnClrrx()
{
    // TODO: 在此添加控件通知处理程序代码
    GetDlgItem(IDC_EDIT_RX); 
    SetDlgItemText(IDC_EDIT_RX, ""); 
    m_intRxCnt = 0; 
    m_intTxCnt = 0; 
    strIn = ""; 
    ShowStatus();
    
}


void CcommassistDlg::OnBtnClrtx()
{
    // TODO: 在此添加控件通知处理程序代码
    GetDlgItem(IDC_EDIT_TX); 
    SetDlgItemText(IDC_EDIT_TX, "");
}


void CcommassistDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    if (nIDEvent == 1)   
        OnBtnHandsend(); 
    else if (nIDEvent == 2) 
        return;
    CDialogEx::OnTimer(nIDEvent);
}


void CcommassistDlg::OnBtnAutosend()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData(TRUE);
    m_bAutoSend = !m_bAutoSend;// m_bAutoSend
    if (!m_strOut.GetLength())
    {
        MessageBox("请先输入要发送的内容", "提示", MB_ICONINFORMATION);
        m_bAutoSend = !m_bAutoSend;
    }
    else
    {
        if (m_bAutoSend)
        {
            SetTimer(1, atoi(m_strTimer.GetBuffer(m_strTimer.GetLength())), NULL); //设置定时 
            m_autosend.SetText("停止"); 
            GetDlgItem(IDC_COMCONTROL)->EnableWindow(false);
            GetDlgItem(IDC_BTN_CLRTX)->EnableWindow(false);
            GetDlgItem(IDC_BTN_CLRRX)->EnableWindow(false);
            GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(false);
            GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(false);
            GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(false);
        }
        else
        {
            KillTimer(1);
            m_autosend.SetText("自动发送"); 
            GetDlgItem(IDC_COMCONTROL)->EnableWindow(true);
            GetDlgItem(IDC_BTN_CLRTX)->EnableWindow(true);
            GetDlgItem(IDC_BTN_CLRRX)->EnableWindow(true);
            GetDlgItem(IDC_BTN_HANDSEND)->EnableWindow(true);
            GetDlgItem(IDC_BTN_SELCTFILE)->EnableWindow(true);
            GetDlgItem(IDC_BTN_SENDFILE)->EnableWindow(true);
        }
    }
}


void CcommassistDlg::OnCheckHexrx()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData(TRUE); 
    CString hexIn; 
    CString sTemp; 
    if (m_check_hexrx) 
    {
        hexIn = DisplayCString2Hex(strIn, true); 
        GetDlgItem(IDC_EDIT_RX)->SetWindowText(hexIn); 
        //将hexIn内容放入IDC_EDIT_RX框内,即为显示转换 
    } else 
        GetDlgItem(IDC_EDIT_RX)-> SetWindowText(strIn); 
       //将strIn内容放入IDC_EDIT_RX框内,即为显示不转换 
    return;

}


void CcommassistDlg::OnCheckHextx()
{
    // TODO: 在此添加控件通知处理程序代码
    UpdateData();
    CString hexOut; 
    CString str, sTemp; 
    GetDlgItem(IDC_EDIT_TX)->GetWindowText(strOut); 
    if (m_check_hextx) 
    { 
        str = DisplayCString2Hex(strOut, true); 
        strOut = str; 
    }
    else {
        int i, n;
        CString strTemp; 
        CByteArray hexdata; 
        ConvertHexC2String(strOut, hexdata); 
        n = hexdata.GetSize();
        for (i = 0; i < n; i++) 
        {
            str.Format("%c", hexdata[i]); 
            strTemp += str; 
        } 
        strOut = strTemp;
    }
    GetDlgItem(IDC_EDIT_TX)->SetWindowText(strOut); 
    //将strOut内容放入IDC_EDIT_TX框内,即为不转换 
    UpdateData(); 
    return;

}


void CcommassistDlg::OnBtnSelctfile()
{
    // TODO: 在此添加控件通知处理程序代码
    CFile file; 
    m_strFile.Empty();
    CFileDialog FileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "文本文件(*.txt)|*.txt||"); if (FileDlg.DoModal() == IDOK) //打开文件对话框 
    m_strFilePath = FileDlg.GetPathName(); //得到文件路经 
    else 
        return;
    file.Open(m_strFilePath, CFile::modeRead | CFile::typeBinary);//打开这个文件 
    file.Read(m_strFile.GetBuffer(file.GetLength()), file.GetLength()); //读文件 
    m_strFile.ReleaseBuffer(); if(m_strFile.GetLength() >= 2048) 
    {      
        AfxMessageBox("文件的长度超过2k字节!",MB_ICONINFORMATION); 
    } 
    else 
    {   
        m_strOut += m_strFile; //文件内容加入发送框变量内    
        UpdateData(false); //更新发送框内容 
    } 
    file.Close(); 
}


void CcommassistDlg::OnBtnSendfile()
{
    // TODO: 在此添加控件通知处理程序代码
    COMSTAT state; 
    DWORD errors; 
    CString sTemp; 
    int iTemp; 
    ClearCommError(hCom, &errors, &state); //清除串口错误、得到当前状态 
    iTemp=m_strOut.GetLength(); //写入串口的字符串长度,由EDIT控件内字符串数决定 
    iTemp += iTemp; 
    OnBtnHandsend(); //调用发送函数 
    GetDlgItem(IDC_EDIT_FILEPATH)->SetDlgItemText(IDC_EDIT_FILEPATH,""); 
    UpdateData(false); //更新发送框内容;

}


HBRUSH CcommassistDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    // TODO:  在此更改 DC 的任何特性
    if (nCtlColor == CTLCOLOR_DLG)   //所有对话框 
    {     
        HBRUSH   brush=CreateSolidBrush(RGB(220,250,250)); 
        return   brush;   
    } 
    if(nCtlColor == CTLCOLOR_STATIC)   
    {
        pDC->SetTextColor(RGB(50, 50, 50));         
        //pDC->SetBkColor(RGB(128,128,128));//设置文本背景色        
        //pDC->SetTextColor(RGB(55,55,66));        
        pDC->SetBkMode(TRANSPARENT);//设置背景透明    
    } 
    switch (pWnd->GetDlgCtrlID())                 
    { 
        //针对ID为IDC_CTL1、IDC_CTL2和IDC_CTL3的控件进行同样的设置    
        case IDC_EDIT_RX:        
        {            
            //pDC->SetBkMode(TRANSPARENT);//背景色透明            
            //pDC->SetTextColor(RGB(250,0,0));// 设置字体颜色为红色
            pDC->SetTextColor(RGB(0, 0, 255));// 设置字体颜色为   //255  
            pDC->SetBkColor(RGB(255, 255, 255));   // 改为背景颜色即可  
            hbr = CreateSolidBrush(RGB(255, 255, 255));//背景

            //Invalidate(false);
            break;
        } 
        
        case IDC_EDIT_TX:        
        {   
            pDC->SetTextColor(RGB(0,0,255));// 设置字体颜色为   //255  
            pDC->SetBkColor(RGB(255, 255, 255));   // 改为背景颜色即可  
            hbr = CreateSolidBrush(RGB(255, 255, 255));//背景
            break;
        }


        default:break;
    } 
    // TODO:  如果默认的不是所需画笔,则返回另一个画笔
    return hbr;
}

void CcommassistDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);

    // TODO: 在此处添加消息处理程序代码
    // nType == 1不可以省略,否则由最小化恢复为正常状态下的时候会出错

        //nType是一个枚举类型,主要是指定所请求的不同的调整大小。这个参数可以是下列值之一:SIZE_MAXIMIZED 、SIZE_MINIMIZED 。SIZE_RESTORED , SIZE_MAXHIDE ,SIZE_MAXSHOW 其原型可以在msdn上查看


        if (nType == 1) return; //最小化则什么都不做 
    CWnd* pWnd;
    pWnd = GetDlgItem(IDC_EDIT_RX); //获取控件句柄
    ChangeSize(pWnd, cx, cy,20); //调用changesize()函数
    pWnd = GetDlgItem(IDC_STATIC_RX); //获取控件句柄
    ChangeSize(pWnd, cx, cy,10);//调用changesize()函数

    pWnd = GetDlgItem(IDC_EDIT_TX); //获取控件句柄
    ChangeSize(pWnd, cx, cy, 20); //调用changesize()函数
    pWnd = GetDlgItem(IDC_STATIC_TX); //获取控件句柄
    ChangeSize(pWnd, cx, cy, 10);//调用changesize()函数


    GetClientRect(&m_rect); //将变化后的对话框设置为旧大小
}

void CcommassistDlg::ChangeSize(CWnd* pWnd, int cx, int cy, int deviate)
{
    if (pWnd)
    {
        CRect rect;
        pWnd->GetWindowRect(&rect); //获取控件变化前的大小
        ScreenToClient(&rect);//将控件大小转换为在对话框中的区域坐标 
        rect.right = cx- deviate;
        pWnd->MoveWindow(rect);//设置控件大小
    }
}
 

 

 

 

 

 

 

 

5  总结

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

你可能感兴趣的:(MFC学习)