⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chap12_4.htm

📁 vc教程,真正的程序员用Visual C++。如果你想当程序员
💻 HTM
📖 第 1 页 / 共 4 页
字号:
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">// 工作者线程,负责监视串行口</p>
          <p align="JUSTIFY">UINT CommProc(LPVOID pParam)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">OVERLAPPED os;</p>
          <p align="JUSTIFY">DWORD dwMask, dwTrans;</p>
          <p align="JUSTIFY">COMSTAT ComStat;</p>
          <p align="JUSTIFY">DWORD dwErrorFlags;</p>
          <p align="JUSTIFY">CTerminalDoc *pDoc=(CTerminalDoc*)pParam;</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">memset(&amp;os, 0, sizeof(OVERLAPPED));</p>
          <p align="JUSTIFY">os.hEvent=CreateEvent(NULL, TRUE, FALSE, NULL);</p>
          <p align="JUSTIFY">if(os.hEvent==NULL)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">AfxMessageBox(&quot;Can't create event object!&quot;);</p>
          <p align="JUSTIFY">return (UINT)-1;</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">while(pDoc-&gt;m_bConnected)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">ClearCommError(pDoc-&gt;m_hCom,&amp;dwErrorFlags,&amp;ComStat);</p>
          <p align="JUSTIFY">if(ComStat.cbInQue)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">// 无限等待WM_COMMNOTIFY消息被处理完</p>
          <p align="JUSTIFY">WaitForSingleObject(pDoc-&gt;m_hPostMsgEvent, INFINITE);</p>
          <p align="JUSTIFY">ResetEvent(pDoc-&gt;m_hPostMsgEvent);</p>
          <p align="JUSTIFY">// 通知视图</p>
          <p align="JUSTIFY">PostMessage(pDoc-&gt;m_hTermWnd, WM_COMMNOTIFY, EV_RXCHAR, 
            0);</p>
          <p align="JUSTIFY">continue;</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">dwMask=0;</p>
          <p align="JUSTIFY">if(!WaitCommEvent(pDoc-&gt;m_hCom, &amp;dwMask, &amp;os)) 
            // 重叠操作</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">if(GetLastError()==ERROR_IO_PENDING)</p>
          <p align="JUSTIFY">// 无限等待重叠操作结果</p>
          <p align="JUSTIFY">GetOverlappedResult(pDoc-&gt;m_hCom, &amp;os, &amp;dwTrans, 
            TRUE);</p>
          <p align="JUSTIFY">else</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">CloseHandle(os.hEvent);</p>
          <p align="JUSTIFY">return (UINT)-1;</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">CloseHandle(os.hEvent);</p>
          <p align="JUSTIFY">return 0;</p>
          <p align="JUSTIFY">}</p>
          </b> 
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">BOOL CTerminalDoc::CanCloseFrame(CFrameWnd* pFrame) 
          </p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">// TODO: Add your specialized code here and/or call 
            the base class</p>
          <p><b> </b></p>
          <b> 
          <p align="JUSTIFY">SetModifiedFlag(FALSE); // 将文档的修改标志设置成未修改</p>
          </b> 
          <p align="JUSTIFY">return CDocument::CanCloseFrame(pFrame);</p>
          <p align="JUSTIFY">}</p>
          <p>  毫无疑问,CTerminalDoc类是研究重点。该类负责Terminal的通信任务,主要包括设置通信参数、打开和关闭串行口、建立和终止辅助工作线程、用辅助线程监视串行口等等。</p>
          <b></b> 
          <p align="JUSTIFY">  在CTerminalDoc类的头文件中,有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。</p>
          <p align="JUSTIFY">  成员m_bConnected用来表明当前是否存在一个通信连接。m_hTermWnd用来保存是视图的窗口句柄。m_hPostMsgEvent事件对象用于WM_COMMNOTIFY消息的允许和禁止。m_pThread用来指向AfxBeginThread创建的CWinThread对象,以便对线程进行控制。OVERLAPPED结构m_osRead和m_osWrite用于串行口的重叠读/写,程序应该为它们的hEvent成员创建事件句柄。</p>
          <p align="JUSTIFY">  CTerminalDoc类的构造函数主要完成一些通信参数的初始化工作。OnNewDocument成员函数创建了三个事件对象,CTerminalDoc的析构函数关闭串行口并删除事件对象句柄。</p>
          <p align="JUSTIFY">  OnFileSettings是File-&gt;Settings...的命令处理函数,该函数弹出一个CSetupDlg对话框来设置通信参数。实际的设置工作由ConfigConnection函数完成,在OpenConnection和OnFileSettings中都会调用该函数。</p>
          <p align="JUSTIFY">  OpenConnection负责打开串行口并建立辅助工作线程,当用户选择了File-&gt;Connect命令时,消息处理函数OnFileConnect将调用该函数。该函数调用CreateFile以重叠方式打开指定的串行口并把返回的句柄保存在m_hCom成员中。接着,函数对m_hCom通信设备进行各种设置。需要注意的是对超时的设定,将读间隔超时设置为MAXDWORD并使其它读超时参数为0会导致ReadFile函数立即完成操作并返回,而不管读入了多少字符。设置超时就规定了GetOverlappedResult函数的等待时间,因此有必要将写超时设置成适当的值,这样如果不能完成写串口的任务,GetOverlappedResult函数会在超过规定超时后结束等待并报告实际传输的字符数。</p>
          <p align="JUSTIFY">  如果对m_hCom设置成功,则函数会建立一个辅助线程并暂时将其挂起。在最后,调用CWinThread:: 
            ResumeThread使线程开始运行。</p>
          <p align="JUSTIFY">  OpenConnection调用成功后,线程函数CommProc就开始工作。该函数的主体是一个while循环,在该循环内,混合了两种方法监视串行口输入的方法。先是调用ClearCommError函数查询输入缓冲区中是否有字符,如果有,就向视图发送WM_COMMNOTIFY消息通知其接收字符。如果没有,则调用WaitCommEvent函数监视EV_RXCHAR通信事件,该函数执行重叠操作,紧接着调用的GetOverlappedResult函数无限等待通信事件,如果EV_RXCHAR事件发生(串口收到字符并放入输入缓冲区中),那么函数就结束等待。</p>
          <p align="JUSTIFY">  上述两种方法的混合使用兼顾了线程的效率和可靠性。如果只用ClearCommError函数,则辅助线程将不断耗费CPU时间来查询,效率较低。如果只用WaitCommEvent来监视,那么由于该函数对输入缓冲区中已有的字符不会产生EV_RXCHAR事件,因此在通信速率较高时,会造成数据的延误和丢失。</p>
          <p align="JUSTIFY">  注意到辅助线程用m_PostMsgEvent事件对象来同步WM_COMMNOTIFY消息的发送。在发送消息之前,WaitForSingleObject函数无限等待m_PostMsgEvent对象,WM_COMMNOTIFY的消息处理函数CTerminalView::OnCommNotify在返回时会把该对象置为有信号,因此,如果WaitForSingleObject函数返回,则说明上一个WM_COMMNOTIFY消息已被处理完,这时才能发下一个消息,在发消息前还要调用ResetEvent把m_PostMsgEvent对象置为无信号的,以供下次使用。</p>
          <p align="JUSTIFY">  由于PostMessage函数在消息队列中放入消息后会立即返回,所以如果不采取上述措施,那么辅助线程可能在主线程未处理之前重复发出WM_COMMNOTIFY消息,这会降低系统的效率。</p>
          <p align="JUSTIFY">  可能有读者会问,为什么不用SendMessage?该函数在发送的消息被处理完毕后才返回,这样不就不用考虑同步问题了吗?是的,本例中也可以使用SendMessage,但该函数会阻塞辅助线程的执行直到消息处理完毕,这会降低效率。如果用PostMessage,那么在函数立即返回后线程还可以干别的事情,因此,考虑到效率问题,这里使用了PostMessage而不是SendMessage。</p>
          <p align="JUSTIFY">  函数ReadComm和WriteComm分别用来从m_hCom通信设备中读/写指定数量的字符。ReadComm函数很简单,由于对读超时的特殊设定,ReadFile函数会立即返回并完成操作,并在length变量中报告实际读入的字符数。此时,没有必要调用等待函数或GetOverlappedResult。在WriteComm中,调用GerOverlappedResult来等待操作结果,直到超时发生。不管是否超时,该函数在结束等待后都会报告实际的传输字符数。</p>
          <p align="JUSTIFY">  CloseConnection函数的主要任务是终止辅助线程并关闭m_hCom通信设备。为了终止线程,该函数设置了一系列信号,以结束辅助线程中的等待和循环,然后调用WaitForSingleObject等待线程结束。</p>
          <b> 
          <p align="JUSTIFY">清单12.7 CTerminalView类的部分代码</p>
          </b> 
          <p align="JUSTIFY">// TerminalView.h : interface of the CTerminalView 
            class</p>
          <p align="JUSTIFY">/////////////////////////////////////////////////////////////////////////////</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">class CTerminalView : public CEditView</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY"><b>. . .</b></p>
          <b> 
          <p align="JUSTIFY">afx_msg LRESULT OnCommNotify(WPARAM wParam, LPARAM 
            lParam);</p>
          </b> 
          <p align="JUSTIFY">DECLARE_MESSAGE_MAP()</p>
          <p align="JUSTIFY">};</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">// TerminalView.cpp : implementation of the CTerminalView 
            class</p>
          <p align="JUSTIFY">//</p>
          <p align="JUSTIFY">BEGIN_MESSAGE_MAP(CTerminalView, CEditView)</p>
          <p align="JUSTIFY"><b>. . .</b></p>
          <b> 
          <p align="JUSTIFY">ON_MESSAGE(WM_COMMNOTIFY, OnCommNotify)</p>
          </b> 
          <p align="JUSTIFY">END_MESSAGE_MAP()</p>
          <p align="JUSTIFY"> </p>
          <p><b> </b></p>
          <b> 
          <p align="JUSTIFY">LRESULT CTerminalView::OnCommNotify(WPARAM wParam, 
            LPARAM lParam)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">char buf[MAXBLOCK/4];</p>
          <p align="JUSTIFY">CString str;</p>
          <p align="JUSTIFY">int nLength, nTextLength;</p>
          <p align="JUSTIFY">CTerminalDoc* pDoc=GetDocument();</p>
          <p align="JUSTIFY">CEdit&amp; edit=GetEditCtrl();</p>
          <p align="JUSTIFY">if(!pDoc-&gt;m_bConnected || </p>
          <p align="JUSTIFY">(wParam &amp; EV_RXCHAR)!=EV_RXCHAR) // 是否是EV_RXCHAR事件?</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">SetEvent(pDoc-&gt;m_hPostMsgEvent); // 允许发送下一个WM_COMMNOTIFY消息</p>
          <p align="JUSTIFY">return 0L;</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">nLength=pDoc-&gt;ReadComm(buf,100);</p>
          <p align="JUSTIFY">if(nLength)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">nTextLength=edit.GetWindowTextLength();</p>
          <p align="JUSTIFY">edit.SetSel(nTextLength,nTextLength); //移动插入光标到正文末尾</p>
          <p align="JUSTIFY">for(int i=0;i&lt;nLength;i++)</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">switch(buf[i])</p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">case '\r': // 回车</p>
          <p align="JUSTIFY">if(!pDoc-&gt;m_bNewLine) </p>
          <p align="JUSTIFY">break;</p>
          <p align="JUSTIFY">case '\n': // 换行</p>
          <p align="JUSTIFY">str+=&quot;\r\n&quot;;</p>
          <p align="JUSTIFY">break;</p>
          <p align="JUSTIFY">case '\b': // 退格</p>
          <p align="JUSTIFY">edit.SetSel(-1, 0);</p>
          <p align="JUSTIFY">edit.ReplaceSel(str);</p>
          <p align="JUSTIFY">nTextLength=edit.GetWindowTextLength();</p>
          <p align="JUSTIFY">edit.SetSel(nTextLength-1,nTextLength);</p>
          <p align="JUSTIFY">edit.ReplaceSel(&quot;&quot;); //回退一个字符</p>
          <p align="JUSTIFY">str=&quot;&quot;;</p>
          <p align="JUSTIFY">break;</p>
          <p align="JUSTIFY">case '\a': // 振铃 </p>
          <p align="JUSTIFY">MessageBeep((UINT)-1);</p>
          <p align="JUSTIFY">break;</p>
          <p align="JUSTIFY">default : </p>
          <p align="JUSTIFY">str+=buf[i];</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">edit.SetSel(-1, 0);</p>
          <p align="JUSTIFY">edit.ReplaceSel(str); // 向编辑视图中插入收到的字符</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">SetEvent(pDoc-&gt;m_hPostMsgEvent); // 允许发送下一个WM_COMMNOTIFY消息</p>
          <p align="JUSTIFY">return 0L;</p>
          <p align="JUSTIFY">}</p>
          </b> 
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">void CTerminalView::OnChar(UINT nChar, UINT nRepCnt, 
            UINT nFlags) </p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">// TODO: Add your message handler code here and/or 
            call default</p>
          <p><b> </b></p>
          <b> 
          <p align="JUSTIFY">CTerminalDoc* pDoc=GetDocument();</p>
          <p align="JUSTIFY">char c=(char)nChar;</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">if(!pDoc-&gt;m_bConnected)return;</p>
          <p align="JUSTIFY">pDoc-&gt;WriteComm(&amp;c, 1);</p>
          <p align="JUSTIFY">if(pDoc-&gt;m_bEcho) </p>
          </b> 
          <p align="JUSTIFY">CEditView::OnChar(nChar, nRepCnt, nFlags); // 本地回显</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY">  CTerminalView是CEditView的派生类,利用CEditView的编辑功能,可以大大简化程序的设计。</p>
          <p align="JUSTIFY">  OnChar函数对WM_CHAR消息进行处理,它调用CTerminalDoc::WriteComm把用户键入的字符从串行口输出。如果设置了Local 
            echo,那么就调用CEditView::OnChar把字符输出到视图中。</p>
          <p align="JUSTIFY">  OnCommNotify是WM_COMMNOTIFY消息的处理函数。该函数调用CTerminalDoc::ReadComm从串行口输入缓冲区中读入字符并把它们输出到编辑视图中。在输出前,函数会对一些特殊字符进行处理。如果读者对控制编辑视图的代码不太明白,那么请参见6.1.4。在函数返回时,要调用SetEvent把m_hPostMsgEvent置为有信号。</p>
          <b> 
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">清单12.8 CSetupDlg类的部分代码</p>
          </b> 
          <p align="JUSTIFY">// SetupDlg.h : header file</p>
          <p align="JUSTIFY">//</p>
          <p align="JUSTIFY">class CSetupDlg : public CDialog</p>
          <p align="JUSTIFY">{</p>
          <p><b> </b></p>
          <b> 
          <p align="JUSTIFY">. . .</p>
          <p align="JUSTIFY">public:</p>
          <p align="JUSTIFY">BOOL m_bConnected;</p>
          <p align="JUSTIFY">. . .</p>
          </b> 
          <p align="JUSTIFY">};</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">// SetupDlg.cpp : implementation file</p>
          <p align="JUSTIFY">//</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">BOOL CSetupDlg::OnInitDialog() </p>
          <p align="JUSTIFY">{</p>
          <p align="JUSTIFY">CDialog::OnInitDialog();</p>
          <p align="JUSTIFY"> </p>
          <p align="JUSTIFY">// TODO: Add extra initialization here</p>
          <p><b> </b></p>
          <b> 
          <p align="JUSTIFY">GetDlgItem(IDC_PORT)-&gt;EnableWindow(!m_bConnected);</p>
          </b> 
          <p align="JUSTIFY">return TRUE; // return TRUE unless you set the focus 
            to a control</p>
          <p align="JUSTIFY">// EXCEPTION: OCX Property Pages should return FALSE</p>
          <p align="JUSTIFY">}</p>
          <p align="JUSTIFY"><b></b>  CSetupDlg的主要任务是配置通信参数。在OnInitDialog函数中,要根据当前是否连接来允许/禁止Port组合框。因为在打开一个连接后,显然不能随便改变端口。</p>
          <div align="center">
            <center>
              <table border="0" cellpadding="0" cellspacing="0" width="615">
                <tr> 
                  <td><a href="chap12_3.htm">上一页</a></td>
                  <td>
                    <p align="right"><a href="chap12_5.htm">下一页</a>
                  </td>
                </tr>
              </table>
              <p><a href="http://www.cpcw.com">电脑报首页</a> <a href="../../index.htm">网络学院首页</a></p>
            </center>
          </div>
          <font size="5">
          <hr noshade color="#3973DE" size="1">
          </font>
          <p align="center"><font size="5"></font><font size="2" color="#000000">本教程由<a href="http://vcdynasty.yeah.net">Visual 
            C++王朝(Where programmers come together)</a>协助制作<br>
            未经许可,请勿以任何形式复制</font>
        </td>
      </tr>
    </table>
    
  </center>
</div>

</body>
</html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -