win10注册MSCOMM32控件及简单使用

前言 & 吐槽

前几天重装了系统,然后之前写的用MsComm控件(Microsoft Communications Control)进行串口通信的程序无法运行了,用VS打开项目进入资源视图,发现打不开包含MsComm控件的对话框资源,提示未在此计算机上注册ActiveX控件。而VS工具箱提供的COM组件里也没有MsComm控件。
根本原因是这个ActiveX控件根本不是win10安装自带的,高版本VS也不会自带的,你要写这种串口通信程序,要么用底层API,要么用微软大力支持的.Net系语言,而不是MFC这个非常非常过时的破东西了,然而就这个破东西,很多时候还是被迫得写,嗯,前几天才看到2018年的二级C/C++上机环境终于从万年不变的VC6变成了VS2010,简直了。

注册MsComm控件

首先给下载地址,其实谷歌MsComm.ocx第一条就是
https://www.ocxme.com/files/mscomm32_ocx
当然国内用百度的多,一堆文章全部都指向了CSDN花几个积分的下载链接,好点的有百度盘链接,不过天知道过段时间会不会被和谐。还是这种专门的免费下载站更舒心。
下完解压后就是MSCOMM32.OCX,然后需要注意(我踩了这个坑),对于64位系统需要把它放在C:\Windows\SysWOW64目录下,32位系统才是C:\Windows\system32目录。之前我放在system32目录然后注册出了问题。
然后右键管理员权限打开cmd窗口,进入对应目录输入指令regsvr32 MSCOMM32.OCX即可(如下图所示)


网上有些教程还有一步是修改注册表,不过我发现regsvr32命令已经修改了注册表,无需手动修改。

为何使用MsComm控件?

其实直接用底层API进行串口编程未尝不可,MSDN也给出了示例,但是事件回调的步骤得自己写,如果不是时间充裕用来学习/练手的情况,没必要重复造轮子。
网上也可以搜到不少包装好的C++类,但是串口这东西本来就是古老物了,搜到的代码还是很多年前的,代码风格不一定很好。而且有的接口已经和现代C++标准不兼容了。
比如之前师兄用了一个简单小巧的库(只包含1个头文件和1个源文件,添加进工程即可)提供了这样的接口

    void        WriteToPort(char* string);

然而C++11标准在近几个版本的VS里已经得到了支持,字符串字面值是不能直接转换成char*类型的,也就是说实际调用的时候得像这样 xxx.WriteToPort((char*)"hello")xxx.WriteToPort(const_cast<char*>("hello"))
然后错误信息字符串操作全都是基于char*的,MFC默认Unicode,而且方便移植的代码应该都对TCHAR*来操作,直接编译会出错的,需要一个个用宏_T()把字符串包含起来。当然,会正则表达式的话起来替换相对会比较轻松。
我对MsComm控件不甚了解,但是明显这也是古老物了,毕竟还不支持64位程序,VS里用Debug或Release x64来编译的话会失败。
但是好处在于,这个控件是官方的,值得信赖,不像很多开源库那样缺乏大量测试。

为何不使用MsComm控件?

这篇文章是2018-01-17发布的,我在2018-02-01完成课题程序时发现了问题。正如上文所言,MsComm控件是古老物了,只适用于32位的程序。如果程序必须编译成64位的,那么MsComm控件无法派上用场。我的程序里需要用到OpenCV和一个第三方库,两者刚好都只提供了64位的lib和dll,所以只有使用MsComm控件,后来使用了上文提到的CSerialPort,才发现这个古老版本原来国内有不少人进行了维护,现在还是非常好用的,强力推荐。

添加MsComm控件

首先需要在VS中添加该控件到工具箱中



按上述操作点确定即可
PS:可以看到版本才1.1,还是version 6.0,目测从VC6之后再也没更新过……
然后在对话框资源编辑框中右键,插入ActiveX控件



不同于常规的界面控件,MsComm控件不会显示出来,所以随便拖到哪个位置都可以。
拖了控件之后就是为控件添加变量了

这一步,VS会自动生成一对.h和.cpp文件,然后在xxxDlg.h中添加成员变量
CMscomm1 m_comm1;
并在xxxDlg.cpp中添加数据交换操作

void CTestDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_MSCOMM1, m_comm1);
}

可以发现和使用其他控件一样的套路。

从串口发送和接收字节

文本框/复选框这种自定义参数输入的控件就不祥述了,以我的需求为例

波特率: 9600 停止位: 1 传送位数: 8位 奇偶校验: 无
串口协议是7个字节表示1个数据包
MsComm控件类型为CMscomm_las,变量名为m_commLaser

  1. 打开/关闭串口
        m_commLaser.put_CommPort("COM1");
        m_commLaser.put_InputMode(CMscomm_las::comInputModeBinary);
        m_commLaser.put_InBufferSize(512);  // 接收缓冲区大小
        m_commLaser.put_OutBufferSize(512);  // 发送缓冲区大小
        m_commLaser.put_Settings(_T("9600,n,8,1"));
        if (!m_commLaser.get_PortOpen())
        {
            try {
                m_commLaser.put_PortOpen(TRUE);  // 打开串口
            }
            catch (CException* e) {
                TCHAR error_msg[1024];
                e->GetErrorMessage(error_msg, 1024);
                MessageBox(error_msg);
                return;
            }
            m_commLaser.put_RThreshold(7);  // 每当接收缓冲区有7个字符时则接收串口数据
            m_commLaser.put_InputLen(0);
            m_commLaser.get_Input();
        }
        else
        {
            MessageBox(_T("打开端口失败!"));
        }

注意put_Settings的参数,n代表无奇偶校验,是DCB结构的Parity成员的可选取值NOPARITY的缩写,同理,奇校验ODDPARITY是o,偶校验EVENPARITY是e。
打开串口是put_PortOpen(TRUE),关闭串口自然就是put_PortOpen(FALSE)。

  1. 发送字节序列
    void put_Output(VARIANT newValue)
    直接调用上述方法即可,问题来了,VARIANT类型是什么?这就是ActiveX控件的蛋疼之处,它的数据交换必须用Ole那一套来。
    正常来说,发送给串口的都是字节序列,即uint8_t或BYTE数组。因此这里只需要知道怎么转换成VARIANT类型即可。
    MFC是上古时期的产物,那个时候C++98标准都没确立,因此微软弄了一堆自定义的容器类,虽然除了CString外几乎都被C++标准库的STL取代了。但在这里,MFC的CByteArray刚好能直接用来构造COleVariant对象,COleVariant继承自VARIANT(C结构体的typedef别名),仅仅是增加了若干方法,可以隐式类型转换。
    比如发送3个字节0x01 0x02 0x04给串口的代码如下
    CByteArray m_baSend;
    m_baSend.Add(0x01);
    m_baSend.Add(0x02);
    m_baSend.Add(0x04);
    m_commLaser.put_Output(COleVariant(m_baSend));
  1. 接收字节序列
    给MsComm控件添加OnComm事件处理程序即可




    和其他控件一样,MsComm控件也是事件驱动,在后台接收数据,然后处理不同的事件

enum
{
    comEvSend = 1,
    comEvReceive = 2,
    comEvCTS = 3,
    comEvDSR = 4,
    comEvCD = 5,
    comEvRing = 6,
    comEvEOF = 7
}OnCommConstants

接收数据只需要处理comEvReceive事件即可,其他几个事件等真正有需求的时候再去处理。下面给出我接收7个字节的代码

void CMsCommDemoDlg::OnCommMscommLas()
{
    if (m_commLaser.get_CommEvent() == CMscomm_las::comEvReceive)
    {
        // 读取串口的接收缓冲区(之前打开串口时设置过缓冲区大小)
        COleSafeArray safearray_obj = m_commLaser.get_Input();
        // 填充数据到自定义缓冲区中
        const int BUFF_SIZE = 7;
        BYTE buffer[BUFF_SIZE];
        for (long i = 0; i < BUFF_SIZE; i++)
        {
            safearray_obj.GetElement(&i, &buffer[i]);
        }
        // TODO: 处理缓冲区buffer[]的数据
    }
}

这里就和发送数据反过来了,接收的数据类型是VARIANT,需要转换成BYTE数组来处理。套路就是借助COleSafeArray这个中间物及其GetElement方法。

总结

其实这个程序是我几周之前写的Demo,最近要重新写了,照着我的代码写下来,自己也梳理了用MsComm控件的步骤。
控件方面,由于这东西实在太古老了而被抛弃了,而官方也没给C++提供什么替代品,于是得手动引入

  1. 注册MSCOMM32.OCX
  2. 在VS工具箱的COM组件中找到MsComm控件添加进来
  3. 像使用其他MFC控件一样使用它(添加关联的控件变量/事件处理函数)

代码方面,虽然COM接口的实现看起来非常复杂且蛋疼,但是其实接口很清晰,都是一堆put(设置)和get(获取)方法,注意打开/关闭串口也是通过putxxx来执行的,相当于设置串口的连接状态。
难点在于数据交换格式都是VARIANT,因此需要借用MFC专门提供的类来转换成方便处理的BYTE数组。大致过程如下

BYTE[] -> CByteArray -> COleVariant -> VARIANT
VARIANT -> COleSafeArray -> BYTE[]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270