Archive

Archive for May, 2008

QQ邮箱“全文查找”开始内测了

May 29th, 2008 will 2 comments

刚收到QQ的邮件邀请测试QQ邮箱“全文查找”。信的原文如下:

我们很荣幸地通知您,QQ邮箱“全文查找”功能开始小范围内测了,很高兴能请您参加试用。
“全文查找”功能力求迅速方便地呈现出您需要的内容,我们向您推介它的如下特点:

  1. 可以快速查找邮件全文。不仅可以通过姓名、主题来查询,还可以输入正文中的关键词来查询。查询结果快速显示。
  2. 方便进行“逼近式查找”。在查找结果页面的右侧,直接呈现出各类查找选项,可以更方便更容易的进行精确定位。
  3. 关键字标亮,内容关键字附近的摘要信息直接展示出来。

欢迎您亲自体验。新的查找入口位于QQ邮箱页面右上角,方便随时查找。位置如下图:

温馨提示:

  1. 新的查找功能目前为试运行(仅向5月20日前邮箱等级达到7级的用户开放),期间可能出现新旧版本随时切换的情况。如果您发现新版查找入口不见了,还请见谅,此为内测期间正常现象。此时可以进入“收件箱”或文件夹页面内,使用里面的旧版查找功能)。
  2. 全文搜索内测版暂不支持群邮件和“我的其它邮箱”中的邮件。
  3. 功能细节需要您的意见建议才能更好地完善,欢迎在邮箱论坛里留言,或者写信给searchhelp@qq.com

试用了一下,感觉还不错,确实提高不少效率。这次还改变了“我的其它邮箱”的列表方式,感觉比以前更友好了。但是这个改版貌似将浏览器兼容性变差了。在Opera下浏览,左侧的导航条基本不能使用了。
qqmail_error

Categories: 网络应用 Tags:

调试串口通信程序

May 27th, 2008 will No comments

项目做到最后了,上位机(普通台式计算机)需要接收下位机(实验板)处理过后的数据,由于返回的数据较少,一次可能就是几个字节,传输时间没有严格要求,所以选择了串口通信。串口通信方式简单。调试起来也比较简单。下位机的串口硬件调试是PK同学负责的,我负责上位机的串口程序的调试。

刚开始的调试想法是让计算机COM1口发送数据,然后COM2接收,后来发现计算机就只带有一个串口,那就用邻近同学的计算机发送数据吧,但是这样是无法通信的,因为我的串口线是对等的。后来根据小丫头同学调试串口的经验,让一个串口COM1自发自收,这样就不用考虑对等线还是交叉线的问题了。

调试计算机串口,需要一根串口线,对等线或者交叉线均可。
1、检测串口线是否能连通;检测方法,用万用表检测串口线的1-9号线是否对应连通。
2、检测计算机串口是否能正常工作;检测方法,将串口线一端接入计算机串口,另一端2、3两线用导线连通。下载串口调试助手观察计算机串口的自发自收情况。
3、检测自己程序的正确性;

以下串口类在原始是应用于WINCE下,我稍作修改即可在windows下运行。以下程序在VC2005下编译、测试通过。PSerialPort.cpp增加了TRACE调试的代码。

//From unkown author:unkonwn modified by IndexZ@xinzero.com
//PSerialPort.h
#pragma once
typedef void (*LPDataArriveProc)(char *data,int length,DWORD userdata);
class CPSerialPort
{
public:
	CPSerialPort();
	virtual ~CPSerialPort();
	BOOL OpenPort(LPCTSTR Port,int Baudrate,
		/*int DataBits,int StopBits,int Parity,*/
		LPDataArriveProc proc=NULL,DWORD userdata=0); //打开串口
	BOOL ClosePort();		//关闭串口
	//激活与关闭持续接收
	BOOL Activate();
	BOOL Deactivate();
	BOOL IsActive();
	//接收数据线程函数
	static DWORD WINAPI ReadPortThread(LPVOID lpParameter);
	DWORD ReadPort(char *data,int length);		//读取一定长度的数据
	DWORD WritePort(char *data,int length);		//发送数据
	DWORD WriteFileToPort(LPCTSTR FileName);	//发送文件
private:
	HANDLE m_hComm; //串口设备handle
	HANDLE m_hReadThread; //读串口线程handle
	BOOL m_bReceiving; //是否持续接收
	int m_nBufferSize; //缓冲大小
	char *Buffer; //缓冲区
	LPDataArriveProc m_lpDataArriveProc;
	DWORD m_dwUserData;
	//串口设置以及超时参数
	DCB dcb;
	COMMTIMEOUTS CommTimeOuts;
};
//From unkown author:unkonwn modified by IndexZ@xinzero.com
//PSerialPort.cpp
#include "StdAfx.h"
#include "PSerialPort.h" 

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
////////////////////////////////////////////////////////////////////// 

CPSerialPort::CPSerialPort()
{
	m_hComm = INVALID_HANDLE_VALUE;
	m_hReadThread = NULL;
	m_bReceiving = FALSE;
	m_nBufferSize = 256; //缓冲大小
} 

CPSerialPort::~CPSerialPort()
{
	ClosePort();
} 

DWORD WINAPI CPSerialPort::ReadPortThread(LPVOID lpParameter)
{
	CPSerialPort* m_pSerial;
	m_pSerial=(CPSerialPort*)lpParameter;
	BOOL fReadState;
	DWORD dwLength;
	char* buf=new char[m_pSerial->m_nBufferSize];
	while((m_pSerial->m_hComm!=INVALID_HANDLE_VALUE)
		&&(m_pSerial->m_bReceiving))
	{
		fReadState=ReadFile(m_pSerial->m_hComm,
			buf,m_pSerial->m_nBufferSize,&dwLength,NULL);
		if(!fReadState)
		{
			AfxMessageBox(_T("无法从串口读取数据!"));
		}
		else
		{
			if(dwLength!=0)
			{	//调试输出代码开始
				TRACE("%d\n",dwLength);
				int *pint = (int*)buf;
				for(int i=0;i<(dwLength/(sizeof(int)));i++)
				{
					TRACE("%d\n",pint[i]);
				}//调试输出代码结束
				//回送数据
				if(m_pSerial->m_lpDataArriveProc!=NULL)
				{
					m_pSerial->m_lpDataArriveProc(buf,
							dwLength,m_pSerial->m_dwUserData);
				}
			}
		}
		Sleep(100);		//休眠cpu
	}
	delete[] buf;
	return 0;
} 

BOOL CPSerialPort::OpenPort(LPCTSTR Port,int BaudRate,
			/*int DataBits,int StopBits,int Parity,*/
			LPDataArriveProc proc,DWORD userdata)
{
	m_lpDataArriveProc = proc;
	m_dwUserData = userdata; 

	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		m_hComm=CreateFile(Port,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
		if(m_hComm==INVALID_HANDLE_VALUE )
		{
			AfxMessageBox(_T("无法打开端口!请检查是否已被占用。"));
			return FALSE;
		}
		GetCommState(m_hComm,&dcb);
		dcb.BaudRate=BaudRate;
		//dcb.ByteSize=DataBits;
		//dcb.Parity=Parity;
		//dcb.StopBits=StopBits;
		dcb.fParity=FALSE;
		dcb.fBinary=TRUE;
		dcb.fDtrControl=0;
		dcb.fRtsControl=0;
		dcb.fOutX=dcb.fInX=dcb.fTXContinueOnXoff=0; 

		//设置状态参数
		SetCommMask(m_hComm,EV_RXCHAR);
		SetupComm(m_hComm,16384,16384);
		if(!SetCommState(m_hComm,&dcb))
		{
			AfxMessageBox(_T("无法按当前参数配置端口,请检查参数!"));
			PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
			ClosePort();
			return FALSE;
		} 

		//设置超时参数
		GetCommTimeouts(m_hComm,&CommTimeOuts);
		CommTimeOuts.ReadIntervalTimeout=100;
		CommTimeOuts.ReadTotalTimeoutMultiplier=1;
		CommTimeOuts.ReadTotalTimeoutConstant=100;
		CommTimeOuts.WriteTotalTimeoutMultiplier=0;
		CommTimeOuts.WriteTotalTimeoutConstant=0;
		if(!SetCommTimeouts(m_hComm,&CommTimeOuts))
		{
			AfxMessageBox(_T("无法设置超时参数!"));
			PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
			ClosePort();
			return FALSE;
		} 

		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		return TRUE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::ClosePort()
{
	Deactivate();
	if(m_hComm!=INVALID_HANDLE_VALUE)
	{
		SetCommMask(m_hComm,0);
		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		CloseHandle(m_hComm);
		m_hComm=INVALID_HANDLE_VALUE;
		return TRUE;
	} 

	return TRUE;
} 

BOOL CPSerialPort::Activate()
{
	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return FALSE;
	} 

	if(!m_bReceiving)
	{
		//开始接收线程
		PurgeComm(m_hComm,PURGE_TXCLEAR|PURGE_RXCLEAR);
		m_bReceiving=TRUE;
		m_hReadThread=CreateThread(NULL,0,ReadPortThread,this,0,NULL);
	}
	if(m_hReadThread!=NULL)
	{
		return TRUE;
	}
	else
	{
		m_bReceiving=FALSE;
		return FALSE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::Deactivate()
{
	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return FALSE;
	} 

	//停止接收线程
	if(m_bReceiving)
	{
		m_bReceiving=FALSE;
		WaitForSingleObject(m_hReadThread,500);
		CloseHandle(m_hReadThread);
		m_hReadThread=NULL;
		return TRUE;
	} 

	return FALSE;
} 

BOOL CPSerialPort::IsActive()
{
	return m_bReceiving;
} 

DWORD CPSerialPort::WritePort(char *data,int length)
{
	if(m_hComm == INVALID_HANDLE_VALUE)
	{
		return 0;
	} 

	BOOL bFileWriteState;
	DWORD dwBytesWritten=0; 

	bFileWriteState = WriteFile(m_hComm,data,
			length*sizeof(char),&dwBytesWritten,NULL);
//	AfxMessageBox(L"正在写串口数据。。。");
	return dwBytesWritten;
} 

DWORD CPSerialPort::ReadPort(char *data,int length)
{
	BOOL bFileReadState;
	DWORD dwLength,dwByteRead;
	int TimeOutCount; 

	dwByteRead=0;
	TimeOutCount=0;
//	AfxMessageBox(L"正在读串口数据。。。");
	while(m_hComm!=INVALID_HANDLE_VALUE)
	{
		char* buf=new char[length];
		bFileReadState = ReadFile(m_hComm,data,length,&dwLength,NULL);
		if(!bFileReadState)
		{
			break;
		}
		else
		{
			dwByteRead+=dwLength;
			data+=dwLength;
		}
		if(dwByteRead == (unsigned long)length)
		{
			break;
		}
		if(dwLength!=0)
		{
			TimeOutCount=0;
		}
		else
		{
			TimeOutCount++;
			Sleep(5);
		}
		if(TimeOutCount==5)
		{
			break;
		}
	}
	return dwByteRead;
} 

DWORD CPSerialPort::WriteFileToPort(LPCTSTR FileName)
{
	CFile cf; 

	BOOL fWriteState,isOpen;
	DWORD dwBytesWritten;
	DWORD dwCharToWrite; 

	dwCharToWrite=0; 

	if(m_hComm==INVALID_HANDLE_VALUE)
	{
		return 0;
	}
	isOpen = cf.Open(FileName,CFile::modeRead);
	if(!isOpen)
	{
		//AfxMessageBox(_T("无法打开Hex文件!"));
		return 0;
	}
	dwCharToWrite=(DWORD)cf.GetLength();
	cf.Seek(0,CFile::begin); 

	dwBytesWritten=0; 

	if(m_hComm!=INVALID_HANDLE_VALUE&&dwCharToWrite!=0)
	{
		char* buf=new char[dwCharToWrite];
		cf.Read(buf,dwCharToWrite); 

		fWriteState=WriteFile(m_hComm,buf,dwCharToWrite*sizeof(char),
					&dwBytesWritten,NULL);
		if(!fWriteState)
		{
			//AfxMessageBox(_T("无法向端口写入数据!"));
		}
		delete[] buf;
	}
	cf.Close();
	return dwBytesWritten;
}

解决ethereal运行无法加载wpcap.dll问题

May 24th, 2008 will No comments

在windows下编译ethereal成功之后,由于时间比较晚,还没抓过包,今天来抓一下包时才发现ethereal还缺少wpcap.dll加载,不能正常运行。
Ethereal_WinPcap_Error
可以按照错误提示到http://www.winpcap.org/install/default.htm下载最新版4.0.2,也可以在源码文件包ethereal-0.99.0\packaging\nsis目录下找到WinPcap_3_1.exe,就是WinPcap的3.1版。由于Ethereal0.99.0也是在2006年4月发布的,考虑到兼容性,我选择了安装低版本的WinPcap。安装完之后就可以顺利抓包了。

Categories: 学习笔记 Tags: ,

上位机和下位机

May 23rd, 2008 will No comments

接触硬件开发也有一年的时间了,经常听到上位机控制、上位机程序、上位机通讯之类的词语。而问到上位机的概念或者什么是上位机的时候,大家基本都说不清楚。百度百科中的上位机的解释是:人可以直接发出操控命令的计算机,一般是PC,屏幕上显示各种信号变化(液压,水位,温度等)。我的理解是上位机就是PC或者类似于PC,常见的就是工控机(皮厚、扩展槽N多),现在实验室的gjj同学还用工控机做开发呢。

有了上位机,就有相对应的下位机,那什么是下位机呢?下位机就是受上位机控制,直接控制外部设备,将各种参量转化为数字信号返回给上位机,下位机具有较好的实时性,具有多种通信接口。

上位机与下位机之间通过各种通信接口连接,常见的有串口、SUB、LAN网口。上位机需要根据各种接口协议编写专用的控制程序;下位机需要编写对应的响应控制程序。

这样划分的话,我主要是还是从事下位机方面的开发。偶尔也写写上位机的程序。

Categories: 学习笔记 Tags: ,

在windows下建立Ethereal开发环境

May 21st, 2008 will No comments

Ethereal是一个开源项目,Ethereal的功能是对网络传输的数据进行抓包,可以运行在UNIX、Linux、Windows操作系统上。由于它的开源和多平台支持,使其成为学习网络知识的最好工具。

如果只是使用Ethereal来抓包,可以下载Ethereal的Binary发行版,需要再次开发可以下载Source code发行版,本文中所使用的源码包为ethereal-0.99.0.tar.gz。下面为在windows下建立Ethereal编译开发环境的过程。此过程根据Ethereal Developer’s Guide 18189 for Ethereal 0.10.14文档操作得来,采用Ethereal推荐的Cygwin+MSVC6方式。

1、安装Cygwin
http://www.cygwin.com/下载cygwin安装更新包(一个setup.exe),选择download without installing模式,接下来的设置指定cygwin的安装目录和cygwin更新或安装文件的下载保存目录,指定下载服务器,下载完成后出现下面画面,在默认选择安装的基础上添加下列安装包选择

Archive/unzip Devel/bison Devel/flex Interpreters/perl
Interpreters/python Utils/patch Web/wget  

cygwin_setup_select_packages 
下载完成之后需要安装这些软件包,再次运行setup.exe,选择Install form Local Directory,此时只需要将All 后面设置为Install,即可安装。
安装中遇到的问题,选择Install form internet模式,在上图中点下一步会出现下载失败;在本地没有安装包的时候,选择Install form Local Directory模式,会出错。cygwin安装成功后查看bash版本如下
cygwin_setup_succeed

二、安装MSVC6.0
其实我机器早就安装了MSVC6.0,MSVC6.0提供编译器和链接器。安装过程可以勾选注册环境变量,由于我安装比较早,当时没有选择注册环境变量,按照开发文档中所说的安装VC98\Bin中的vcvars32.bat,但是似乎并没有起作用,还是手动设置。“我的电脑->属性->高级->环境变量”编辑Path变量值,将vcvars32.bat的变量值加入path的变量值中,用分号(;)隔开。我的VC安装在D盘添加的环境变量如下:
D:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin;
D:\Program Files\Microsoft Visual Studio\VC98\Bin;
D:\Program Files\Microsoft Visual Studio\Common\Tools\WinNT;
D:\Program Files\Microsoft Visual Studio\Common\Tools;
默认情况下环境变量中没有Include变量,需要新建Include变量然后添加如下变量值
D:\Program Files\Microsoft Visual Studio\VC98\ATL\Include;
D:\Program Files\Microsoft Visual Studio\VC98\Include;
D:\Program Files\Microsoft Visual Studio\VC98\MFC\Include;
默认情况下环境变量中没有Lib变量,需要新建Lib变量添加如下变量值
D:\Program Files\Microsoft Visual Studio\VC98\Lib;
D:\Program Files\Microsoft Visual Studio\VC98\MFC\Lib;
添加cygwin的bin目录到环境变量path中,为了nmake时运行bash。

三、检测已安装工具的有效性
在windows的cmd下,先进入Ethereal源码所在文件夹,然后运行
nmake -f Makefile.nmake verify_tools
验证通过如图verify_installed_tools_success 如果验证失败,提示有什么软件没有安装,通过cygwin的setup.exe重新安装相应软件。

四、安装库文件
简单的方法是通过nmake -f Makefile.nmake setup,通过wget自动下载所需的库文件,保存目录在config.nmake中的ETHEREAL_LIBS指定,可以自行修改。我将其修改为ETHEREAL_LIBS=D:\ethereal-win32-libs。由于我使用校园网的关系,下载必然是要失败的,所以只能手动安装这些库文件了。
安装目录以D:\ethereal-win32-libs为当前目录,虽然说明文档中表明有些库是可选的,但我不安装可选库,编译不通过。
下表是根据主makefile.nmake中setup来建立。

库名称 版本号 包格式 安装目录 备注
GLib 2.6.6 Binary&Dev glib 下载
GTK+ 1.3.0 Binary&Dev gtk+ 下载
libiconv 1.9.1 Binary&Dev libiconv-1.9.1.bin.woe32 下载
gettext 0.13 Binary gettext-runtime-0.13.1 下载
Net snmp 5.2.5pre2 Source net-snmp-5.2.5 下载 说明
wpdpack 3.1 Developer’s Packs WpdPack 下载
adns 1.0-05 Binary&Source adns-1.0-win32-05 下载
pcre 6.4 Binary&Developer pcre-6.4 下载
zlib 1.2.3 Binary&Dev zlib123-dll 下载
lua 5.1.3 win32bin&win32lib lua5.1 下载 说明

Net SNMP库使用说明:找到net-snmp-5.2.5.pre2\win32\libsnmp\文件夹中的libsnmp.dsp工程文件,用VC6.0编译生成release版本。默认情况下生成的netsnmp.lib文件在net-snmp-5.2.5.pre2\win32\lib\release\文件夹下。
将net-snmp-5.2.5.pre2文件夹下的include和win32文件夹拷到D:\ethereal-win32-libs\net-snmp-5.2.5文件夹下。
修改config.nmake中的NET_SNMP_DIR宏,修改如下
NET_SNMP_DIR=$(ETHEREAL_LIBS)\net-snmp-5.2.5

lua库的使用说明:lua5_1_3_Win32_vc6_lib.zip解压后的lua5.1.lib拷到D:\ethereal-win32-libs\lua5.1\lib目录下,将include文件夹拷到D:\ethereal-win32-libs\lua5.1\目录下;将lua5_1_3_Win32_bin.zip解压到D:\ethereal-win32-libs\lua5.1\bin目录下。修改config.nmake中的LUA_LIBS宏,修改如下:
LUA_LIBS=$(LUA_DIR)\lib\lua5.1.lib

五、编译ethereal
以上搞定之后就可以编译了,cmd下输入nmake -f makefile.nmake all
遇到了找不到unistd.h头文件的错误,解决办法自己新建一个unistd.h头文件,内容如下:

/** This file is part of the Mingw32 package.
* unistd.h maps     (roughly) to io.h
*/

#ifndef _UNISTD_H
#define _UNISTD_H

#include <io.h>
#include <process.h>

#endif /* _UNISTD_H */

将其放到include的环境变量值所指的目录下,如D:\Program Files\Microsoft Visual Studio\VC98\Include;
编译成功之后会在源码文件夹中生成ethereal.exe文件,现在运行ethereal.exe会提示缺少wiretap-0.3.dll、intl.dll等动态链接库。解决办法是在cmd下运行nmake -f makefile.nmake install-deps。将所需的dll拷到源码文件的根目录下。
ethereal_ui

界面还是比较粗糙的,下次把gtk2加入,看下能有改善否。
差点忘了,在nmake -f makefile.nmake distclean的时候有个cd asn1的错误,这应该是ethereal的一个bug,路径错误。解决办法,修改makefile.nmake中的cd asn1修改为cd plugins/asn1,下面隔一行添加cd .. 。

此文基本是按照我安装过程记录下来,若有问题请联系我。