win10注冊MSCOMM32控件及簡單使用

前言 & 吐槽

前幾天重裝了系統(tǒng),然后之前寫的用MsComm控件(Microsoft Communications Control)進行串口通信的程序無法運行了敌卓,用VS打開項目進入資源視圖,發(fā)現(xiàn)打不開包含MsComm控件的對話框資源癣防,提示未在此計算機上注冊ActiveX控件肪虎。而VS工具箱提供的COM組件里也沒有MsComm控件惧蛹。
根本原因是這個ActiveX控件根本不是win10安裝自帶的,高版本VS也不會自帶的迅腔,你要寫這種串口通信程序沧烈,要么用底層API像云,要么用微軟大力支持的.Net系語言,而不是MFC這個非常非常過時的破東西了腋逆,然而就這個破東西惩歉,很多時候還是被迫得寫俏蛮,嗯,前幾天才看到2018年的二級C/C++上機環(huán)境終于從萬年不變的VC6變成了VS2010争涌,簡直了辣恋。

注冊MsComm控件

首先給下載地址抑党,其實谷歌MsComm.ocx第一條就是
https://www.ocxme.com/files/mscomm32_ocx
當然國內(nèi)用百度的多,一堆文章全部都指向了CSDN花幾個積分的下載鏈接害晦,好點的有百度盤鏈接,不過天知道過段時間會不會被和諧鲫剿。還是這種專門的免費下載站更舒心灵莲。
下完解壓后就是MSCOMM32.OCX殴俱,然后需要注意(我踩了這個坑),對于64位系統(tǒng)需要把它放在C:\Windows\SysWOW64目錄下明场,32位系統(tǒng)才是C:\Windows\system32目錄苦锨。之前我放在system32目錄然后注冊出了問題趴泌。
然后右鍵管理員權限打開cmd窗口,進入對應目錄輸入指令regsvr32 MSCOMM32.OCX即可(如下圖所示)


網(wǎng)上有些教程還有一步是修改注冊表秃励,不過我發(fā)現(xiàn)regsvr32命令已經(jīng)修改了注冊表莺治,無需手動修改谣旁。

為何使用MsComm控件滋早?

其實直接用底層API進行串口編程未嘗不可,MSDN也給出了示例搁进,但是事件回調(diào)的步驟得自己寫饼问,如果不是時間充裕用來學習/練手的情況揭斧,沒必要重復造輪子。
網(wǎng)上也可以搜到不少包裝好的C++類盅视,但是串口這東西本來就是古老物了闹击,搜到的代碼還是很多年前的,代碼風格不一定很好贺归。而且有的接口已經(jīng)和現(xiàn)代C++標準不兼容了。
比如之前師兄用了一個簡單小巧的庫(只包含1個頭文件和1個源文件,添加進工程即可)提供了這樣的接口

    void        WriteToPort(char* string);

然而C++11標準在近幾個版本的VS里已經(jīng)得到了支持,字符串字面值是不能直接轉(zhuǎn)換成char*類型的丹莲,也就是說實際調(diào)用的時候得像這樣 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發(fā)布的废赞,我在2018-02-01完成課題程序時發(fā)現(xiàn)了問題叮姑。正如上文所言传透,MsComm控件是古老物了,只適用于32位的程序朱盐。如果程序必須編譯成64位的兵琳,那么MsComm控件無法派上用場。我的程序里需要用到OpenCV和一個第三方庫者春,兩者剛好都只提供了64位的lib和dll碧查,所以只有使用MsComm控件,后來使用了上文提到的CSerialPort忠售,才發(fā)現(xiàn)這個古老版本原來國內(nèi)有不少人進行了維護稻扬,現(xiàn)在還是非常好用的羊瘩,強力推薦盼砍。

添加MsComm控件

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



按上述操作點確定即可
PS:可以看到版本才1.1,還是version 6.0黔宛,目測從VC6之后再也沒更新過……
然后在對話框資源編輯框中右鍵,插入ActiveX控件



不同于常規(guī)的界面控件,MsComm控件不會顯示出來案淋,所以隨便拖到哪個位置都可以险绘。
拖了控件之后就是為控件添加變量了

這一步隆圆,VS會自動生成一對.h和.cpp文件,然后在xxxDlg.h中添加成員變量
CMscomm1 m_comm1;
并在xxxDlg.cpp中添加數(shù)據(jù)交換操作

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

可以發(fā)現(xiàn)和使用其他控件一樣的套路蹬屹。

從串口發(fā)送和接收字節(jié)

文本框/復選框這種自定義參數(shù)輸入的控件就不祥述了慨默,以我的需求為例

波特率: 9600 停止位: 1 傳送位數(shù): 8位 奇偶校驗: 無
串口協(xié)議是7個字節(jié)表示1個數(shù)據(jù)包
MsComm控件類型為CMscomm_las,變量名為m_commLaser

  1. 打開/關閉串口
        m_commLaser.put_CommPort("COM1");
        m_commLaser.put_InputMode(CMscomm_las::comInputModeBinary);
        m_commLaser.put_InBufferSize(512);  // 接收緩沖區(qū)大小
        m_commLaser.put_OutBufferSize(512);  // 發(fā)送緩沖區(qū)大小
        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);  // 每當接收緩沖區(qū)有7個字符時則接收串口數(shù)據(jù)
            m_commLaser.put_InputLen(0);
            m_commLaser.get_Input();
        }
        else
        {
            MessageBox(_T("打開端口失敗!"));
        }

注意put_Settings的參數(shù)潮太,n代表無奇偶校驗虾攻,是DCB結(jié)構(gòu)的Parity成員的可選取值NOPARITY的縮寫,同理奇钞,奇校驗ODDPARITY是o漂坏,偶校驗EVENPARITY是e。
打開串口是put_PortOpen(TRUE)谷徙,關閉串口自然就是put_PortOpen(FALSE)。

  1. 發(fā)送字節(jié)序列
    void put_Output(VARIANT newValue)
    直接調(diào)用上述方法即可图呢,問題來了骗随,VARIANT類型是什么鸿染?這就是ActiveX控件的蛋疼之處,它的數(shù)據(jù)交換必須用Ole那一套來涨椒。
    正常來說蚕冬,發(fā)送給串口的都是字節(jié)序列,即uint8_t或BYTE數(shù)組猎提。因此這里只需要知道怎么轉(zhuǎn)換成VARIANT類型即可旁蔼。
    MFC是上古時期的產(chǎn)物棺聊,那個時候C++98標準都沒確立,因此微軟弄了一堆自定義的容器類葵诈,雖然除了CString外幾乎都被C++標準庫的STL取代了作喘。但在這里耐亏,MFC的CByteArray剛好能直接用來構(gòu)造COleVariant對象,COleVariant繼承自VARIANT(C結(jié)構(gòu)體的typedef別名)暇矫,僅僅是增加了若干方法,可以隱式類型轉(zhuǎn)換李根。
    比如發(fā)送3個字節(jié)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. 接收字節(jié)序列
    給MsComm控件添加OnComm事件處理程序即可




    和其他控件一樣粤攒,MsComm控件也是事件驅(qū)動囱持,在后臺接收數(shù)據(jù),然后處理不同的事件

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

接收數(shù)據(jù)只需要處理comEvReceive事件即可盔几,其他幾個事件等真正有需求的時候再去處理掩幢。下面給出我接收7個字節(jié)的代碼

void CMsCommDemoDlg::OnCommMscommLas()
{
    if (m_commLaser.get_CommEvent() == CMscomm_las::comEvReceive)
    {
        // 讀取串口的接收緩沖區(qū)(之前打開串口時設置過緩沖區(qū)大小)
        COleSafeArray safearray_obj = m_commLaser.get_Input();
        // 填充數(shù)據(jù)到自定義緩沖區(qū)中
        const int BUFF_SIZE = 7;
        BYTE buffer[BUFF_SIZE];
        for (long i = 0; i < BUFF_SIZE; i++)
        {
            safearray_obj.GetElement(&i, &buffer[i]);
        }
        // TODO: 處理緩沖區(qū)buffer[]的數(shù)據(jù)
    }
}

這里就和發(fā)送數(shù)據(jù)反過來了芯丧,接收的數(shù)據(jù)類型是VARIANT世曾,需要轉(zhuǎn)換成BYTE數(shù)組來處理。套路就是借助COleSafeArray這個中間物及其GetElement方法肿轨。

總結(jié)

其實這個程序是我?guī)字苤皩懙腄emo,最近要重新寫了驼唱,照著我的代碼寫下來玫恳,自己也梳理了用MsComm控件的步驟京办。
控件方面,由于這東西實在太古老了而被拋棄了不恭,而官方也沒給C++提供什么替代品,于是得手動引入

  1. 注冊MSCOMM32.OCX
  2. 在VS工具箱的COM組件中找到MsComm控件添加進來
  3. 像使用其他MFC控件一樣使用它(添加關聯(lián)的控件變量/事件處理函數(shù))

代碼方面折晦,雖然COM接口的實現(xiàn)看起來非常復雜且蛋疼沾瓦,但是其實接口很清晰,都是一堆put(設置)和get(獲确缋)方法魂莫,注意打開/關閉串口也是通過putxxx來執(zhí)行的豁鲤,相當于設置串口的連接狀態(tài)鲸沮。
難點在于數(shù)據(jù)交換格式都是VARIANT琳骡,因此需要借用MFC專門提供的類來轉(zhuǎn)換成方便處理的BYTE數(shù)組。大致過程如下

BYTE[] -> CByteArray -> COleVariant -> VARIANT
VARIANT -> COleSafeArray -> BYTE[]
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讼溺,一起剝皮案震驚了整個濱河市楣号,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌怒坯,老刑警劉巖炫狱,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異剔猿,居然都是意外死亡视译,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門归敬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酷含,“玉大人,你說我怎么就攤上這事椅亚。” “怎么了媚赖?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豺妓。 經(jīng)常有香客問我训堆,道長,這世上最難降的妖魔是什么絮缅? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮允扇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘糊治。我一直安慰自己俊戳,他們只是感情好渐北,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布恃锉。 她就那樣靜靜地躺著,像睡著了一般土砂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上序臂,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音,去河邊找鬼析显。 笑死分尸,一個胖子當著我的面吹牛箩绍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播卑吭,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼掷邦,長吁一口氣:“原來是場噩夢啊……” “哼或杠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笋额,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤鉴未,失蹤者是張志新(化名)和其女友劉穎淹真,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體客扎,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡针姿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饲趋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片家肯。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歹茶,到底是詐尸還是另有隱情,我是刑警寧澤尸昧,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站幢妄,受9級特大地震影響朽褪,放射性物質(zhì)發(fā)生泄漏衍锚。R本人自食惡果不足惜戴质,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裸诽。 院中可真熱鬧甘畅,春花似錦、人聲如沸匀们。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽三妈。三九已至对室,卻和暖如春么翰,著一層夾襖步出監(jiān)牢的瞬間戴已,已是汗流浹背怔匣。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工代芜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挤庇,地道東北人苹威。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓汇在,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353