在Visual C++ 6.0中使用串行通讯控件
发表于:2007-07-14来源:作者:点击数:
标签:
山东科技大学智能工程研究所 杨在春 最近,在互联网上发现许多Visual C++爱好者求助如何使用MSComm串行通讯控件,笔者也读到过一些关于使用MSComm通讯控件的文章,大部分只是从表面上泛泛地做了介绍,没有涉及到真正的程序实现,读者仍然无法使通讯控件工作
山东科技大学智能工程研究所 杨在春
最近,在互联网上发现许多Visual C++爱好者求助如何使用MSComm串行通讯控件,笔者也读到过一些关于使用MSComm通讯控件的文章,大部分只是从表面上泛泛地做了介绍,没有涉及到真正的程序实现,读者仍然无法使通讯控件工作起来。其实,在VC++6.0中使用通讯控件,还必须了解COleVari
ant与动态数组等一些
知识。现在我单位正与济宁二号煤矿合作
开发“井下采区变电所微机集中监控系统”,我所选用的开发环境即为Visual C++6.0,其中的通讯功能都是由MSComm通讯控件完成的,本人希望能通过本文将在程序开发过程中的一些体会和心得奉献给广大读者。
在Visual C++6.0中使用串行通讯控件,至少应该从以下几个方面进行考虑:
1.如何引入通讯控件
如果使用的是其他可视化程度较高编程语言,如
VB、C++ Builder或Delphi等,通讯控件可以被直接引入到任意某个窗体之中,而使用Visual C++时,只有通过对话框资源编辑器来引入通讯控件到对话框中或由CFormView派生而来的视图中。如果选择的对话框是模态的,则在使用通讯控件时必须打开该对话框,这对于基于对话框的应用程序使用十分方便。如果不想使用对话框的话,则可将程序的视图类设计为由CFormView派生而来的视图。CFormView与相应的对话框资源相关联,具有很多对话框的特点,这样,我们可以将通讯控件直接引入到程序视图之中。后面这种方法的缺点是视图的使用受到了很大的限制。下面介绍另外一种使用比较方便,且更加有效的方法。
同样,我们还是将通讯控件引入到对话框中,只是将对话框做成非模态的。如果没有其他需要的话,对话框中只包含通讯控件一个元素,且在程序执行时,该对话框始终处于不可视状态。设计时程序中任何地方,只要能访问到该对话框,就可使用其中的通讯控件。下面介绍非模态对话框
CCommDlg的制作及其使用方法,CCommDlg由CDialog派生而来。由于在程序执行过程中对话框是不可视的,用户不可能对其进行任何操作,因此不需要处理对话框的确定和取消按钮的响应消息。为了方便起见,只需向其中增加一个BOOL类型的成员函数Create(),定义如下:
BOOL CCommDlg::Create()
{
return CDialog::Create(CCommDlg::IDD);
}
另外,由于对话框是不可视的,必须通过对话框资源编辑器将其Visible属性设置掉。
由于主框架窗口CMainFrame是程序的主窗口,程序其他部分的代码很容易通过函数AfxGetMainWnd或其他方法访问到CMainFrame类型的成员,并程序的主窗口的C++对象和其窗口对象在程序的运行过程中总是有效的,直到程序运行结束才将其释放和析够。因此,我们可以在CMainFrame中增加一个指向CCommDlg类型的指针变量:CCommDlg * m_pCommDlg,在CMainFrame的OnCreate成员函数构造并创建该对话框,代码如下所示:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
……
m_pCommDlg = new CCommDlg();
if(!m_pCommDlg)return -1;
m_pCommDlg->Create();
……
}
m_pCommDlg对象创建成功后,其C++对象和窗口对象一直处于有效状态,直到程序结束时,才用下面的代码在CMainFrame的析够函数中将其释放:
CMainFrame::~CMainFrame()
{
if(m_pCommDlg)
{
if(m_pCommDlg->GetSafeHwnd())
m_pCommDlg->DestroyWindow();
delete m_pCommDlg;
}
……
}
这样,在程序的执行过程中,我们就可以通过访问m_pCommDlg来访问通讯控件m_Comm,比如程序语句:m_pCommDlg->m_Comm.SetPortOpen(true)可以用来打开通讯端口。注意,上述存放通讯控件的对话框是不可视的,若想使其成为可视的,并能够完成一些如设置通讯控件的功能的话,还需要做一些额外的工作。
2.发送通讯数据
在Visual C++ 中,通讯控件发送数据的任务是由其接口成员函数SetOutput来实现的,该函数使用的参数是const VARIANT &newValue。VARIANT其实是一个C++结构类型,COleVariant类型对VARIANT做了进一步的封装和扩展,提供了许多新的功能和操作方法,支持OLE自动化,且更容易向其数据成员填入数据。由于COleVariant类型由VARIANT派生而来,因此将COleVariant类型的变量传递给SetOutput函数更为方便。另外,SetOutput的参数newValue类型必须是存放字节类型数据的动态数组。因此,可以利用Visual C++提供CByteArray类型来构造COleVariant类型的对象,并将其传递给SetOutput函数。对CByteArray类型变量的操作相对来说要容易的多,比如其成员函数SetSize可用来设置动态数组的大小,下标操作符[]可用来为其元素赋值等等。下面的程序代码可实现将存放在缓冲区strBuf中的100个字节的数据通过通讯控件发送出去:
……
BYTE strBuf[128];
CByteArray OutBuf;
COleVariant varOutput;
……
OutBuf.SetSize(100);
for(i=0;i<100;i++)OutBuf[i] = strBuf[i];
varOutput = OutBuf;
m_pCommDlg->m_Comm.SetOutput(varOutput);
……
利用通讯控件发送数据的关键在于构造COleVariant类型的变量,并向其中填入通讯数据,使其能满足通讯控件的成员函数SetOutput的需要。上面的程序语句varOutput = OutBuf可以直接写成:
COleVariant varOutput(OutBuf);
但这样必须将变量varOutput的定义语句COleVariant varOutput删除掉。
3.接收通讯数据
通讯控件接收通讯数据的任务是通过其接口成员函数GetInput来实现的,该函数的返回值的类型为VARIANT,同样我们可以将其赋值给COleVariant类型的变量。进一步,我们必须知道,该函数返回值变量的有效成员为parray,其类型定义为SAFEARRAY FAR*,即parray为一个
安全数组指针,且数组的元素类型为BYTE。因此,对GetInput函数返回值的访问,可以通过安全数组操作函数来实现,比如:读取数组的维数、下边界、上边界以及各个元素的实际的值等。该返回值变量所对应的安全数组的维数为1,下边界为0。根据上述分析,下面的程序代码基本能够实现利用通讯控件接收通讯数据:
CByteArray ByteBuf;
COleVariant varInput;
long ix,l,u;
BYTE bit;
……
varInput = m_pCommDlg->m_Comm.GetInput();
if(varInput.parray!=NULL)
{
SafeArrayGetLBound(varInput.parray,1,&l);
SafeArrayGetUBound(varInput.parray,1,&u);
for(ix=l;ix<=u;ix++)
{
SafeArrayGetElement(varInput.parray,&ix,&bit);
ByteBuf.Add(bit);
}
}
……
4.小结
本文讲述的利用通讯控件发送与接收通讯数据都是基于二进制来实现的,基于文本的情况基本与此类似。要想利用好通讯控件还有其他一些应该注意的地方,比如波特率的设置、接收与发送缓冲区的设置以及通讯过程中的延时问题的处理等等。
原文转自:http://www.ltesting.net