12. 4 一个通信演示程序
为了使读者更好地掌握本章的概念,这里举一个具体实例来说明问题。如图
12.1 所示,例子程序名为 Terminal,是一个简单的 TTY 终端仿真程序。读者可
以用该程序打开一个串行口,该程序会把用户的键盘输入发送给串行口,并把从
串口接收到的字符显示在视图中。用户通过选择 File->Connect 命令来打开串行
口,选择 File->Disconnect 命令则关闭串行口。
图 12.1 Terminal 终端仿真程序
当用户选择 File->Settings...命令时,会弹出一个 Communication
settings 对话框,如图 12.2 所示。该对话框主要用来设置串行口,包括端口、
波特率、每字节位数、校验、停止位数和流控制。
图 12.2 Communication settings 对话框
通过该对话框也可以设置 TTY 终端仿真的属性,如果选择 New Line(自动换
行),那么每当从串口读到回车符(‘\r’)时,视图中的正文就会换行,否则,
只有在读到换行符(‘\n’)时才会换行。如果选择 Local echo(本地回显),那
么发送的字符会在视图中显示出来。
终端仿真程序的特点是数据的传输没有规律。因为键盘输入速度有限,所以
发送的数据量较小,但接收的数据源是不确定的,所以有可能会有大量数据高速
涌入的情况发生。根据 Terminal 的这些特性,我们在程序中创建了一个辅助工
作者线程专门来监视串行口的输入。由于写入串行口的数据量不大,不会太费时,
所以在主线程中完成写端口的任务是可以的,不必另外创建线程。
现在就让我们开始工作。请读者按下面几步进行:
用 AppWizard 建立一个名为 Terminal 的 MFC 应用程序。在 MFC AppWizard 对话
框的第 1 步选择 Single document,在第 4 步去掉 Docking toolbar 的选择,在
第 6 步把 CTerminalView 的基类改为 CEditView。
在 Terminal 工程的资源视图中打开 IDR_MAINFRAME 菜单资源。去掉 Edit 菜单和
View 菜单,并去掉 File 菜单中除 Exit 以外的所有菜单项。然后在 File 菜单中
加入三个菜单项,如表 12.5 所示。
表 12.5 新菜单项
标题
ID
Settings...
ID_FILE_SETTINGS
Connect
ID_FILE_CONNECT
Disconnect
ID_FILE_DISCONNECT
用 ClassWizard 为 CTerminalDoc 类创建三个与上表菜单消息对应的命令处理函
数,使用缺省的函数名。为 ID_FILE_CONNECT 和 ID_FILE_DISCONNECT 命令创建
命令更新处理函数。另外,用 ClassWizard 为该类加入 CanCloseFrame 成员函数。
用 ClassWizard 为 CTerminalView 类创建 OnChar 函数,该函数用来把用户键入
的字符向串行口输出。
新建一个对话框模板资源,令其 ID 为 IDD_COMSETTINGS。请按图 12.2 和表 12.6
设计对话框模板。
表 12.6 通信设置对话框中的主要控件
控件
ID
属性设置
Base options 组框
缺省
标题为 Base options
Port 组合框
IDC_PORT
Drop List,不选 Sort,初始列表
为 COM1、COM2、COM3、COM4
Baud rate 组合框
IDC_BAUD
Drop List,不选 Sort,初始列表
为 300、600、1200、2400、9600、
14400、19200、38400、57600
Data bits 组合框
IDC_DATABITS
Drop List,不选 Sort,初列表为
5、6、7、8
Parity 组合框
IDC_PARITY
Drop List,不选 Sort,初列表为
None、Even、Odd
Stop bits 组合框
IDC_STOPBITS
Drop List,不选 Sort,初列表为
1、1.5、2
Flow control 组框
缺省
标题为 Flow control
None 单选按钮
IDC_FLOWCTRL
标题为 None,选择 Group 属性
RTS/CTS 单选按钮
缺省
标题为 RTS/CTS
XON/XOFF 单选按钮
缺省
标题为 XON/XOFF
TTY options 组框
缺省
标题为 TTY options
New line 检查框
IDC_NEWLINE
标题为 New line
Local echo 检查框
IDC_ECHO
标题为 Local echo
打开 ClassWizard,为 IDD_COMSETTINGS 模板创建一个名为 CSetupDlg 的对话框
类。为该类加入 OnInitDialog 成员函数,并按表 12.7 加入数据成员。
表 12.7 CSetupDlg 类的数据成员
控件 ID
变量名
数据类型
IDC_BAND
m_sBaud
CString
IDC_DATABITS
m_sDataBits
CString
IDC_ECHO
m_bEcho
BOOL
IDC_FLOWCTRL
m_nFlowCtrl
int
IDC_NEWLINE
m_bNewLine
BOOL
IDC_PARITY
m_nParity
int
IDC_PORT
m_sPort
CString
IDC_STOPBITS
m_nStopBits
int
按清单 12.6、12.7 和 12.8 修改程序。清单 12.6 列出了 CTerminalDoc 类的部分
代码,清单 12.7 是 CTerminalView 的部分代码,清单 12.8 是 CSetupDlg 类的部
分代码。在本例中使用了 WM_COMMNOTIFY 消息。虽然在 Win32 中,WM_COMMNOTIFY
消息已经取消,系统自己不会产生该消息,但 Visual C++对该消息的定义依然保
留。考虑到使用习惯,Terminal 程序辅助线程通过发送该消息来通知视图有通
信事件发生。
清单 12.6 CTerminalDoc 类的部分代码
// TerminalDoc.h : interface of the CTerminalDoc class
//
/////////////////////////////////////////////////////////////////////
////////
#define MAXBLOCK 2048
#define XON 0x11
#define XOFF 0x13
UINT CommProc(LPVOID pParam);
class CTerminalDoc : public CDocument
{
protected: // create from serialization only
CTerminalDoc();
DECLARE_DYNCREATE(CTerminalDoc)
// Attributes
public:
CWinThread* m_pThread; // 代表辅助线程
volatile BOOL m_bConnected;
volatile HWND m_hTermWnd;
volatile HANDLE m_hPostMsgEvent; // 用于 WM_COMMNOTIFY 消息的事件对象
OVERLAPPED m_osRead, m_osWrite; // 用于重叠读/写
volatile HANDLE m_hCom; // 串行口句柄
int m_nBaud;
int m_nDataBits;
BOOL m_bEcho;
int m_nFlowCtrl;
BOOL m_bNewLine;
int m_nParity;
CString m_sPort;
int m_nStopBits;