我们知道,Flash可以做出很炫很酷的界面,且都是矢量图形,所以我们这里可以通过Active X控件shockwave Flash object将Flash嵌入到VC中,用控件与VC接口对Flash进行操作,如实时读取数据并作图类似的程序。曾经想用Flash连接串口读取数据,但Flash根本不能与串口连接,只好用Flash做用户交互界面,用VC做后台计算,实时通知Flash绘制实时曲线,走曲线救国的路径。( 注意,重要算法不要写到Flash中,Flash极易破解,即使用swf encrypt加密也敌不过action script viewer,用sothink flash decompile虽破解功夫不如ASV,但也能让破解者看透算法。尤其你想保护你的核心算法,所以仅仅用Flash做用户界面即可。)
使用微软的串口封装类,实现对串口操作(读写)很是方便,API简单明了。微软的串口类CSerial定义如下:
// Serial.h
#ifndef __SERIAL_H__
#define __SERIAL_H__
#define FC_DTRDSR 0x01
#define FC_RTSCTS 0x02
#define FC_XONXOFF 0x04
#define ASCII_BEL 0x07
#define ASCII_BS 0x08
#define ASCII_LF 0x0A
#define ASCII_CR 0x0D
#define ASCII_XON 0x11
#define ASCII_XOFF 0x13
class CSerial
{
public:
CSerial();
~CSerial();
BOOL Open(int nPort=2, int nBaud=9600);
BOOL Close(void);
int ReadData(void*,int);
int SendData(const char *,int);
int ReadDataWaiting(void);
BOOL IsOpened(void){return(m_bOpened);}
protected:
BOOL WriteCommByte(unsigned char);
HANDLE m_hIDComDev;
OVERLAPPED m_OverlappedRead, m_OverlappedWrite;
BOOL m_bOpened;
};
#endif
虽然看不懂其内部如何操作硬件,但封装好的类就是好用,实现如下:
// Serial.cpp
#include "stdafx.h"
#include "CSerial.h"
//CSerial::CSerial是类构造函数,不带参数,
//负责初始化所有类成员变量
CSerial::CSerial()
{
memset(&m_OverlappedRead,0,sizeof(OVERLAPPED));
memset(&m_OverlappedWrite,0,sizeof(OVERLAPPED));
m_hIDComDev=NULL;
m_bOpened=FALSE;
}
CSerial::~CSerial()
{
Close();
}
//CSerial::Open是打开通信端口的成员函数,带两个参数,
//一个是串行端口号,另一个是波特率。
BOOL CSerial::Open(int nPort,int nBaud)
{
if(m_bOpened) return(TRUE);
char szPort[15];
char szComParams[50];
DCB dcb;
wsprintf(szPort,"COM%d",nPort);
m_hIDComDev=CreateFile(szPort,GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
if(m_hIDComDev==NULL) return(FALSE);
memset(&m_OverlappedRead,0,sizeof(OVERLAPPED));
memset(&m_OverlappedWrite,0,sizeof(OVERLAPPED));
COMMTIMEOUTS CommTimeOuts;
CommTimeOuts.ReadIntervalTimeout=0xFFFFFFFF;
CommTimeOuts.ReadTotalTimeoutMultiplier=0;
CommTimeOuts.ReadTotalTimeoutConstant=0;
CommTimeOuts.WriteTotalTimeoutMultiplier=0;
CommTimeOuts.WriteTotalTimeoutConstant=5000;
SetCommTimeouts(m_hIDComDev,&CommTimeOuts);
wsprintf(szComParams,"COM%d:%d,n,8,1",nPort,nBaud);
m_OverlappedRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
m_OverlappedWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
dcb.DCBlength=sizeof(DCB);
GetCommState(m_hIDComDev,&dcb);
dcb.BaudRate=nBaud;
dcb.ByteSize=8;
unsigned char ucSet;
ucSet=(unsigned char)((FC_RTSCTS&FC_DTRDSR)!=0);
ucSet=(unsigned char)((FC_RTSCTS&FC_RTSCTS)!=0);
ucSet=(unsigned char)((FC_RTSCTS&FC_XONXOFF)!=0);
if(!SetCommState(m_hIDComDev,&dcb)||
!SetupComm( m_hIDComDev,10000,10000)||
m_OverlappedRead.hEvent==NULL||
m_OverlappedWrite.hEvent==NULL)
{
DWORD dwError=GetLastError();
if(m_OverlappedRead.hEvent!=NULL) CloseHandle(m_OverlappedRead.hEvent);
if(m_OverlappedWrite.hEvent!=NULL) CloseHandle(m_OverlappedWrite.hEvent);
CloseHandle(m_hIDComDev);
return(FALSE);
}
m_bOpened=TRUE;
return(m_bOpened);
}
//CSerial::Close是关闭通信端口的成员函数。类析构函数调用这个函数,
//因此可不用显式调用这个函数
BOOL CSerial::Close(void)
{
if(!m_bOpened||m_hIDComDev==NULL)
return( TRUE );
if( m_OverlappedRead.hEvent!=NULL)
CloseHandle(m_OverlappedRead.hEvent);
if( m_OverlappedWrite.hEvent!=NULL)
CloseHandle(m_OverlappedWrite.hEvent);
CloseHandle(m_hIDComDev);
m_bOpened=FALSE;
m_hIDComDev=NULL;
return(TRUE);
}
//++///
BOOL CSerial::WriteCommByte(unsigned char ucByte)
{
BOOL bWriteStat;
DWORD dwBytesWritten;
bWriteStat=WriteFile(m_hIDComDev,
(LPSTR)&ucByte,
1,
&dwBytesWritten,
&m_OverlappedWrite);
if(!bWriteStat&&(GetLastError()==ERROR_IO_PENDING))
{
if(WaitForSingleObject(m_OverlappedWrite.hEvent,1000))
dwBytesWritten=0;
else
{
GetOverlappedResult(m_hIDComDev,&m_OverlappedWrite,&dwBytesWritten,FALSE);
m_OverlappedWrite.Offset+=dwBytesWritten;
}
}
return(TRUE);
}
/*CSerial::ReadData函数从端口接收缓冲区读入数据。第一个参数是缓冲区指针,
第二个参数是个整数值,给出缓冲区的大小*/
int CSerial::SendData(const char *buffer,int size)
{
if(!m_bOpened||m_hIDComDev==NULL)
return(0);
DWORD dwBytesWritten=0;
int i;
for(i=0;i<size;i++)
{
WriteCommByte(buffer[i]);
dwBytesWritten++;
}
return((int)dwBytesWritten);
}
/*CSerial::ReadDataWaiting函数返回等待在
通信端口缓冲区中的数据,不带参数*/
int CSerial::ReadDataWaiting(void)
{
if(!m_bOpened||m_hIDComDev==NULL)
return(0);
DWORD dwErrorFlags;
COMSTAT ComStat;
ClearCommError(m_hIDComDev,&dwErrorFlags,&ComStat);
return((int)ComStat.cbInQue);
}
/*CSerial::ReadData函数从端口接收缓冲区读入数据。第一个参数
是缓冲区指针,第二个参数是个整数值,给出缓冲区的大小*/
int CSerial::ReadData(void*buffer,int limit)
{
if(!m_bOpened||m_hIDComDev==NULL)
return(0);
BOOL bReadStatus;
DWORD dwBytesRead, dwErrorFlags;
COMSTAT ComStat;
ClearCommError(m_hIDComDev,&dwErrorFlags,&ComStat);
if(!ComStat.cbInQue)
return(0);
dwBytesRead=(DWORD)ComStat.cbInQue;
if(limit<(int)dwBytesRead)
dwBytesRead=(DWORD)limit;
bReadStatus=ReadFile(m_hIDComDev,
buffer,
dwBytesRead,
&dwBytesRead,
&m_OverlappedRead);
if(!bReadStatus)
{
if(GetLastError()==ERROR_IO_PENDING)
{
WaitForSingleObject(m_OverlappedRead.hEvent,2000);
return((int)dwBytesRead);
}
return(0);
}
return((int)dwBytesRead);
}
强烈建议开启一个线程,监视串口数据,如此便可实现Active X控件MSComm的监听功能。串口使用很简单,初始化串口(设置波特率,串口号等);初始化成功后就可以读写串口了:
if(SerialPort.IsOpened());
SerialPort.Close();
if(!SerialPort.Open(Port,Baud))
{
AfxMessageBox("无法打开计算机串口,程序将退出!!\r\n请检查串口连接或配置是否正常!!");
exit(0);
}
char buffer[1024];
SerialPort.ReadData(buffer,1024);
本人年轻时,为公司制作大量如此的串口应用类程序,虽无技术亮点,但尚能勉强稳定运行于工业现场;然最大遗憾是经常根据公司上级领导指示修改串口接收数据,深知实属不该。
如果你想美化VC程序界面,用Flash是不错的选择,至于美化的多好,就看你的Flash制作水平啦。除Flash 8和Flash CS3外还有好多Flash工具可用,生成简单美观的Flash界面,呵呵! 用Flash8做很方便,VC直接控制Flash,但是变量获取算法不当可能不及时得到更新。用FlashCS3虽稍微繁琐,但很容易使Flash与VC交互通信,比JavaScript麻烦点,要有一点点XML的基础,强烈推荐使用AS3与VC通信,毕竟AS1/2过时啦,属于低级脚本语言。AS3强大,如日中天,且有N多第三方类库API,所以应该尽量使用AS3,其与后台的SOCKET通信也较为方便,适用于大量数据交换的情形。AS3与VC编程,也许会遇到很多BUG,只要肯动脑子,就难不倒。 再者Flex 3.0内置了图表组件外,其它组件美观是FLASH CS3不能比的,用Flex作图美观就是灵活性差了点,生成的swf文件也较大。
如果只做简单图表推荐用Flex 3.0,其中最大问题就是如何让Flex读取外部实时数据(xml格式)转换为arrayCollection,这种方法在网上传滥了,屡试不爽。如果需要传输大量XML数据,建议使用XMLSocket传递数据,用C++做后台。 用Flash绘图另一好处就是不用考虑重绘,对向我一样的VC菜鸟级编程者,绘图本身不难,但重绘就有些繁琐,用Flash就不存在这个问题啦。再者,Flash内核提供较高质量打印服务,不影响VC后台工作。