2010年5月14日星期五

VC定时器介绍



在工业生产控制系统中,有许多需要定时完成的操作,如定时显示当前时间,定时刷新屏幕上的进度条,上位 机定时向下位机发送命令和传送数据等。特别是在对控制性能要求较高的实时控制系统和数据采集系统中,就更需要精确定时操作。
  众所周知,Windows 是基于消息机制的系统,任何事件的执行都是通过发送和接收消息来完成的。 这样就带来了一些问题,如一旦计算机的CPU被某个进程占用,或系统资源紧张时,发送到消息队列 中的消息就暂时被挂起,得不到实时处理。因此,不能简单地通过Windows消息引发一个对定时要求 严格的事件。另外,由于在Windows中已经封装了计算机底层硬件的访问,所以,要想通过直接利用 访问硬件来完成精确定时,也比较困难。所以在实际应用时,应针对具体定时精度的要求,采取相适 应的定时方法。
  VC中提供了很多关于时间操作的函数,利用它们控制程序能够精确地完成定时和计时操作。本文详细介绍了 VC中基于Windows的精确定时的七种方式,如下图所示:



图一 图像描述

  方式一:VC中的WM_TIMER消息映射能进行简单的时间控制。首先调用函数SetTimer()设置定时 间隔,如SetTimer(0,200,NULL)即为设置200ms的时间间隔。然后在应用程序中增加定时响应函数 OnTimer(),并在该函数中添加响应的处理语句,用来完成到达定时时间的操作。这种定时方法非常 简单,可以实现一定的定时功能,但其定时功能如同Sleep()函数的延时功能一样,精度非常低,最小 计时精度仅为30ms,CPU占用低,且定时器消息在多任务操作系统中的优先级很低,不能得到及时响 应,往往不能满足实时控制环境下的应用。只可以用来实现诸如位图的动态显示等对定时精度要求不高的情况。如示例工程中的Timer1。
  方式二:VC中使用sleep()函数实现延时,它的单位是ms,如延时2秒,用sleep(2000)。精度非常 低,最小计时精度仅为30ms,用sleep函数的不利处在于延时期间不能处理其他的消息,如果时间太 长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer2。
  方式三:利用COleDateTime类和COleDateTimeSpan类结合WINDOWS的消息处理过程来实现秒级延时。如示例工程中的Timer3和Timer3_1。以下是实现2秒的延时代码:

COleDateTime start_time = COleDateTime::GetCurrentTime();

COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;

while(end_time.GetTotalSeconds()<>实现延时2秒

{

MSG msg;

GetMessage(&msg,NULL,0,0);

TranslateMessage(&msg);

DispatchMessage(&msg);

//以上四行是实现在延时或定时期间能处理其他的消息,

       //虽然这样可以降低CPU的占有率,

//但降低了延时或定时精度,实际应用中可以去掉。

end_time = COleDateTime::GetCurrentTime()-start_time;

}//这样在延时的时候我们也能够处理其他的消息。

  方式四:在精度要求较高的情况下,VC中可以利用GetTickCount()函数,该函数的返回值是 DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高,在较 短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中。如示例工程中的Timer4和Timer4_1。下列代码可以实现50ms的精确定时:

DWORD dwStart = GetTickCount();

DWORD dwEnd = dwStart;

do

{

dwEnd = GetTickCount()-dwStart;

}while(dwEnd <50);

为使GetTickCount()函数在延时或定时期间能处理其他的消息,可以把代码改为:

DWORD dwStart = GetTickCount();

DWORD dwEnd = dwStart;

do

{

MSG msg;

GetMessage(&msg,NULL,0,0);

TranslateMessage(&msg);

DispatchMessage(&msg);

dwEnd = GetTickCount()-dwStart;

}while(dwEnd <50);

虽然这样可以降低CPU的占有率,并在延时或定时期间也能处理其他的消息,但降低了延时或定时精度。
  方式五:与GetTickCount()函数类似的多媒体定时器函数DWORD timeGetTime(void),该函数定时精 度为ms级,返回从Windows启动开始经过的毫秒数。微软公司在其多媒体Windows中提供了精确定时器的底 层API持,利用多媒体定时器可以很精确地读出系统的当前时间,并且能在非常精确的时间间隔内完成一 个事件、函数或过程的调用。不同之处在于调用DWORD timeGetTime(void) 函数之前必须将 Winmm.lib 和 Mmsystem.h 添加到工程中,否则在编译时提示DWORD timeGetTime(void)函数未定义。由于使用该 函数是通过查询的方式进行定时控制的,所以,应该建立定时循环来进行定时事件的控制。如示例工程中的Timer5和Timer5_1。
  方式六:使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级。利用该函数可以实现周期性的函数调用。如示例工程中的Timer6和Timer6_1。函数的原型如下:

MMRESULT timeSetEvent( UINT uDelay,

UINT uResolution,

LPTIMECALLBACK lpTimeProc,

WORD dwUser,

UINT fuEvent )

  该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL。函数的参数说明如下:

uDelay:以毫秒指定事件的周期。

Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。

LpTimeProc:指向一个回调函数。

DwUser:存放用户提供的回调数据。

FuEvent:指定定时器事件类型:

TIME_ONESHOT:uDelay毫秒后只产生一次事件

TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。

  具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数 中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。
  方式七:对于精确度要求更高的定时操作,则应该使用QueryPerformanceFrequency()和 QueryPerformanceCounter()函数。这两个函数是VC提供的仅供Windows 95及其后续版本使用的精确时间函数,并要求计算机从硬件上支持精确定时器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。
QueryPerformanceFrequency()函数和QueryPerformanceCounter()函数的原型如下:

BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);

  数据类型ARGE_INTEGER既可以是一个8字节长的整型数,也可以是两个4字节长的整型数的联合结构, 其具体用法根据编译器是否支持64位而定。该类型的定义如下:

typedef union _LARGE_INTEGER

{

struct

{

DWORD LowPart ;// 4字节整型数

LONG HighPart;// 4字节整型数

};

LONGLONG QuadPart ;// 8字节整型数

}LARGE_INTEGER ;

  在进行定时之前,先调用QueryPerformanceFrequency()函数获得机器内部定时器的时钟频率, 然后在需要严格定时的事件发生之前和发生之后分别调用QueryPerformanceCounter()函数,利用两次获得的计数之差及时钟频率,计算出事件经 历的精确时间。下列代码实现1ms的精确定时:

LARGE_INTEGER litmp;

LONGLONG QPart1,QPart2;

double dfMinus, dfFreq, dfTim;

QueryPerformanceFrequency(&litmp);

dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率

QueryPerformanceCounter(&litmp);

QPart1 = litmp.QuadPart;// 获得初始值

do

{

QueryPerformanceCounter(&litmp);

QPart2 = litmp.QuadPart;//获得中止值

dfMinus = (double)(QPart2-QPart1);

dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒

}while(dfTim<0.001);

  其定时误差不超过1微秒,精度与CPU等机器配置有关。 下面的程序用来测试函数Sleep(100)的精确持续时间:

LARGE_INTEGER litmp;

LONGLONG QPart1,QPart2;

double dfMinus, dfFreq, dfTim;

QueryPerformanceFrequency(&litmp);

dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率

QueryPerformanceCounter(&litmp);

QPart1 = litmp.QuadPart;// 获得初始值

Sleep(100);

QueryPerformanceCounter(&litmp);

QPart2 = litmp.QuadPart;//获得中止值

dfMinus = (double)(QPart2-QPart1);

dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒

  由于Sleep()函数自身的误差,上述程序每次执行的结果都会有微小误差。下列代码实现1微秒的精确定时:

LARGE_INTEGER litmp;

LONGLONG QPart1,QPart2;

double dfMinus, dfFreq, dfTim;

QueryPerformanceFrequency(&litmp);

dfFreq = (double)litmp.QuadPart;// 获得计数器的时钟频率

QueryPerformanceCounter(&litmp);

QPart1 = litmp.QuadPart;// 获得初始值

do

{

QueryPerformanceCounter(&litmp);

QPart2 = litmp.QuadPart;//获得中止值

dfMinus = (double)(QPart2-QPart1);

dfTim = dfMinus / dfFreq;// 获得对应的时间值,单位为秒

}while(dfTim<0.000001);

其定时误差一般不超过0.5微秒,精度与CPU等机器配置有关。(完)

2010年5月13日星期四

WinAPI 改变窗口大小

void SetClientWindowSize(HWND, int , int)

void SetClientWindowSize(HWND hWnd, int nClientWidth, int nClientHeight)
{
RECT WindowRect,ClientRect;

int nDeltaWidth, nDeltaHeight;

GetWindowRect( hWnd , &WindowRect );
GetClientRect( hWnd , &ClientRect );

nDeltaWidth = ( WindowRect.right - WindowRect.left ) - ( ClientRect.right - ClientRect.left );
nDeltaHeight = ( WindowRect.bottom - WindowRect.top ) - ( ClientRect.bottom - ClientRect.top );

MoveWindow( hWnd , WindowRect.left , WindowRect.top , nWidth + nDeltaWidth , nHeight + nDeltaHeight , TRUE );
}

SetClientWindowSize( hWnd , nWidth , nHeight );

笑话(6Park)

1. 女领导夜归,突被两男架上车,一男威胁说:“老实点,劫色的。”女领导闻言笑骂道:“他爹的,这么愉快的事,搞得这么紧张,吓死了,我还以为被双规了!”

2. 某小学门口家长贴出雷人标语:“冤有头,债有主,前方右转是政府。”

3. 快到晚上刷牙的时间了,这是个让人生转变的过程。把杯具拿起来,加上牙刷和水,就变成洗具了……

4. 杜拉拉和徐静蕾的双料粉丝,简称杜蕾丝。

5. 一白领丽人因工作疲惫不堪在msn上的签名上戏称“求包养”,众男同事纷纷打电话询问价钱,更有精明者咨询:包年多少钱?包月多少钱?能包日算否诸如此类的问题;最后只有一才子俘获丽人芳心,他问:“请问能按流量计算么?

6. 不抽烟不喝酒,63岁——林彪同志; 只喝酒不抽烟,73岁——恩来同志; 只抽烟不喝酒,83岁——主席同志; 既抽烟又喝酒,93岁——小平同志; 吃喝嫖赌样样来,103 岁——张学良同志; 啥坏习惯没有,每天尽做好人好事,23岁——雷锋同志。

7. 刚才在一个网上注册了一个用户名“爹”,结果它给我发来邮件,一看就傻眼了,“爹,您好,你的用户名注册成功了!” 我汗。。。

8. 王姓朋友生了双胞胎,去派出所登记名字:王国,王党。片警说:“你他妈这也太过分了,一个就算了,两个太明显了。”王姓朋友说:“反正我就要这两个字,其他的你看着办。”过了几天,他去派出所,看到的名字是:王爱国,王爱党。

9. 两个男人坐在酒吧里。 其中一个开始侮辱另一个人。 他高声嚷到:“我和你妈睡过!”酒吧里顿时一片寂静,每个人都想听听那个人会怎么反击。 他又一次大喊:“我睡过你妈!” 另一个人说:“ 你喝醉了,爸,咱回家吧!”

10. 女人当官的两种最佳捷径:一、睡她的男人厉害;二、睡她妈的男人厉害。

11. 大家一起开玩笑,说在中国直选的话谁最可能做总统,最后结论是马化腾,他只要弹出一个窗口投马化腾一票,送10Q币……

12. 逃生指南:地震了,往挂有国徽的建筑里跑。

13. 大学里有棵树,叫高数,很多人挂在上面.

14. 潘石屹一次到新浪做节目,站在新浪落地窗前,见下面还有一大片空地,大喜,急电秘书:中关村还有这么大片空地我们都不知道,快看看卖了没有,没卖咱们赶紧买下来!少顷,秘书回电:潘总,那是北大!

15. 一司机开车撞倒一老汉,司机慌忙伸出头来看个究竟,老汉看见后大惊。奄奄一息地说:“老兄,你还想倒车不成”。

16. 我说我是导演,她就跟我睡了。第二天,我说我是动画片导演,她哭了。。

17. “我饿了”是句很可爱的话。跟妈妈说:我饿了!妈妈会立刻起身道:我这就做饭去!跟爸爸说:我饿了!爸爸会笑呵呵的说:走,咱出去吃!跟男友说:我饿了!他会温柔的问:想吃什么呢?跟女友说:我饿了!她永远都会回你:我也是……

18. 今天的核安全峰会有46个国家参加,加上美国刚好47个。电影《2012》第20分钟20秒:美国总统对他女儿说:“如今我们正进行一项史无前例的合作,目前有其他 46个国家参加。另外,波兰总统及夫人已经登船了。”

19. 一个来自宅女的呐喊——“不要叫我宅女,请叫我居里夫人!”

20. “我是你的什么? ”你是我的山西疫苗啊.。”原来我是毒药啊。”“这样,我就可以跟你死在一起了。

21. 性知识普及是军训不可分割的组成部分。听同学说,高中军训第一天,男寝互相不认识就纷纷以压箱底的黄段子开场,只有一个娃娃脸的男生一声不吭地写东西,大家惊讶怎么有这么单纯爱学习的人,他说:我在记。

22. 在二战的时候,一个钉子户不搬,导致军用机场没法建,受到其他居民和媒体的谴责。丘吉尔说:我们和德国人打仗就是为了保护合法财产不受侵害,如果拆了他的家,那我们为什么要打仗?

23. 原本上帝只是偶尔给地球拍一部灾难片,现在已经改拍连续剧了。

24. 党报记者下基层,到一贫困山区深入采访,偶遇一特困老汉,便问:大爷,请您谈谈这些年生活发生的巨大变化。大爷:变化大啊。旧社会俺过着牛马不如的日子。记者大喜:现在呢?大爷:现在如了。

25. 两只母鸡在聊天,看到一只公鸡无精打彩地走来,母鸡问:“咋地了?没精神?”公鸡说:“创业了,做了点小生意。”母鸡问:“做啥生意累成这德行啊?”公鸡朝天叹了口气说:“经济危机,弄了点鸡精卖卖。。。”

26. “日本馆呢在A片区,来,一起体验一下……”电视台如是报道上海世博会

27. 老师提问学生;人生最高的境界是什么?学生答;能拿沙特的工资,住英国的房子,戴瑞士的名表,娶上韩国的女人,包养日本的二奶,做泰国的按摩,开德国的轿车,坐美国的飞机,喝法国的红酒,吃澳洲的海鲜,抽古巴的雪茄,穿意大利的皮鞋,看奥地利的歌剧,买俄罗斯的别墅,雇菲律宾的女佣,配以色列的保镖,洗土耳其的桑拿。老师批语:啰嗦,你不就想当个中国干部嘛!

28. 说有一只企鹅,他的家离北极熊家特别远,要是靠走的话,得走20年才能到。有一天,企鹅在家里呆着特别无聊,准备去找北极熊玩,与是他出门了,可是走到路的一半的时候发现自己家的煤气忘记关了,这就已经走了10年了,可是煤气还是得关啊,于是企鹅又走回家去关煤气。关了煤气以后,企鹅再次出发去找北极熊,等于他花了40年才到了北极熊他们家……然后企鹅就敲门说:“北极熊北极熊,企鹅找你玩来了!”结果北极熊开门以后你猜他说什么……“我不和你玩!”


29.
小明送给女友一件礼物,女友接过来,看了一眼说:破滴。小明大惊:新的啊怎么会是破的。女友说:你看啊,上面拼音写着:podi。小明:-_-你拿倒了。

30. 去一朋友的企业。在卫生间,朋友看小便池地上的尿渍说,这些人总是尿到外边,贴了提示语也没用。我看提示语一共十个字:向前一小步,文明一大步。我说,汉语博大精深,我给你减两个字,保准今后没人尿外边。果然,从那以后,他的企业男员工如厕再没人尿外边。那八个字是:尿到外边,说明你短。

31. 你忍心蜗居在不到10平米的小屋里吗,你忍心看着自己的女友和你奋斗一辈子还供不起一套房吗,你忍心看着你父母缩衣节食把仅有的一点养老金帮你还房贷吗,这里才是实现你梦想的地方。加入我们,待遇从优,装备齐全,食宿全免,一条大裤衩,一双人字拖,一把AK47,800美元底薪+提成,全天移动式海景套房,多劳多得,只要大干一票,在大都市买楼不再是梦想,干两票,跻身上层社会,直接与奥巴马对话不再是距离。别再犹豫了,给你一片海域,换你一生奇迹! ---索马里海盗人力资源部

32. 东方早报:上海公墓已经占用超过70%,数年后将面临死亡人口将无地可葬的危机 网友回复:让领导先死!

33. 波兰总统:我坠机了,郁闷呐。 林彪回复:沙发,同楼主,悲剧。 金正日跟贴:只坐火车的飘过,安全。 张作霖跟帖:楼上sb。


34.
强盗:“抢劫,都他妈的给我趴下!”当他看到一女士趴下的姿势后吼道:“你他妈给我文明点,老子是劫财不是劫色!”

35. 从医学的角度看,所谓幼稚,就是既憋不住尿又憋不住话;所谓不够成熟,就是只能憋得住尿,却憋不住话;所谓成熟,就既憋得住尿,又憋得住话;所谓衰老,就是只憋得住话,却憋不住尿。

36. 中国历史上最牛逼的皇帝是唐中宗李显!自己是皇帝,父亲是皇帝,儿子是皇帝,弟弟是皇帝,侄子是皇帝!更要命的是他妈也是皇帝!史称:六位帝皇丸!

37. 子:“爸,我的这篇稿子往哪里投?” 父:“往钱多的地方投。” 子:“中国人民银行,行吗?”

38. 某市长被双规,家中大保险柜久不能开。中纪委官员专家说:此乃声控锁,密码八个字。办案人员轮流猜试:“人不为己,天诛地灭”“上天保佑,升官发财”“人为财死,鸟为食亡”……均不灵!无奈,押该市长至。他清清嗓子,用浓重乡音正色道:“清正廉洁,执政为民!”门应声而开,满柜珠宝惊呆众人。

39. 记得儿子刚出生的那天,护士把儿子从产房抱出来。初为人父那叫一个激动啊,急忙接过儿子,嘴里还习惯性的说着:来,叔叔抱一下,乖……周围立刻一片沉寂。

40. 领导新官上任,为表示亲民,与众下属寒暄,说:我是农民的儿子。然后问秘书:你呢?刚好秘书长在大城市,谄媚道:我是农民的孙子耶。领导很满意,再问身边刚刚分来的一朴实大学生:你呢?该新同志答:俺就是农民。

41. “地球一小时”姑且不说对电网能造成多大的损失,关键是40周后中国又将出现生育高峰!无脑的媒体啊~

42. “他妈的,读了四年大学,就得了一个学生证,毕业时还得上交给学校”一位毕业未获毕业证的同学如是说。

43. 小李要移民到美国,领导问他:“你对你的工资不满意吗?” 小李说:“满意。” “对你的住房不满意?” “满意” “那是上网环境不满意?” “也满意” “对医疗,孩子上学都不满意?” “都满意!” “既然你都满意为什么还要移民?” “因为那里允许有不满意!”

44. 一哥们儿隔壁新搬来个老外,一天晚上老外敲门求助,说:“我的电视坏了,不能换台。”这哥们儿低头看了眼表,很镇定的说:“全国的电视在晚上七点到七点半都会这样的。”

45. 性别:男、爱好:女 46. 一小贩被城管抓住,苦苦哀求城管仍不放人,情急之下小贩说了这样一句话:“你就把我当个屁给放了吧!”遂放人。 47. 某男生到女生宿舍访其女友,该宿舍的门卫要他填写一份会客单,其上要填写姓名、性别、地址、年龄……一直填写到最后一栏“关系”时,该男生想了半天才写下 “尚未发生”4个字。

48. 亲男人,远女人,此先汉所以兴隆也;亲女人,远男人,此后汉所以倾颓也。

49. 拉登:“我有两个消息要告诉你,一个好的,一个坏的,你要听哪个?”布什:“好的。” 拉登:“我要自首。” 布什:“好啊!欢迎你自首,那坏消息呢?” 拉登:“我开着飞机来自首。”


50.
为了杜绝学生翻墙,某中学的铁栅栏都是带尖头的。上面的警示标语如下:严禁翻墙,男生不小心就会翻成女生,女生不小心就会翻成女人。