基于MSCOMM控件串口通讯(MFC)

获取当前可用的串口

 

可以通过读取注册表获取相应的值。当前可用的COM口在注册表中的位置:

HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM

通过枚举来获取所有的COM口。实现如下:

 

调用举例:

enum_reg_key_value(HKEY_LOCAL_MACHINE, ”HARDWARE\\DEVICEMAP\\SERIALCOMM”);

 

bool CValueDlg::enum_reg_key_value(HKEY hKey, LPCTSTR lpSubKey)

{

#define MAX_VALUE_NAME 100

 

    HKEY    hOpenKey;

    long    lResult = 0;

    CHAR    achClass[MAX_PATH];

    DWORD   cchClassNameLen;

    DWORD   cMaxClassLen;

    DWORD   cSubKeysNum;

    DWORD   cbMaxSubKeyLen;

    DWORD   cbValuesNum;

    DWORD   cbMaxValueNameLen;

    DWORD   cbMaxValueLen;

    DWORD   cbSecurityDescriptor;

    FILETIME ftLastWriteTime;

 

    CHAR  achValue[MAX_VALUE_NAME];

    DWORD cbValue = MAX_VALUE_NAME;

   

    DWORD DataType;

    BYTE  Data[255];

    DWORD DataLen;

    CString StrData = L"";

 

    lResult = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_READ, &hOpenKey);

 

    if(ERROR_SUCCESS != lResult)

    {

        return FALSE;

    }

 

    RegQueryInfoKey(hOpenKey, //已打开的键的句柄

                                       achClass, //类型名称

                                       &cchClassNameLen, //类型名称的长度

                                      NULL, //保留

                                    &cSubKeysNum, // 返回子键的数目

                                    &cbMaxSubKeyLen, // 返回最长的子键长度

                                   &cMaxClassLen, // 返回最长的类长度

                                  &cbValuesNum, // 返回值的数目

                                 &cbMaxValueNameLen, // 返回最长的值项,名称的长度

                                 &cbMaxValueLen, // 返回最长的值的长度

                                &cbSecurityDescriptor, //返回安全描述

                               &ftLastWriteTime); // 返回键最后被写入的时间

 

    for (DWORD j = 0, retValue = ERROR_SUCCESS; j < cbValuesNum; j++)

   {

        cbValue = MAX_VALUE_NAME;

        memset(achValue, 0, sizeof(achValue));

        memset(Data, 0, sizeof(Data));

        StrData = L"";

 

        retValue = RegEnumValue(hOpenKey,

                                                           j,

                                                          achValue,

                                                          &cbValue,

                                                          NULL,

                                                          &DataType,

                                                         Data,

                                                        &DataLen);

 

         if (retValue == (DWORD) ERROR_SUCCESS )

         { 

             StrData.Format("%s", Data);

             m_ComboComSelect.AddString(StrData);  

         }

    }

 

    RegCloseKey(hOpenKey);

    return true;

 

#undef MAX_VALUE_NAME

}

打开串口

 

m_CMSComm.SetCommPort(4);

m_CMSComm. SetInBufferSize(1024);

m_CMSComm. SetOutBufferSize(512);

 

if(!m_CMSComm.GetPortOpen())

{

       try

       {

              m_CMSComm.SetPortOpen(TRUE); //打开串口

       }

       catch(COleDispatchException *e)//如果所要打开的串口已经被其他程序使用,会报异常

       {    

              AfxMessageBox("Error:The COM has been occupied or isn't Exist");

              e = NULL;

              return;

       }

 

       m_CMSComm.SetInputMode(0); //设置输入方式为Text

       m_CMSComm.SetSettings(m_Uart_Struct.settings);

       m_CMSComm.SetRThreshold(1); //为1表示接收到一个字符便出发接收事件

       m_CMSComm.SetInputLen(0);// 设置当前接收区数据长度为0,表示全部读取

}

else

{

              AfxMessageBox("Error:The COM has been occupied or isn't Exist");

}

 

串口数据发送

关于串口数据的发送网上很多的写法都是利用以下的方法

CByteArray array;

DWORD len = 0;

char TxData [1024 + 1];

CString StrData = L"";

 

len = StrData.GetLength();

memset(TxData, 0, sizeof(TxData));

strcpy(TxData, StrData);

array.RemoveAll();

array.SetSize(len);

 

for(DWORD i=0; i<len; i++)

{

       array.SetAt(i, TxData[i]);

}

      

m_CMSComm.SetOutput(COleVariant(array)); …………………………①

其实所有的关键都在于①这条语句,传入的数据类型强制转换为任意数据类型COleVariant,于是将所要发送的字符串直接强制转换也是可以的。以上代码便以下面一条语句即可实现发送数据的操作。

m_CMSComm.SetOutput(COleVariant(StrData));

 

数据接收

 

事件2表示接收事件,其他事件所对应的值可以网上查下,很多有介绍了。如

http://blog.ednchina.com/bobi2005/175488/message.aspx

其实这里的关键就在于接收到的数据存在数据类型VARIANT中,关于它的使用也自己查吧。以下是接收的一种写法。

 

void CValueDlg::OnComm ()

{

       VARIANT          vResponse;

       int                        rec_data_len;

 

       if(m_CMSComm.GetCommEvent() == 2)

       {

              rec_data_len = m_CMSComm.GetInBufferCount();

 

              if(rec_data_len > 0)

              {

                     vResponse = m_CMSComm.GetInput();

                    

                     if(VT_BSTR == vResponse.vt)

                     {

                            save_rec_data(CString(vResponse.bstrVal));

                     }

              } 

       }

 

       return;

}

 

以下介绍另外两种接收数据写法,注意,这两种写法需要将open函数中的模式设为二进制模式即

注:m_CMSComm.SetInputMode(1);

 

方法 一、

 

void CValueDlg::OnComm ()

{

       VARIANT variant_inp;

       int rec_data_len;

       CString strtemp;

       char chData[1024 + 1];

 

       if(m_CMSComm.GetCommEvent() == 2)

       {

              variant_inp = m_CMSComm.GetInput();

              rec_data_len = variant_inp.parray->rgsabound->cElements;

 

              if (rec_data_len > 0)

              {

                     memset(chData, 0, sizeof(chData));

                     memcpy(chData, (char *)variant_inp.parray->pvData, rec_data_len);

                     strtemp.Format("%s", chData);

                     save_rec_data(strtemp);

              }

       }

 

       return;

}

 

方法 二、

这种方法是网上很多人转帖的写法,也可以实现接收的功能。如下

 

void CValueDlg::OnComm ()

{

    VARIANT      variant_tmp;

    COleSafeArray  safe_array_tmp;                      

    long length, i;

    BYTE data[1024];

    CString str ="";

    CString strTemp;

 

    if(m_CMSComm.GetCommEvent() == 2)

   {

       variant_tmp  =  m_CMSComm.GetInput();

       safe_array_tmp  =  variant_tmp;………………②

       length = safe_array_tmp.GetOneDimSize();

           

       for(i=0; i<length; i++)

      {

              safe_array_tmp.GetElement(&i, data+i); //将数据转换为BYTE型

     }

           

      for(i=0; i<length; i++) //将数组转换为Cstring型变量

      {

           BYTE bt = *(char*)(data+i);

           strTemp.Format("%c",bt);

           m_strReceiveMsg += strTemp;

            ……

      }

  }

}

 

       我最开始看到的也是这种方法,但是和许多的网友一样在执行到②语句的时候,就弹出一个警告框,忽略后还是可以继续运行。还以为是这种写法有错呢,硬着头皮看了VARIANT和COleSafeArray这两个类确定没错,回头仔细检查打开串口的设置发现设置输入模式的地方错了,如果你要用以上这种方法需要将输入模式设置成二进制模式,这样②这条语句便可以正确执行了,不知道其他人是否也很我一样的这样解决的,提出来分享下。

    以上三法希望你可以找到合适的。

 

关闭串口

 

if(m_CMSComm.GetPortOpen())

{

       m_CMSComm.SetPortOpen(FALSE);

}

        用控件虽然大大简化,但是如果有台上位机没装VC++程序,那么Release出来的程序也是不能运行的,这里下了个批处理文件,来注册控件,见附件。这样串口通信就OK了。

 

 

在②的地方经本人测试是有问题的,即使从转帖人的那边说,设置模式为二进制依然出现问题,我的编译器不报错,但就是接收不到数据,经过单步调试发现,length 的值为0,也就是说在safe_array_tmp被赋值的时候出现错误,由于本人初学MFC,做串口调试,很多地方都不是很理解,但愿会有强人指导下!

方法一是可行的,发送数据我不是按照他写的,自己写的函数,没有问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值