MFC实战篇——VS实现基于MFC的串口通信程序实例

一、串口通信的概念

  • 串口通行是一种将接受来自cpu的并行数据字符转化为连续的串行数据流发送出去,同时可将接收的串行数据流转化为并行的数据字符供给cpu的器件。
  • 串口通信是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低
  • 串口通信指串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。
  • 串口用于ASCII码字符的传输。通信使用3根线完成,分别是地线、发送、接收由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配

二、串口通信的三种协议

2.1 RS-232

RS-232使用费平衡参考地的信号。
RS-232只限于PC串口和设备间点对点的通信。
RS-232串口通信最远距离是50英尺。
在这里插入图片描述

RS-232针脚的功能:
数据:

引脚作用
TXD(pin3)串口数据输出
RXT(pin2)串口数据输入

握手

引脚作用
RTS(pin7)发送数据请求(requst to send)
CTS(pin8)清除数据 clear to send
DSR(pin6)数据发送就绪date send ready
DCD(pin1)数据载波检测date carrier detect
DTR(pin4)数据终端就绪date terminal ready

地线:

引脚作用
GND(pin5)地线

其它

引脚作用
RI(pin 9)铃声指示
  • 通信方式采用连接三线:TX,Rx和地线。但是对于数据传输,双方需要对数据定时使用相同的载波率

2.2 RS-422

RS-422是苹果计算机的串口连接标准

使用差分信号。差分传输使用二根线实现发送和接收信号具有更好的抗噪声和更远的传输距离。

2.3 RS-485

RS-485是RS-422的改进,增加了设备的个数,从10个增加到32。
RS-485的引脚的功能:

  • 数据:1(DATA-) 2(DATA+)
  • 地线:5

三、串口通信方法和用处

3.1 用处

  • 大多数计算机(不包括笔记本电脑)包含两个基于RS-232的串口。
  • 串口同时也是仪器仪表设备通用的通信协议;很多GPIB兼容的设备也带有RS-232口。
  • 串口通信协议也可以用于获取远程采集设备的数据。

3.2 串口通信方法

  1. 利用Windows API通信函数;
    在32位的Windows系统中,串口和其它通信设备是作为文件处理的。串口的打开、关闭、读取和写入所用的函数与操作文件的函数完全一致。

    1. 通信会话以调用CreateFile()开始。CreateFile()为读访问、写访问或读写访问“打开”串口。按照Windows的通常做法,CreateFile()返回一个句柄。
    2. 关闭串口调用CLoseHandle().
    3. 获取串口的当前配置:getCommState()
    4. 实现初始化的缓冲区控制:SetupComm()
    5. ReadFile()和WriteFile()读写数据。
    6. 利用GetCommMask()函数和 SetCommMask函数控制通信事件。
      感觉每个函数都非常的复杂,参数非常多。但是直接使用windows系统提供的串行口API函数相对较为灵活
  2. 利用Visual C++的标准通信函数_inp、_inpw、_inpd、_outp、_outpw、_outpd等直接对串口进行操作;

  3. 通过微软的串口通信控件MSComm,它是一种ActiveX控件;

  4. 利用第3方编写的通信类,比如MuMega Technologies公司提供的CSerail类;

四、重要参数

4.1 波特率

衡量符号传输速率的参数。

指的是信息被调制之后单位时间内的变化。即单位时间内载波参数变化的次数。

如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。

4.2 数据位

衡量通信中实际传输数据的参数。

计算机发出一个信息包,实际的数据位7或者8位。每个包是一个字节,包括开头位和停止位。

实际数据位取决于通信协议的选举。

4.3 停止位

用于表示单个包的最后一位。典型的值为1、1.5/2位。

由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

4.4 奇偶检验位

在串口通信中一种简单的检错方式。

有四种检错方式:偶、奇、高和低。没有校验位也是可以的。

  • 对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。

    例如 : 如果数据是011,
    对于偶校验,校验位为0,保证逻辑高的位数是偶数个。
    对于奇校验,校验位为1,这样就有3个逻辑高位。

  • 高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步

五、MSComm

  • MSComm控件通过串行端口传输和接受数据的,为应用程序提供串行通讯功能

    Microsoft Communications Control(简称MSComm)是Microsoft公司提供的简化Windows下串行通信编程的ActiveX控件。它为应用程序提供了通过串行接口收发数据的简便方法。

  • MSComm控件提供了两种处理通信问题的方法:

    ▪◾◼🔲 1. 事件驱动(Event-driven)方法
           事件驱动通讯是处理串行端口交互作用的一种非常有效的方法。在许多情况下,在事件发生时需要得到通知。

           例如,在串口接收缓冲区中有字符,或者 Carrier Detect (CD) 或 Request To Send (RTS) 线上一个字符到达或一个变化发生时。在这些情况下,可以利用 MSComm 控件的 OnComm 事件捕获并处理这些通讯事件。

           OnComm 事件还可以检查和处理通讯错误。所有通讯事件和通讯错误的列表,参阅 CommEvent 属性。在编程过程中,就可以在OnComm事件处理函数中加入自己的处理代码。

           这种方法的优点是程序响应及时,可靠性高。每个MSComm 控件对应着一个串行端口。如果应用程序需要访问多个串行端口,必须使用多个 MSComm 控件。

    ▪◾◼🔲 2. 查询法
           查询方式实质上还是事件驱动。但在有些情况下,这种方式显得更为便捷。在程序的每个关键功能之后,可以通过检查 CommEvent 属性的值来查询事件和错误。如果应用程序较小,并且是自保持的,这种方法可能是更可取的。

5.1 版本不兼容的注意事项

MSComm控件函数更新使用:
新版本的Visual studio使用MSComm时之前很多以Set开头或者Get开头的函数都改成了以Put_开头或者Get_开头

如之前的SetCommPort改成 Put_CommPort;GetInput改成Get_Input;


5.2 属性

  • portOpen

    1. Bool get_PortOPen() 返回当前是否打开串口 ,true为打开,false为关闭。
    2. void put_PortOpen(bool newValue)设置串口打开,参数newValue为要打开的串口号。
    3. 设置或返回通讯口的状态以及打开和关闭端口,可通过把该属性设置为true或者false来打开或者关闭端口;

  • CommPort设置并返回端口号

    1. put_CommPort(short nnewvalue)设置需要打开的端口号
    2. short get_CommPort()获取已经打开的串口号
    3. 可以设置为1到16之间的任何值;
    4. 注意: 在打开串口之前必须要设置要打开的串口。在关闭串口前也必须保证串被关闭串是处于打开状态的。

  • BufferSize

    1. put_InBufferSize(short newValue) 设置输入缓冲区大小,缺省下为1024Byte
    2. put_OutBufferSize(short newValue) 设置输出缓冲区大小,缺省为1024字节

  • BufferCount

    1. InBufferCount(short newValue);设置接收缓冲区内等待读取的字节数,是指已接收。参数为0表示清空接收缓冲区。
    2. OutBufferCount(short newValue);设置/获得发送缓冲区内等待发送的字节数,0为清空发送缓冲区

  • put_InputMode()

    1. put_InputMode()设置为以二进制或者字符串的方式进行输入。
    2. comInputModeText(0):表示以文本(ASCII)方式取回数据;
      comInputModeBinary(1):表示以二进制方式取回数据;
      comInputModeText, comInputModeBinary为预定义常量,分别表示0,1。

  • put_RThreshold()

    1. 当输入缓冲区的字符达到设置的阈值时,控件就会产生一个onComm事件,并且CommEvent属性设置为ComEvReceive,即为接受事件。
    2. 语法:object.put_RThreshold(len);
    3. value为整型表达式。 收到len个字节将引发onComm事件
    4. 当接收字符后,若 RThreshold 属性设置为 0(缺省值)则不产生 OnComm 事件

  • put_SThreshold属性

    1. 每当串口缓冲区有多于或等于设定的某个字节数据时触发接收数据的onComm函数,而且MSComm 控件设置 CommEvent 属性为 comEvSend ,即为发送事件。
    2. 语法 object.put_RThreshold(value);
    3. value 整形表达式,代表在 OnComm 事件产生之前在传输缓冲区中的最小字符数。
    4. 若设置 SThreshold 属性为 0(缺省值),数据传输事件不会产生 OnComm 事件。
      若设置 Sthreshold 属性为 1,当传输缓冲区完全空时,MSComm 控件产生 OnComm 事件。
      如果在传输缓冲区中的字符数小于 value,CommEvent 属性设置为comEvSend,并产生 OnComm 事件。comEvSend 事件仅当字符数与 Sthreshold 交叉时被激活一次。

      例如,如果 Sthreshold 等于 5,仅当在输出队列中字符数从 5 降到 4 时,comEvSend 才发生。如果在输出队列中从没有比 Sthreshold 多的字符,comEvSend 事件将绝不会发生。

  • put_Settings()

    1. 设置通信的参数,格式为:波特率,奇偶校验类型,数据位,停止位;
    2. 语法: object_put_Settings( value)
    3. 说明:当端口打开时,如果 value 非法,MSComm 控件产生错误 380(非法属性值)。
    4. Value 由四个设置值组成,有如下的格式:
      “BBBB,P,D,S”
      1. BBBB 为波特率
      2. P 为奇偶校验,
      3. D 为数据位数,
      4. S 为停止位数。
      5. value 的缺省值是:“9600,N,8,1”

    m_mscomm.put_Settings(_T(“9600,n,8,1”));//波特率9600 、无校验位,8位数据位,1位停止位。

  • put_InputLen()

    1. 确定希望从接收缓冲区移出的字符数量,当InputLen=0时,一次把接收缓冲区的字符全部移出;
    2. 语法:object.put_InputLen(value)
    3. value是整型表达式,说明input属性从缓冲区读取的字符数。InputLen 属性的缺省值是 0。设置InputLen 为 0 时,使用 Input 将使 MSComm 控件读取接收缓冲区中全部的内容。
    4. 若接收缓冲区中 InputLen 字符无效,Input 属性返回一个零长度字符串 ("")。在使用 Input 前,用户可以选择检查 InBufferCount 属性来确定缓冲区中是否已有需要数目的字符。该属性在从输出格式为定长数据的机器读取数据时非常有用。

  • OutPut属性

    1. 向发送缓冲区传递待发送的数据。
    2. 形式 :object . put_Output(COleVariant(date));

  • Input属性
    1. 从接收缓冲区中读出数据,然后将该数据从缓冲区移走
    2. 形式:object.get_Input();

  • EofEnable属性
    1. 确定在输入过程中 MSComm 控件是否寻找文件结尾(EOF) 字符。如果找到 EOF 字符,将停止输入并激活OnComm 事件,此时 CommEvent 属性设置为 comEvEOF,
    2. 语法:object.EOFEnable (value)
    3. value 布尔表达式。确定当找到 EOF 字符时,OnComm 事件是否被激活。
    4. value 的设置值:
      1. True 当 EOF 字符找到时 OnComm 事件被激活。
      2. False (缺省)当 EOF 字符找到时 OnComm 事件不被激活。 当 EOFEnable 属性设置为 False,OnComm 控件将不在输入流中寻找 EOF 字符。

5.3 MSComm控件的事件

如果在通信过程中发生事件或错误,则会触发OnComm事件,并由CommEvent获得代码反应错误类型,可以根据该属性值执行不同的操作
CommEvent属性值及其含义如下:

  • MSCOMM控件只使用一个事件OnComm,用属性CommEvent的17个值来区分不同的触发时机,主要有以下几个:
    1. CommEvent=1时:comEvSend :传输缓冲区中的字符个数已少于Sthreshold(可设置的属性值)个;

    2. CommEvent=2时:comEvReceive:接收缓冲区中收到Rthreshold(可设置的属性值)个字符,利用此事件可编写接收数据的过程;

    3. CommEvent=3时:comEvCTS:CTS线发生变化;

    4. CommEvent=4时:comEvDSR :DSR线发生变化;

    5. CommEvent=5时:comEvCD:CD线发生变化;

    6. CommEvent=6时:comEvRing:检测到振铃信号;

      另外十种情况是通信错误时产生,即错误代码。

  • 错误消息
    下表列出 MSComm 控件可以捕获的错误:
    值         描述 
    380   无效属性值 comInvalidPropertyValue
    383    属性为只读 comSetNotSupported
    394    属性为只读 comGetNotSupported 
    8000   端口打开时操作不合法 comPortOpen
    8001   超时值必须大于 0 
    8002   无效端口号 comPortInvalid
    8003   属性只在运行时有效 
    8004   属性在运行时为只读 
    8005   端口已经打开 comPortAlreadyOpen
    8006   设备标识符无效或不支持该标识符 
    8007   不支持设备的波特率 
    8008   指定的字节大小无效 
    8009   缺省参数错误 
    8010   硬件不可用(被其它设备锁定) 
    8011   函数不能分配队列 
    8012   设备没有打开 comNoOpen 
    8013   设备已经打开 
    8014   不能使用 comm 通知 
    8015   不能设置 comm 状态 comSetCommStateFailed
    8016   不能设置 comm 事件屏蔽 
    8018   仅当端口打开时操作才有效 comPortNotOpen 
    8019   设备忙 
    8020   读 comm 设备错误 comReadError
    8021   为该端口检索设备控制块时的内部错误 comDCBError 
    
    

    详细参考:https://blog.csdn.net/h_kingone/article/details/53588686

5.4 串口数据的读写

  • MSComm 类的读写函数比较简单:get_Input()和put_Output()
  • 函数原形分别为
      VARIANT get_Input();
      void put_Output(const VARIANT newValue);
      均使用VARIANT类型。
  • PC机发送和接收数据时习惯用字符串形式。

    MSDN中查阅VARIANT类型,可以用BSTR表示字符串,但所有的BSTR都包含宽字符,而只有Windows NT支持宽字符,Windows 9X并不支持。

5.4.1 数据的发送

为发送按钮添加事件处理程序。

//发送数据
void CMFCSerialComDlg::OnBnClickedsend()
{
	if (m_setOk==TRUE) {
		UpdateData(TRUE);
		m_ctrlComm.put_Output(COleVariant(m_sendDate + TEXT("\r\n")));     //发送数据,换行
		m_iSentBytes += m_sendDate.GetLength();                            //获取发送了多少字节
	}
	else {
		AfxMessageBox(TEXT("请先设置参数并打开串口"));
	}
}

5.4.2 数据的接收

VARIANT variant;                         //接收到的缓冲区数据类型
COleSafeArray csa;
LONG len = 0;
LONG k=0;
BYTE reDate[2048] = { 0 };                   //用于存放缓冲区数据转化后的数据
CString strtemp;                             //存放中间变量
static int iRecvBytes = 0;
if (pWnd->m_ctrlComm.get_CommEvent() == 2){  //事件值为2表示接收事件'
	variant=pWnd->m_ctrlComm.get_Input();//开始读缓冲区
	csa = variant;                   //VARIANT型变量转换为COleSafeArray变量
	len = csa.GetOneDimSize();           //得到缓冲区读到的有效数据长度
	for (k = 0; k < len; k++) {
		csa.GetElement(&k, reDate + k);      //转换成byte数组
	}

	//m_ctrlComm.put_OutBufferCount(0);        //清空发送缓冲区
	//m_ctrlComm.put_InBufferCount(0);         //滑空接收缓冲区
	//csa_inp.Clear();

	for (k = 0; k < len; k++) {             //将数组转化为CString型变量
		BYTE bt = *(char*)(reDate+k);         //转化成字符型
		strtemp.Format(_T("%c"), bt);          //将变量送到临时变量存放
		pWnd->m_receiveDate += strtemp;     //将数据显示在接收编辑框
		++iRecvBytes;                       //接收到的字节加一;
		pWnd->m_iRecvBytes = iRecvBytes;    //将值传给成员变量
	}
}

5.5 数据类型的处理

输入输出缓冲区得书局类型都为VARIANT类型程序中常用到的字符处理类型时CString,char*等类型。所以需要数据类型的转换。

VARIANT input1;//定义一个缓冲区内的数据类型变量
char *str,*str1;
int counts,i;
CString input2;
counts=m_ctrlComm.get_InBufferCount();//获取接收缓冲区中的字符数
if(counts>0){
  input1=m_ctrlComm.get_Input();//接收缓冲区内容,存储到input1中。
}
i=0;
str1=str;
while(i<counts){
   i++;
   str1++;
}
*str1='\0';
input2=(conuts char*) str;//根据counts值,得到有效的输入input2
  • 在发送数据时:
    需要使用ColeVariant(Cstring &str)将字符串转化成VARIANT类型变量。
    Cstring m_sendDate;
    m_sendDate="love"
    m_ctrlComm.put_Output(COleVariant(m_sendDate));
    

📢📢📢 小知识:
检验位的获取必须进行转化。
MSCOMM串口通讯的中的奇偶校验位的参数:even、 mark、no、odd、space。even是偶校验,odd为奇校验,no为无校验。

六、相关知识

6.1 SetDlgItemTextW函数

  • 功能
    SetDlgItemText是一种函数,功能是设置对话框中控件的文本和标题。

  • 函数原型

    BOOLSetDlgltemText(HWND hDlg,int nlDDlgltem,LPCTSTR IpString)
    
  • 参数

    1. hDlg:指定含有控件的对话框。
    2. nlDDlgltem:标识带有将被设置的标题和文本的控件。
    3. IpString:指向一个以NULL结尾的字符串指针,该字符串指针包含了将被复制到控件的文本。
  • 返回值
    如果函数调用成功,则返回值为非零值。
    如果函数调用失败,则返回值为零。
    若想获得更多的错误信息,请调用GetLastError函数。

  • SetDlgItemText, SetDlgItemTextA, SetDlgItemTextW

    1. 三者的关系好比:人、男人、女人
    2. 三个对应的字符编码分别是:TCHAR、char、wchar_t /WCHAR
    3. 如果你要获取文字到 char 数组,显式调用SetDlgItemTextA。
      如果你要获取文字到 WCHAR 数组,显式调用SetDlgItemTextW。
      如果你要获取文字到 TCHAR 数组,调用 SetDlgItemText
    4. TCHAR 是个不确定类型,可能是 char 或 WCHAR。
    5. SetDlgItemText 是个宏名称,可能定义SetDlgItemTextA 或 SetDlgItemTextW。取决于 Visual Studio 项目设置为多字节编码,还是 Unicode 编码。
    6. SetDlgItemText 存在的意义,是与 TCHAR 的存在一样。
      通过宏定义,使得 SetDlgItemText 和 TCHAR 能根据项目编码设置同步切换到对应编码。

6.2 ComboBox

6.2.1 Combo Box (组合框)控件

Combo Box (组合框)控件很简单,可以节省空间。从用户角度来看,这个控件是由一个文本输入控件和一个下拉菜单组成的。用户可以从一个预先定义的列表里选择一个选项,同时也可以直接在文本框里面输入文本。下面的例子简要说明如何利用 MFC CComboBox Class来操作字符串列表。

  1. 定义控件对应变量
    假定已经创建了一个Dialog,并且从控件工具箱将 Combo Box 控件拖放到上面。打开 Class Wizard,添加控件对应变量,如:CComboBox m_cbExamble;

    在后面的代码中会不断使用这个变量。


  1. 向控件添加 Items
    1. 在Combo Box控件属性的Data标签里面添加,一行表示Combo Box下拉列表中的一行。换行用ctrl+回车。
    2. 利用函数 AddString() 向 Combo Box 控件添加 Items,如:
      m_cbExample.AddString(“StringData1”);
      m_cbExample.AddString(“StringData2”);
      m_cbExample.AddString(“StringData3”);
      
    3. 也可以调用函数 InsertString() 将 Item 插入指定位置 nIndex,如:
      m_cbExample.InsertString( nIndex, “StringData” );
    在程序初始化时动态添加:
    CString strTemp;
    ((CComboBox*)GetDlgItem(IDC_COMBO_CF))->ResetContent();//消除现有所有内容
    for(int i=1;i<=100;i++){
    	strTemp.Format("%d",i);
    	((CComboBox*)GetDlgItem(IDC_COMBO_CF))->AddString(strTemp);
    }
    
    下拉的时候添加:
    CString strTemp;
    int iCount=((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetCount();//取得目前已经有的行数
    if(iCount<1)//防止重复多次添加
    {
    	((CComboBox*)GetDlgItem(IDC_COMBO_CF))->ResetContent();
    	for(int i=1;i<=100;i++){
    		strTemp.Format("%d",i);
    		((CComboBox*)GetDlgItem(IDC_COMBO_CF))->AddString(strTemp);
    	}
    }				
    

  1. 从控件得到选定的Item
    假设在控件列表中已经选定某项,现在要得到被选定项的内容,首先要得到该项的位置,然后得到对应位置的内容。这里会用到两个函数,如:

    int nIndex = m_cbExample.GetCurSel();
    CString strCBText;
    m_cbExample.GetLBText( nIndex, strCBText);
    

    这样,得到的内容就保存在 strCBText 中。

    若要选取当前内容,可调用函数GetWindowText(strCBText)


  1. 在控件中查找给定Item
    这种操作一般用于在程序中动态修改控件中该项的值,可以用函数FindStringExact() 精确匹配,如:
    int nIndex = m_cbExample.FindStringExact( nStartAfter, “value to be found”);
    nStartAfter指明从哪一行开始查找。
    如果查找成功,返回的是该项的位置;否则,返回CB_ERR。
    
    也可以选中包含指定字符串的项,如:
    int nIndex = m_cbExample.SelectString( nStartAfter, “value to be selected”);
    

  1. 删除控件中的Item
    该操作可以利用函数DeleteString(),需要指定被删除项的位置,如:m_cbExample.DeleteString(nIndex);

    也可以使用函数ResetContent(),清除目前的所有项,如:m_cbExample.ResetContent();


  1. 显示控件中的某项
    int nIndex = m_cbExample.GetCurSel(); //当前选中的项
    m_cbExample.SetCurSel(nIndex); //设置第nIndex项为显示的内容
    

  1. 得到或设置输入框中被选中的字符位置
    DWORD GetEditSel( ) /BOOL SetEditSel( int nStartChar, int nEndChar );
    BOOL LimitText( int nMaxChars ); 设置输入框中可输入的最大字符数。
    

  1. 列表框常用消息映射宏
    ON_CBN_DBLCLK 鼠标双击
    ON_CBN_DROPDOWN 列表框被弹出
    ON_CBN_KILLFOCUS / ON_CBN_SETFOCUS 在输入框失去/得到输入焦点时产生
    ON_CBN_SELCHANGE 列表框中选择的行发生改变
    ON_CBN_EDITUPDATE 输入框中内容被更新
    

6.2.2 为ComboBox添加下拉选项

  1. 首先我通过类向导给下拉框控件所在的对话框添加了一个ComboBox变量,如图所示。
    在这里插入图片描述
    添加后再对话框的cpp文件的DoDataExchange函数中会自动生成一句代码:
    DDX_Control(pDX, IDC_COMBO_com, m_combo_Com);
    表示将控件与添加的变量绑定。这样就可以通过该变量来对控件进行操作。
  2. 定义了一个数组:
    CString strFont[5] = { _T("COM1"),_T("COM2"),_T("COM3"),_T("COM4"),_T("COM4")};
  3. 添加子项目:
    1. 通过for循环将CString数组逐个添加到控件:
      for (int i = 0; i < 5; i++) {
           m_Combobox.AddString(strFont[i]); 
      }
      
    2. 换成InsertString方法:
      for (int i = 0; i < 5; i++) {
            m_Combobox.InsertString(i,strFont[i]);
      } 
      
    3. 直接在Date属性上添加属性内容
      在这里插入图片描述

6.2.3 如何控制Combo Box的下拉长度

首先要知道两点:

  1. 那就是在设计界面里,点击一下Combo Box的下拉箭头,此时出现的调整框就是Combo Box的下拉调整框。

  2. 属性里有个 No integral height 钩选项,表示最大长度为设计长度,如果实际内容比设计长度多,就出现滚动条,少就以实际长度显示。

  3. 选择其中的某行

    • 选中:
      int iPos=((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetCurSel();//当前选中的行。
      
    • 设置
      ((CComboBox*)GetDlgItem(IDC_COMBO_CF))->SetCurSel(n)//设置第n行内容为显示的内容。
      
  4. 取得Combo Box框内容

    1. 取当前内容
      ((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetWindowText(strTemp);
      
    2. 取其他行内容
      ((CComboBox*)GetDlgItem(IDC_COMBO_CF))->GetLBText(n,strTemp);
      
  5. 获得焦点

    通常要判断控件是否获得了焦点,可以用GetFocus()函数
    例如:if(GetFocus()==GetDlgItem(IDC_EDIT_VALUE2))//判断焦点是否在编辑框IDC_EDIT_VALUE2内

    但是combobox 的焦点不同,因为它是由edit和listbox两部分组成的,所以获得焦点要用GetParent():if ((GetFocus()->GetParent())==GetDlgItem(IDC_COMBO_CF))

6.2.4 VC++ Combo Box/Combo Box Ex控件

组合窗口是由一个输入框和一个列表框组成。创建一个组合窗口可以使用成员函数:

BOOL CListBox::Create( LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID = 0xffff );
  • dwStyle
    其中dwStyle将指明该窗口的风格,除了子窗口常用的风格WS_CHILD,WS_VISIBLE外,你可以针对列表控件指明专门的风格。
    CBS_DROPDOWN 下拉式组合框
    CBS_DROPDOWNLIST 下拉式组合框,但是输入框内不能进行输入
    CBS_SIMPLE 输入框和列表框同时被显示
    LBS_SORT 所有的行按照字母顺序进行排序

由于组合框内包含了列表框:
列表框的功能都能够使用,如可以利用:

int AddString( LPCTSTR lpszItem )添加行,
int DeleteString( UINT nIndex )删除指定行,
int InsertString( int nIndex, LPCTSTR lpszItem )将行插入到指定位置。
void ResetContent( )可以删除列表框中所有行。
int GetCount( )得到当前列表框中行的数量。
int GetCurSel( )/int SetCurSel(int iIndex):得到/设置当前被选中的行的位置
int GetLBText(int nIndex, LPTSTR lpszText )得到列表框内指定行的字符串。
int FindString(int nStartAfter, LPCTSTR lpszItem )可以在当前所有行中查找指定的字符传的位置,nStartAfter指明从那一行开始进行查找。
int SelectString( int nStartAfter, LPCTSTR lpszItem )可以选中包含指定字符串的行。

此外输入框的功能都能够使用,如可以利用:

DWORD GetEditSel( ) /BOOL SetEditSel( int nStartChar, int nEndChar )得到或设置输入框中被选中的字符位置。
BOOL LimitText( int nMaxChars )设置输入框中可输入的最大字符数。
输入框的剪贴板功能Copy,Clear,Cut,Paste动可以使用。

6.2.5 列表框几种常用的消息映射宏

ON_CBN_DBLCLK 鼠标双击
ON_CBN_DROPDOWN 列表框被弹出
ON_CBN_KILLFOCUS / ON_CBN_SETFOCUS 在输入框失去/得到输入焦点时产生
ON_CBN_SELCHANGE 列表框中选择的行发生改变
ON_CBN_EDITUPDATE 输入框中内容被更新

使用以上几种消息映射的方法为定义原型如:afx_msg void memberFxn( ),并且定义形式如ON_Notification( id, memberFxn )的消息映射。如果在对话框中使用组合框,Class Wizard会自动列出相关的消息,并能自动产生消息映射代码。

在MFC 4.2中对组合框进行了增强,你可以在组合框中使用ImageList,有一个新的类CComboBoxEx(由CComboBox派生)来实现这一功能。

  • 在CComboBoxEx类中添加了一些新的成员函数来实现新的功能:首先你需要调用CImageList* SetImageList( CImageList* pImageList );来设置ImageList,然后调用int InsertItem( const COMBOBOXEXITEM* pCBItem );来添加行,其中COMBOBOXEXITEM定义如下:

    typedef struct { 
    UINT mask;
    int iItem; 
    LPTSTR pszText; 
    int cchTextMax; 
    int iImage; 
    int iSelectedImage; 
    int iOverlay;       
    int iIndent; 
    LPARAM lParam;
    }COMBOBOXEXITEM, *PCOMBOBOXEXITEM;
    

    需要设置mask=CBEIF_IMAGE CBEIF_TEXT,并设置iItem为插入位置,设置pszText为显示字符串,设置iImage为显示的图标索引。下面的代码演示了如何进行插入:

    /*m_cbeWnd 为已经创建的CComboBox对象
    m_list 为CImageList对象IDB_IMG 为16*(16*4)的位图,每个图片为16*16共4个图标*/
    m_list.Create(IDB_IMG,16,4,RGB(0,0,0));
    m_cbeWnd.SetImageList(&m_list);
    COMBOBOXEXITEM insItem;insItem.mask=CBEIF_IMAGE CBEIF_TEXT;insItem.iItem=0; insItem.iImage=0;insItem.pszText="Line 1";m_cbeWnd.InsertItem(&insItem);insItem.iItem=1;insItem.iImage=1;insItem.pszText="Line 2";m_cbeWnd.InsertItem(&insItem);
    

    通过调用int DeleteItem( int iIndex );来删除行,并指明行的位置。
    通过调用BOOL GetItem( COMBOBOXEXITEM* pCBItem )/BOOL SetItem( const COMBOBOXEXITEM* pCBItem );来得到/设置行数据

    摘自 https://blog.csdn.net/chinawangfei/article/details/45892847

VS建立的工程用新版本打开升级后,不能打开资源视图,出现错误提示
error RC2104:undefined keyword or key name ······
解决方法:
在*.rc中将#include <windows.h>加上就可以了

七、代码

CMSComm.h : 由 Microsoft Visual C++ 创建的 ActiveX 控件包装器类的声明
// 特性
public:
	
	//设置一个握手枚举
	enum
	{
		NoHandshaking = 0,
		XonXoff = 1,
		RtsCts = 2,
		XonXoffAndRtsCts = 3
	}HandshakingConstants;

	enum
	{
		comNone = 0,
		comXOnXoff = 1,
		comRTS = 2,
		comRTSXOnXOff = 3
	}HandshakeConstants;
	
	//CommEvent属性会返回一个值,每个值代表不同的通信事件或者错误。
	//Error常数
	enum
	{
		comInvalidPropertyValue = 380,
		comGetNotSupported = 394,
		comSetNotSupported = 383,
		comPortInvalid = 8002,
		comPortAlreadyOpen = 8005,
		comPortOpen = 8000,
		comNoOpen = 8012,
		comSetCommStateFailed = 8015,
		comPortNotOpen = 8018,
		comReadError = 8020,
		comDCBError = 8021,
		comBreak = 1001,
		comCTSTO = 1002,
		comDSRTO = 1003,
		comFrame = 1004,
		comOverrun = 1006,
		comCDTO = 1007,
		comRxOver = 1008,
		comRxParity = 1009,
		comTxFull = 1010,
		comDCB = 1011
	}ErrorConstants;
	
	//CommEvent属性会返回一个值,每个值代表不同的通信事件或者错误。
	//以下为通信事件定义了一个枚举。
	enum
	{
		comEventBreak = 1001,
		comEventCTSTO = 1002,
		comEventDSRTO = 1003,
		comEventFrame = 1004,
		comEventOverrun = 1006,
		comEventCDTO = 1007,
		comEventRxOver = 1008,
		comEventRxParity = 1009,
		comEventTxFull = 1010,
		comEventDCB = 1011
	}CommEventConstants;
	
	//OnComm 常数
	enum
	{
		comEvSend = 1,
		comEvReceive = 2,
		comEvCTS = 3,
		comEvDSR = 4,
		comEvCD = 5,
		comEvRing = 6,
		comEvEOF = 7
	}OnCommConstants;



// MFCSerialComDlg.h: 头文件
//
#include "CMSComm.h" 
#include "afxwin.h"
#pragma once


//yj:接受数据线程
UINT ThreadReceive(LPVOID lparam);



// CMFCSerialComDlg 对话框
class CMFCSerialComDlg : public CDialogEx
{
// 构造
public:
	CMFCSerialComDlg(CWnd* pParent = nullptr);	// 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MFCSERIALCOM_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:
	
	
	//yj:以下是个人自定义添加的数据成员
	int m_iRecvBytes;           //已经接受到的数据个数
	int m_iSentBytes;           //已经发送的数据个数
	bool m_setOk;               //用于标记串口是否打开
	int m_iComNum;              //串口号
	CString m_ComBTL;           //波特率
	CString m_ComSertify;       //奇偶校验位
	CString m_ComDateBit;       //数据位
	CString m_ComStopBit;       //停止位
	CString m_filePath;         //文件地址
								  
	//yj:以下是基于控件的成员变量

	//发送编辑框发送数据的内容
	CString m_sendDate;     
	//接收编辑框的数据内容
	CString m_receiveDate;
	//activeX控件的控制变量
	CMSComm m_ctrlComm;
	// 波特率的控制变量
	CComboBox m_combo_btl;
	// 奇偶校验的控制变量
	CComboBox m_combo_sertify;
	// 数据位的控制变量
	CComboBox m_combo_datebit;
	// 停止位的控制变量
	CComboBox m_combo_stopbit;
	//串口控制变量
	CComboBox m_combo_Com;

	DECLARE_EVENTSINK_MAP()
	//active控件的事件处理函数
	void OnCommMscomm4();
	//Com口的处理函数
	afx_msg void OnCbnSelchangeCombocom();
	//发送按钮的处理函数
	afx_msg void OnBnClickedsend();
	//清除按钮的处理函数
	afx_msg void OnBnClickedreceive();
	//波特率的事件处理函数
	afx_msg void OnCbnSelchangeCombobtl();
	//奇偶校验位事件处理函数
	afx_msg void OnCbnSelchangeCombosertify();
	//数据位事件处理函数
	afx_msg void OnCbnSelchangeCombodatebit();
	//停止位的事件处理函数
	afx_msg void OnCbnSelchangeCombostopbit();
	//打开com口的事件处理函数
	afx_msg void OnBnClickedopencomm();
	//保存数据的事件处理函数
	afx_msg void OnBnClickedsavedate();
	void SaveData();
   afx_msg void OnTimer(UINT_PTR nIDEvent);
};

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

#include "stdafx.h"
#include "MFCSerialCom.h"
#include "MFCSerialComDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


UINT ThreadReceive(LPVOID lparam) {
	CMFCSerialComDlg* pWnd = (CMFCSerialComDlg*)lparam;

	VARIANT variant;                         //接收到的缓冲区数据类型
	COleSafeArray csa;
	LONG len = 0;
	LONG k=0;
	BYTE reDate[2048] = { 0 };                   //用于存放缓冲区数据转化后的数据
	CString strtemp;                             //存放中间变量
	static int iRecvBytes = 0;
	if (pWnd->m_ctrlComm.get_CommEvent() == 2){  //事件值为2表示接收事件'
		variant=pWnd->m_ctrlComm.get_Input();//开始读缓冲区
		csa = variant;                   //VARIANT型变量转换为COleSafeArray变量
		len = csa.GetOneDimSize();           //得到缓冲区读到的有效数据长度
		for (k = 0; k < len; k++) {
			csa.GetElement(&k, reDate + k);      //转换成byte数组
		}

		//m_ctrlComm.put_OutBufferCount(0);        //清空发送缓冲区
		//m_ctrlComm.put_InBufferCount(0);         //滑空接收缓冲区
		//csa_inp.Clear();

		for (k = 0; k < len; k++) {             //将数组转化为CString型变量
			BYTE bt = *(char*)(reDate+k);         //转化成字符型
			strtemp.Format(_T("%c"), bt);          //将变量送到临时变量存放
			pWnd->m_receiveDate += strtemp;     //将数据显示在接收编辑框
			++iRecvBytes;                       //接收到的字节加一;
			pWnd->m_iRecvBytes = iRecvBytes;    //将值传给成员变量
		}
	}
	/*SetDlgItemText是一种函数,功能是设置对话框中控件的文本和标题。
	函数原型是BOOLSetDlgltemText(HWND hDlg,int nlDDlgltem,LPCTSTR IpString)。
	hDlg:指定含有控件的对话框。
    nlDDlgltem:标识带有将被设置的标题和文本的控件。
    IpString:指向一个以NULL结尾的字符串指针,该字符串指针包含了将被复制到控件的文本。
    返回值:如果函数调用成功,则返回值为非零值。如果函数调用失败,则返回值为零。若想获得更多的错误信息,请调用GetLastError函数。
	*/
	pWnd->SetDlgItemTextW(IDC_receiveEdit, pWnd->m_receiveDate);
	return 0;
}








// 用于应用程序“关于”菜单项的 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()










// CMFCSerialComDlg 对话框



CMFCSerialComDlg::CMFCSerialComDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MFCSERIALCOM_DIALOG, pParent)
	, m_sendDate(_T(""))
	, m_receiveDate(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	
}

void CMFCSerialComDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_COMBO_com, m_combo_Com);
	DDX_Control(pDX, IDC_MSCOMM4, m_ctrlComm);
	DDX_Text(pDX, IDC_sendEdit, m_sendDate);
	DDX_Text(pDX, IDC_receiveEdit, m_receiveDate);
	DDX_Control(pDX, IDC_COMBO_btl, m_combo_btl);
	DDX_Control(pDX, IDC_COMBO_sertify, m_combo_sertify);
	DDX_Control(pDX, IDC_COMBO_datebit, m_combo_datebit);
	DDX_Control(pDX, IDC_COMBO_stopbit, m_combo_stopbit);
}

BEGIN_MESSAGE_MAP(CMFCSerialComDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_CBN_SELCHANGE(IDC_COMBO_com, &CMFCSerialComDlg::OnCbnSelchangeCombocom)
	ON_BN_CLICKED(IDC_send, &CMFCSerialComDlg::OnBnClickedsend)
	ON_BN_CLICKED(ID_receive, &CMFCSerialComDlg::OnBnClickedreceive)
	ON_CBN_SELCHANGE(IDC_COMBO_btl, &CMFCSerialComDlg::OnCbnSelchangeCombobtl)
	ON_CBN_SELCHANGE(IDC_COMBO_sertify, &CMFCSerialComDlg::OnCbnSelchangeCombosertify)
	ON_CBN_SELCHANGE(IDC_COMBO_datebit, &CMFCSerialComDlg::OnCbnSelchangeCombodatebit)
	ON_CBN_SELCHANGE(IDC_COMBO_stopbit, &CMFCSerialComDlg::OnCbnSelchangeCombostopbit)
	ON_BN_CLICKED(IDC_opencomm, &CMFCSerialComDlg::OnBnClickedopencomm)
	ON_BN_CLICKED(IDC_savedate, &CMFCSerialComDlg::OnBnClickedsavedate)
END_MESSAGE_MAP()


// CMFCSerialComDlg 消息处理程序

BOOL CMFCSerialComDlg::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);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	//初始化串口设置:
	m_combo_Com.SetCurSel(0);
	m_combo_btl.SetCurSel(8);
	m_combo_datebit.SetCurSel(3);
	m_combo_stopbit.SetCurSel(0);
	m_combo_sertify.SetCurSel(0);

	//初始化发送、接收数据设置
	//CButton* pR=(CButton*)GetDlgItem()



	m_setOk = FALSE;
	m_iRecvBytes = 0;
	m_iSentBytes = 0;

	UpdateData(FALSE);

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

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

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

void CMFCSerialComDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

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


BEGIN_EVENTSINK_MAP(CMFCSerialComDlg, CDialogEx)
	ON_EVENT(CMFCSerialComDlg, IDC_MSCOMM4, 1, CMFCSerialComDlg::OnCommMscomm4, VTS_NONE)
END_EVENTSINK_MAP()

//oncomm事件
void CMFCSerialComDlg::OnCommMscomm4()
{
	// TODO: 在此处添加消息处理程序代码
	//启动接收数据的线程
	AfxBeginThread(ThreadReceive, this);
}


//选择com口
void CMFCSerialComDlg::OnCbnSelchangeCombocom()
{
	// TODO: 在此添加控件通知处理程序代码
    m_iComNum = m_combo_Com.GetCurSel()+1;//获取组合框中的选中项的索引
}




//发送数据
void CMFCSerialComDlg::OnBnClickedsend()
{
	if (m_setOk==TRUE) {
		UpdateData(TRUE);
		m_ctrlComm.put_Output(COleVariant(m_sendDate + TEXT("\r\n")));     //发送数据,换行
		m_iSentBytes += m_sendDate.GetLength();                            //获取发送了多少字节
	}
	else {
		AfxMessageBox(TEXT("请先设置参数并打开串口"));
	}

}

//清空数据
void CMFCSerialComDlg::OnBnClickedreceive()
{
	// TODO: 在此添加控件通知处理程序代码
	//此函数用于清空接收区域的数据
	m_receiveDate = "";
	UpdateData(FALSE);
}



//波特率
void CMFCSerialComDlg::OnCbnSelchangeCombobtl()
{
	// TODO: 在此添加控件通知处理程序代码
	m_combo_btl.GetLBText(m_combo_btl.GetCurSel(), m_ComBTL);
}

//奇偶校验位
void CMFCSerialComDlg::OnCbnSelchangeCombosertify()
{
	// TODO: 在此添加控件通知处理程序代码
	int isel = m_combo_sertify.GetCurSel();
	switch (isel)
	{
		case 0:m_ComSertify = TEXT("n"); break;
		case 1:m_ComSertify = TEXT("o"); break;
		case 2:m_ComSertify = TEXT("e"); break;
		case 3:m_ComSertify = TEXT("m"); break;
		default:m_ComSertify = TEXT("s"); break;
	}
}

//数据位
void CMFCSerialComDlg::OnCbnSelchangeCombodatebit()
{
	// TODO: 在此添加控件通知处理程序代码
	m_combo_datebit.GetLBText(m_combo_datebit.GetCurSel(), m_ComDateBit);
}

//停止位
void CMFCSerialComDlg::OnCbnSelchangeCombostopbit()
{
	// TODO: 在此添加控件通知处理程序代码
	m_combo_stopbit.GetLBText(m_combo_stopbit.GetCurSel(), m_ComStopBit);
}

//打开串口
void CMFCSerialComDlg::OnBnClickedopencomm()
{
	// TODO: 在此添加控件通知处理程序代码
	if (m_setOk==FALSE) {
		m_ctrlComm.put_CommPort(m_iComNum);//选择即将需要通信的串口
		m_ctrlComm.put_PortOpen(TRUE);     //打开选择的串口
		m_ctrlComm.put_RThreshold(1);      //当缓存区有一个字节便引发onCom事件
		m_ctrlComm.put_InputMode(1);       //输入模式为二进制方式
		CString s;
		s.Format(TEXT("%s,%s,%s,%s"), m_ComBTL,m_ComSertify,m_ComDateBit,m_ComStopBit);
		m_ctrlComm.put_Settings(s);      //设置串口参数,波特率,奇偶检验,停止位,数据位
		m_ctrlComm.put_InputLen(0);      //设置接收缓冲区长度为默认值
		m_ctrlComm.get_Input();          //先预读缓冲区以清除数据残留

		m_setOk = TRUE;
		m_iRecvBytes = 0;
		m_iSentBytes = 0;

	}
	else {
		m_setOk = FALSE;
		MessageBox(_T("请打开串口"));
	}
}

//保存数据到文件
void CMFCSerialComDlg::OnBnClickedsavedate(){
	CFileDialog dlg(FALSE, 0, 0, 0, TEXT("*.txt|*.txt|*.*|*.*||"), this);
	if (dlg.DoModal())
	{
		m_filePath = dlg.GetPathName();
		SetTimer(0, 2000, 0);     //每隔2s写入一次数据
	}

}


//保存数据
void CMFCSerialComDlg::SaveData(){
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	HANDLE hfile = CreateFile(m_filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, NULL);
	if (hfile == INVALID_HANDLE_VALUE) {
		AfxMessageBox(TEXT("新建文件失败!"));
		return;
	}
	else if (!WriteFile(hfile, m_receiveDate, m_receiveDate.GetLength(), NULL, NULL)){ 
		AfxMessageBox(TEXT("保存文件失败!"));
		return;
	}
}

void CMFCSerialComDlg::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	if (m_setOk && nIDEvent == 0)   //串口打开了才保存数据
		SaveData();
	/*else
	{
		CString s;
		s.Format(TEXT("接收字节数:%d"), m_iRecvBytes);
		SetDlgItemText(IDC_STATIC_RECV_BYTES, s);
		s.Format(TEXT("发送字节数:%d"), m_iSendBytes);
		SetDlgItemText(IDC_STATIC_SEND_BYTES, s);
	}*/
	CDialogEx::OnTimer(nIDEvent);
}

在这里插入图片描述

分享几个好的文章:

  1. https://www.cnblogs.com/yzl050819/p/6393905.html
  2. https://www.cnblogs.com/gaohongchen01/p/4432922.html
  3. VS2008后,就没有MSCOMM控件了,用户想用只能手动添加
    https://blog.csdn.net/hustrains/article/details/48227073
  4. 本文将向您展示由程序员llbird编写的cnComm(中国串口?)串口类。
    https://blog.csdn.net/nash635/article/details/5339704
  5. mfc消息大全:
    https://www.cnblogs.com/orez88/articles/2119450.html
  6. (MFC消息映射机制概述)
    http://www.jizhuomi.com/software/147.html
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__ 返回首页