雨疏风骤

编程/音乐/护肤/美食

  VC知识库BLOG :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 登录 ::
  14 随笔 :: 0 文章 :: 1555 评论 :: 9 Trackbacks
<2010年3月>
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910

留言簿(112)

随笔分类

随笔档案

文章档案

相册

走过路过不要错过^_^

搜索

最新评论

阅读排行榜

评论排行榜

2006年12月31日 #

比上不了msn更痛苦的事情,就是你明明上去了,看着众多好友都在msn上闪烁着,可是不管你跟谁说话,说什么,都好比泥牛入海,没有人理踩你,你不知道他们是压根没有收到消息,还是他们给你的回复丢失,也不知道在他们的msn上,你到底是在线还是不在线。偶尔收到只言片语,你喜滋滋的赶紧回复,可是对方却无声无息了。:'(

PS:小p,我看到你跟我说的话了,你看到我说的话没有?:'(
发表于 2006-12-31 10:46 燕七 阅读(5194) | 评论 (170)编辑 收藏

2006年12月30日 #

Oye~~~
经过上午的愤怒,以及中午的发泄愤怒,当时钟转到2006年12月30日下午2点的时候,本姑娘的情绪基本得到了有效的控制,处在平静的怠工状态中。
这时,销售部经理翩然而至,对本人没有及时处理某客户问题提出若干不满和意见,说客户现在很不满,要求退货,说我给客户的程序存在很多问题,云云。blabla一通之后,命令本人立刻把该客户问题处理好。
在他blabal的过程中,我则不断的回忆,某次我为了完成某工作,日以继夜加班,动辄干到凌晨三点,给我造成的眼袋问题至今还没恢复;某次我因为装修请假半天,结果客户一个电话一个电话的打来,花掉的手机费至今还没有报销,而我却在一边接电话一边跟奸商讨价还价的过程中,丢失我的水龙头的所有小零件后来不得不全部重新买过,浪费人民币50大元;以及,现在写字楼修外立面,拆掉了我头顶的空调外机,为了抵御寒冷我多生出来的脂肪,也不知道什么时候才能减掉。

他越说越来劲,我越想越委屈。终于,在他口水喷干之后,我面带微笑、心平气和的说:
        首先,不处理这个客户问题,不是我的意见,是老板的意思,是他指示我不要处理该客户问题,因此客户是退货还是索赔,这些损失与我不关,您不用跟我汇报(这个词儿用的好吧?^^)。
        其次,我们卖给客户的,是硬件+驱动开发接口,客户现在出了问题,可能是硬件问题,也可能是驱动问题,还有可能是他们自己的应用程序问题,您凭什么断定是我的驱动写的有问题?敢情您技术水平这么高,不需要技术支持干预判断,单凭客户说一句话,就能知道是驱动有问题?
        再次,我对要不要处理这个客户问题没什么意见,对我来说,处理哪个客户问题都是工作,不过,您好像并不是我的领导?您好像不能安排我的工作?您要是着急解决这个客户问题,麻烦您去找一个能负责安排我工作的领导,叫他来跟我交代,ok?

       话说出来,该老男人顿时哑口无言,过了那么三五十秒,他讪讪的说,那我找你们陈经理研究一下吧!
       去吧,去研究啊!

       30分钟后,顶头上司陈经理把我叫到了办公室,和蔼的跟我说,以后销售部经理给你下的命令,给你出的技术方案,你千万别听,他没有权力安排你做事,千万记得啊!
 
      Oye!完胜!进公司以来第一次爆发小宇宙,感觉,so 爽~~~~~  
发表于 2006-12-30 15:47 燕七 阅读(5076) | 评论 (109)编辑 收藏

今天真tmd的生气!
去查看饭卡,发现整整12月份,公司没有存一分钱给本姑娘,打电话去问,答因为上个月我饭卡里的钱太多,这个月就没有给我存。K,什么狗屁理由?本姑娘上个月工资还没有花呢,这个月你就不给我发工资?
最tmd的气人的是,不过是打电话去问问而已,那接电话的却好像挺委屈,连说这是你的责任是你不对谁叫你不把钱赶紧花光?还把大老板搬出来了。
冷笑,拿着鸡毛当令箭是吧?谁怕谁啊?不就是100多块钱的饭钱?你不给本姑娘存,本姑娘想个办法占公司千儿八百的便宜呗!
欢迎同学们提供占公司便宜的妙方,多多宜善~~~

发表于 2006-12-30 11:00 燕七 阅读(4876) | 评论 (77)编辑 收藏

2005年5月30日 #

自从开始做USB的驱动程序这个项目,我就开始了跟定时器没完没了的拉锯战……

第一仗,在DDK和手上有的驱动开发书上一通乱翻,终于,找到了几个宝贝函数:KeInitializeTimerKeInitializeDpcKeSetTimerEx。哈,这样做出来的定时器,最小定时单位是1ms,够满足我那8ms的要求了。照着例子研究了一下下,花了一个小时,终于做好了这样一个定时器,回调函数也能周期性的调用了,我心里这个美啊。

旗开得胜,马上在回调函数里开始大展身手了,加了若干代码之后,运行(不好意思,我不会调试sys,所以我的办法就是用debugview看调试信息,根据调试信息解决问题),蓝屏!

唉,真是乐极生悲。问题在哪里?不知道。怎么办?一行一行去掉代码,定位问题吧。无数次的蓝屏、重启之后,有一个可疑的函数冒出来了:IoBuildDeviceIoControlRequest

Leoyin的大力帮助下,我终于看清了DDK文档中的那句话:Callers of IoBuildDeviceIoControlRequest must be running at IRQL <= PASSIVE_LEVEL。咦,我的定时器回调函数是啥level?又是在Leoyin的教导下,用了KeGetCurrentIrql,完了,这个该死的回调函数,居然是什么Dispatch_level

DDK里寻觅良久,终于发现,只能把这个所谓的Level提高,而不能把它降低。这意味着啥?我根本不能用这个定时器了L

郁闷中。。。

怕什么,屡败屡战嘛,我跟定时问题的第二仗开始了。

既然在驱动里已经没有了可用的定时器,沿用我在用户模式下编程的思维,那就用线程吧。恩,这个主意真是不错,于是又DDK里一通搜索,哈,再加上书上看到的例子,首先,我做好了一个线程。线程函数里一个for(;;),这不就成了?现在的问题,无非是叫这个for循环能够乖乖的等上8ms而已嘛。KeWaitForSingleObject,嘻嘻,这可是一个现成的函数么嘛。好,说干就干,根据DDK的说法,这个函数在事件变成有信号和设置的时间超时两种情况下返回。恩,事件用来通知线程退出,时间就设成8ms,这不就ok了?

如此这般之后,哈哈,搞定!

于是开始了sys里一些功能的调试,慢慢的,各种功能都完善起来了,胜利指日可待了~~

这天,跟做硬件的同事联调程序,咦,为什么偶的程序放音放的这么慢?音箱里放出来的声音,就好像40年代初国民党的电台报新闻一样,那叫一个慢啊~

我尚在懵懂中,同事大叫:为什么你每隔15.625ms才读写一次数据啊?打开debugview,输出每次进入循环的间隔,没错,15.625ms,不是原来设想的8ms啊。

郁闷中。。。这一回,在01的指点下,取得了KeWaitForSingleObject返回超时状态的时间间隔,晕了,我明明设置了8ms,它为什么要15.625ms才返回超时呢?一不做二不休,干脆把时间值设成了最小的时间单位:100ns。得,还是15.625的时间间隔。

这下蔫了。本来就不懂所谓的驱动开发,是被领导赶鸭子上架的。好不容易想出来这个自以为得意的定时方法,原来根本就不灵,那我可如何是好啊?

没办法,跟领导老老实实的汇报了战果。领导到还体谅,既然驱动里这个时间没有办法调整,那就叫硬件开发的同事配合一下好了。

于是,以前的驱动里所有跟8ms的定时有关的东西,都被我改了一个遍。如此这般之后,忽又发现,这个15.625ms也根本不能保证啊!CPU忙着干这个干那个,也不是给你这个线程专用的啊!

眼看距离交差的时间越来越近,怎么办?还是DDK渊博啊,依然是到处撒网,乱搜索之后,我的眼睛一亮:KeSetBasePriorityThread。这下发了狠,一口气把我这个宝贝线程的优先级提高了10,哈哈。这下好了。任你多少个程序同时打开,我这个线程都规规矩矩老老实实的每15.625ms做一次循环。

看来,我跟定时问题这一仗,最终还是我赢了耶~虽说被诸位警告说乱改线程优先级很危险,可是,火烧眉毛,咱不得先顾眼前么。。。

离最后期间越来越近了,咱这设备的功能也越来越全了。终于熬到了今天,把程序打包,整理,交给了测试组的同事测试去喽~

终于交差了,心里这个美啊,中午这一觉,睡的真是格外香甜,要知道,为了这个破驱动,我可是整整一个月,每天工作13个小时还多,强烈缺觉啊。

一觉醒来,就看见测试的同事站在面前,热切的看着我,我心生寒意,感觉必定有状况。果不其然,在他的测试机器上,这设备居然不能好好干活的说!了解了问题之后,我感觉真想从这12楼上跳下去。呜呜呜,该死的KeWaitForSingleObject,在他的机器上,等待超时的间隔,居然又变了。我明明在好几种系统和不同的机器配置上测试了很多遍,自以为这是个不太会改变的常量了,它它它,为什么还是不老实啊!

唉,明天就是最后期限,我唯一期望的,就是我们亲戚的客户,千万不要使用会出状况的环境。。。过了明天,一定老老实实的,听Panic的话,找一个准确的不变的硬件的定时方法。。。

 

 

 

 

发表于 2005-05-30 21:16 燕七 阅读(6314) | 评论 (84)编辑 收藏

2005年4月7日 #

配置类型:EXE
预定义:
WIN32=100
STD_CALL
CONDITION_HANDLING=1
NT_UP=1
NT_INST=0
_NT1X_=100
WINNT=1
_WIN32_WINNT=0x0500
WIN32_LEAN_AND_MEAN=1
DEVL=1
FPO=1
_IDWBUILD
NDEBUG
_DLL=1
_X86_=1
i386=1
调用约定:__stdcall (/Gz)
入口点:DriverEntry
基址:0x10000
合并区:.rdata=.text


声明:本人并未实践过,若有差池,千万别找我算帐,去找。。。。。七猫吧^_^
发表于 2005-04-07 16:24 燕七 阅读(6997) | 评论 (130)编辑 收藏

2005年3月23日 #

灰姑娘-辛德瑞拉

从来没买过阿mei的专辑,所以她的歌,只听过那些很出名的,比如《姐妹》、《bad boy》、《原来你什么都不想要》《我可以抱你吗》等等。不特别喜欢,也不反感。我最爱的,是这首偶尔在别人的机器里发现的《灰姑娘》。

听见别人提起你的名字,我就会脸红

一张有你合照的照片,看来看去都不厌倦

坐在寂寞了很久的窗前,不停地想念

从没有对谁的支字片语,可以读了好几百遍

像童话中的世界,如今出现在真实人生的眼前

再苦闷的时刻也有彩虹,哪怕只是轻靠你的肩

像传说中的爱情,如今出现在真实人生的眼前

当你拥抱着我轻轻地对我说,你会爱我到永远

我就像cinderella,等到了寻找我的他

爱情的的过程总会有泪有挣扎

有你的温柔我就什么都不怕

我就像cinderella,等到了寻找我的他

等待你是我付出最甜蜜的代价

快乐的cinderella真爱得到了回答

那该是178岁的年华里充满期待和梦想的灰姑娘吧。那个时候,有几个女孩,不曾在床头偷偷藏着心爱的人的照片,反反复复的看?不曾听见别人提起他,就心跳脸红?又有谁不曾幻想自己成为美丽的灰姑娘,牵着王子的手走向礼堂和幸福呢?

在那个年纪里,我们相信,虽然没有马车和水晶鞋,没有魔法给我们漂亮的衣裳,可当那个一直寻寻觅觅的他出现在面前,我们一样能做最美丽的女孩,得到真爱。于是我们在期待与梦想中成长,等着心中的王子出现。

 

然后,灰姑娘开始长大,梦想依旧还有,可是王子呢?拿着水晶鞋的王子又在哪里?

她没有玻璃鞋,没有华丽衣裳

没有钟声的敲打,

没有带花香,没有舞会妆

她的名叫 cinderella

不爱说话,不懂装傻,

任别人叫她丑小鸭

春去秋来没变化,

心中只有一套旧想法

cinderella的眼泪,难道现在就不珍贵吗

cinderella的伤悲,难道不需要安慰

没有高跟鞋,没有露背装,

没有收过任何花,

没有长头发,没有涂指甲

她的名叫cinderella

王子白马,现实神话,

梦里梦外,不属于她

这般年华无情啊

她期待奇迹般的潘朵拉

cinderella的眼泪,难道从前比较珍贵吗

cinderella的伤悲,其实更需要安慰

――戴佩妮《辛德瑞拉》

灰姑娘终于明白了,王子或许会有,可并不属于她。年少时的梦想和期待,终究被现实的无情吹散,变成没人安慰与珍惜的眼泪和伤悲。王子岂能灰姑娘?他认识的,不过是那只水晶鞋而已。

如果眼泪已不珍贵,那就不要流泪;既然伤悲无人安慰,那就不要伤悲。那些叫你流泪和伤悲的人,根本不值得你如此。保留着我们所有美好的情怀吧,请继续相信,不需要奇迹,不是在梦里,那个人,也终究会来到你身边……

 

 

发表于 2005-03-23 17:40 燕七 阅读(15203) | 评论 (380)编辑 收藏

2005年3月22日 #

typedef  struct
{
    char change_continually:1;
    char link_fault_continually:1;
    char by0:1;
    char by1:1;
    char link_error:1;
    char processor_error:1;
    char local_inhibit:1;
    char apogee_inhibit:1;
}LINK_BILL;
typedef union
{
 LINK_BILL bill;
 char   just;
}LINK;
        程序里用just的大小来判断一些状态,我怎么看都看不懂,这个晕啊。死活问了同事半天,才恍然,原来LINK_BILL里低比特在前,高比特在后,我全给弄拧了啊!
        程序一下子理解了,可是在同事面前感觉都有点抬不起头来了。基础,基础,基础。。。。。。
       再把那本《C程序设计语言》看一遍吧。
 
发表于 2005-03-22 13:58 燕七 阅读(11472) | 评论 (160)编辑 收藏

2005年3月21日 #

这应该是一首不算太有名的歌,收在苏慧伦很早之前的一张专辑《傻瓜》中。这个人,这张专辑,这首歌,不知道还有谁记得……

那还是在我高中毕业的那个暑假吧。就要读大学了,几个好朋友眼看要各奔东西,两个朋友分别送了我两张苏慧伦的专辑,一张《傻瓜》,一张《鸭子》。专辑的主打歌《傻瓜》和《鸭子》都是节奏比较快的歌,倒也曾经传唱一时。可我一听之后,最喜欢的却是这首《我们都是好人》。不为别的,纯粹只是觉得它好听而已。想想看,那时候的我们,年轻的一塌糊涂,除了对未来的无限期望之外,似乎从来不知道恐惧、悲伤和痛苦。所以歌词里的悲伤、无奈和失落,我根本没有丝毫的领会和感触。

读大学,读研,然后工作,8年的时间,人生中最宝贵和美好的年华好像刷拉一下子,就过了。没有时间整理收拾心情,没有时间回忆品味往事,那些深藏在记忆中的往事、故人和歌曲,实在是藏的太深太深,几乎已经很难再把它们拿出来了。

有一天,整理东西,翻出了已经落满了灰的一大盒磁带,一张一张拿出来翻看,郑均的《赤裸裸》,周华健的《爱相随》,小虎队的《再见》,张宇的《消息》,很多很多,还有这张《傻瓜》。

于是去拿随身听,想再听一听那熟悉的旋律,找了一会,又笑了,在这个数码产品充斥的年代,我的随身听,早就被我丢到了九霄云外了吧。这些珍藏多年的磁带,也只有一点收藏的价值了。

只好拿起歌词页,一页一页的看着这些曾经熟悉和喜爱的歌,心好像回到了167岁的那个年龄,那个年龄,最大的烦恼就是考试会考不好,最担心的事情就是被爸妈责怪,上课的时候会抱着一本《被侮辱与损害的》,看的自己泪流满面,被老师叫起来回答问题的时候还在偷偷的抹眼泪,因为要好的女生出去玩,没有叫自己,于是耍小心眼谁也不理睬……   

慢慢的,我看到了这首《我们都是好人》:

一排黄里透红的路灯,照得半夜三点像黄昏

感觉有些什么已经冰冷,有些什么却正要发生

坐在天桥上面看星辰,脱了鞋子脱不了烦闷

只是想喝一口你的啤酒,却感受到瓶口的微温

如果不是倔强让人变得愚蠢,幸福不会变成被甩上的门

我想我们都是好人,可惜拥有太骄傲的灵魂

如果你没在我头上轻轻一吻,我也不会哭得像个小女生

我想我们都是好人,可惜只有做朋友的缘份

坐在天桥上面看星辰,脱了鞋子脱不了烦闷

只是想喝一口你的啤酒,却感受到瓶口的微温

如果不是倔强让人变得愚蠢,幸福不会变成被甩上的门

我想我们都是好人,可惜拥有太骄傲的灵魂

如果你没在我头上轻轻一吻,我也不会哭得像个小女生

我想我们都是好人,可惜只有做朋友的缘份

八年之前那些我不能体会出的情怀,而今我终于懂了。人生中有太多错过,因为倔强,因为骄傲,因为误会,因为猜疑,因为不愿解释,或许错过了一段感情,或许错过了一个人,或许错过了一种生活和一种命运。而错过的背后,是得到了另外一种感情,另外一个人,和另外一种命运。

我庆幸,我终于没有错过那个他,那个爱我包容我理解我的人,幸福有时候短暂的像转瞬即逝的流星,我终于还是抓住了它。

你呢?若是错过了,笑一笑,你们都是好人,可惜只有做朋友的缘分,请等待和珍惜老天给你的另外一个人;若是没有错过,就跟我一样,感谢那个为自己带来幸运和幸福的人吧。

发表于 2005-03-21 12:58 燕七 阅读(6864) | 评论 (94)编辑 收藏

2005年3月17日 #

C语言中对时间和日期的处理

Chuck Allison

Chuck Allison是盐湖城圣Latter Day教堂总部下耶稣教堂家族历史研究处的软件体系设计师。他拥有数学学士和数学硕士学位。他从1975年起开始编程,从1984年起他开始从事c语言的教学和开发。他目前的兴趣是面向对象的技术及其教育。他是X3J16ANSI C ++标准化委员会的一员。发送e-mailallison@decus.org,或者拨打电话到(801)240-4510均可以与他取得联系。

大部分的操作系统有办法得到当前的日期和时间。通过定义在time.h的库函数,ANSI C能以许多不同的形式得到这个信息。函数time返回一个类型为time_t的值(通常为long),该函数在运行期间对当前的日期和时间进行编码。然后你可以将这个返回值传递给其他能对该值进行解码和格式化的函数。

Listing 1中的程序使用函数timelocaltimestrftime以不同的形式输出当前的日期和时间。函数localtime把已经编码的时间解码成如下的struct

struct tm
{
   int tm_sec;     /* (0 - 61) */
   int tm_min;     /* (0 - 59) */
   int tm_hour;    /* (0 - 23) */
   int tm_mday;    /* (1 - 31) */
   int tm_mon;     /* (0 - 11) */
   int tm_year;    /* past 1900 */
   int tm_wday;    /* (0 - 6) */
   int tm_yday;    /* (0 - 365) */
   int tm_isdst;   /* daylight savings flag */
};

每次当你调用localtime的时候,它会重写一个静态的结构并返回该结构的地址(因此同一时刻在一个程序中只能取得一个这样的结构,而不能做明显的拷贝)。函数ctime返回一个指向静态字符串的指针,该字符串以标准的格式包含了完整的时间和日期。strftime根据用户的指定格式格式化字符串(例如,%A代表一周中每一天的名称)。Table 1列出了格式描述符的完整列表。

时间/日期运算

通过改变tm结构里的值,可对时间/日期进行运算。Listing 2中的程序展示了如何计算将来某天的日期和以秒为单位所计算出的程序执行时间。注意函数time的语法(参数time_t由地址传入,并非作为函数的返回值)。函数mktime改变tm结构的值,以便日期和时间在一个合适的范围内,之后day-of-week (tm_wday)和day-of-year (tm_yday)域进行相应的更新。mktimetm结构中日期和时间的值置于合适的范围之内,相应的更新day of week (tm-wday)和day of year (tm-yday)的值。这种情况发生在当一个日期超出了你的实现能够支持的范围的时候。例如,我的MS-DOS的编译器不能编码1970年1月份之前的日期。函数asctime返回tm参数所描述时间的标准字符串(因此ctime (&tval)与asctime (localtime(&tval)是相等的)。函数difftime返回用秒做单位的两个time_t的差。

如果需要处理超出系统范围的日期,或者需要计算两个日期的间隔又不是用秒来做单位,那你需要设计自己的date编码。Listing 3Listing 5中的应用程序通过使用一个简单的month-day-year结构,展示了确定两个日期间隔的年数、月份数和天数的技术。日期的相减就像你在小学里做的减法那样(例如,首先进行天数的相减,如果需要就向月份数借位,以此类推)。注意跳过的年份都被计算进去了。为了简略起见,date_interval函数假设日期都是有效的,并且第一个日期在第二个日期之前。函数返回一个指向静态Date结构的指针,该结构包含了我们想要的答案。

文件时间/日期戳

大多数操作系统为文件维护时间/日期戳。至少你能得知一个文件最后被修改的时间。(常用的make工具使用这一信息来决定一个文件是否需要被重新编译,或者一个应用程序是否需要被重新连接)。由于文件系统在不同平台上有所不同,没有什么通用的函数得到一个文件的时间/日期戳,因此ANSI 标准没有定义这样的函数。然而,大多数流行的操作系统(包括MS-DOS和VAX/VMS)提供了UNIX函数stat,该函数返回相关的文件信息,包括用time_t表示的最后修改时间。

Listing 6中的程序使用statdifftime来确定是否time1.ctime2.c更新(例如,是否最近被修改过)。

如果你需要更新一个文件的时间/日期戳到当前时间,可简单的重写文件的第一个字节。虽然实际内容并未改变,但你的文件系统会认为文件已经被改变了,并且会相应的更新时间/日期戳。(知道你的文件系统!在VAX/VMS下,当你得到一个文件的新版本的时候,旧的版本仍会被保留)。这种技术叫做“‘touching’一个文件”。Listing 7touch的实现在指定文件不存在的时候会创建一个新文件。注意文件以“binary”模式打开(在打开模式字符串中由字符b决定—在将来的专栏中我会详细讨论文件处理的问题)。

1:strftime的格式描述符

Code  Sample Output
---------------------------------------------
%a    Wed
%A    Wednesday
%b    Oct
%B    October
%c    Wed Oct 07 13:24:27 1992
%d    07    (day of month [01-31])
%H    13    (hour in [00-23])
%I    01    (hour in [01-12])
%j    281   (day of year [001-366])
%m    10    (month [01-12])
%M    24    (minute [00-59])
%p    PM
%S    27    (second [00-59] )
%U    40    (Sunday week of year [00-52])
%w    3     (day of week [0-6])
%W    40    (Monday week of year [00-52])
%x    Wed Oct 7, 1992
%X    13:24:27
%y    92
%Y    1992
%Z    EDT   (daylight savings indicator)

Listing 1 time1.c — 采用不同格式输出当前的日期和时间

#include <stdio.h>
#include <time.h>
 
#define BUFSIZE 128
 
main()
{
   time_t tval;
   struct tm *now;
   char buf[BUFSIZE];
   char *fancy_format =
     "Or getting really fancy:\n"
     "%A, %B %d, day %j of %Y.\n"
     "The time is %I:%M %p.";
 
   /* Get current date and time */
   tval = time(NULL);
   now = localtime(&tval);
   printf("The current date and time:\n"
         "%d/%02d/%02d %d:%02d:%02d\n\n",
     now->tm_mon+1, now->tm_mday, now->tm_year,
     now->tm_hour, now->tm_min, now->tm_sec);
   printf("Or in default system format:\n%s\n",
         ctime(&tval));
   strftime(buf,sizeof buf,fancy_format,now);
   puts(buf);
 
   return 0;
}
 
/*  Output
The current date and time:
10/06/92 12:58:00
 
Or in default system format:
Tue Oct 06 12:58:00 1992
 
Or getting really fancy:
Tuesday, October 06, day 280 of 1992.
The time is 12:58 PM.
*/
 
/* End of File */

Listing 2 time2.c —展示如何计算将来某一天的日期以及以秒为单位计算出的执行时间

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
main()
{
   time_t start, stop;
   struct tm *now;
   int ndays;
 
   /* Get current date and time */
   time(&start);
   now = localtime(&start);
 
   /* Enter an interval in days */
   fputs("How many days from now? ",stderr);
   if (scanf("%d",&ndays) !=1)
      return EXIT_FAILURE;
   now->tm_mday += ndays;
   if (mktime(now) != -1)
      printf("New date: %s",asctime(now));
   else
      puts("Sorry. Can't encode your date.");
 
   /* Calculate elapsed time */
   time(&stop);
   printf("Elapsed program time in seconds: %f\n",
     difftime(stop,start));
 
   return EXIT_SUCCESS;
}
 
/* Output
How many days from now? 45
New date: Fri Nov 20 12:40:32 1992
Elapsed program time in seconds: 1.000000
*/
 
/* End of File */

Listing 3 date.h — 一个简单的日期结构

struct Date
{
   int day;
   int month;
   int year;
};
typedef struct Date Date;
 
Date* date_interval(const Date *, const Date *);
/* End of File */

Listing 4 date_int.c — 计算两个日期的间隔

/* date_int.c: Compute duration between two dates */
 
#include "date.h"
 
#define isleap(y) \
 ((y)%4 == 0 && (y)%100 != 0 || (y)%400 == 0)
 
static int Dtab [2][13] =
{
  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 
Date *date_interval(const Date *d1, const Date *d2)
{
   static Date result;
   int months, days, years, prev_month;
 
   /* Compute the interval - assume d1 precedes d2 */
   years = d2->year - d1->year;
   months = d2->month - d1->month;
   days = d2->day - d1->day;
 
   /* Do obvious corrections (days before months!)
    *
    * This is a loop in case the previous month is
    * February, and days < -28.
    */
   prev_month = d2->month - 1;
   while (days < 0)
   {
      /* Borrow from the previous month */
      if (prev_month == 0)
         prev_month = 12;
      --months;
      days += Dtab[isleap(d2->year)][prev_month--];
   }
 
   if (months < 0)
   {
      /* Borrow from the previous year */
      --years;
      months += 12;
   }
 
   /* Prepare output */
   result.month = months;
   result.day = days;
   result.year = years;
   return &result;
}
/* End of File */

Listing 5 tdate.c — 举例说明日期间隔函数的使用

/* tdate.c: Test date_interval() */
 
#include <stdio.h>
#include <stdlib.h>
#include "date.h"
 
main()
{
   Date d1, d2, *result;
   int nargs;
 
   /* Read in two dates - assume 1st precedes 2nd */
   fputs("Enter a date, MM/DD/YY> ",stderr);
   nargs = scanf("%d/%d/%d%*c", &d1.month,
     &d1.day, &d1.year);
   if (nargs != 3)
      return EXIT_FAILURE;
 
   fputs("Enter a later date, MM/DD/YY> ",stderr);
   nargs = scanf("%d/%d/%d%*c", &d2.month,
     &d2.day, &d2.year);
   if (nargs != 3)
      return EXIT_FAILURE;
 
   /* Compute interval in years, months, and days */
   result = date_interval(&d1, &d2);
   printf("years: %d, months: %d, days: %d\n",
      result->year, result->month, result->day);
   return EXIT_SUCCESS;
 
}
/* Sample Execution:
Enter a date, MM/DD/YY> 10/1/51
Enter a later date, MM/DD/YY> 10/6/92
years: 41, months: 0, days: 5 */
/* End of File */

Listing 6 ftime.c — 确定是否time1.c比time2.c更新

/* ftime.c: Compare file time stamps */
 
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
 
main()
{
   struct stat fs1, fs2;
 
   if (stat("time1.c",&fs1) == 0 &&
      stat("time2.c",&fs2) == 0)
   {
      double interval =
        difftime(fs2.st_mtime,fs1.st_mtime);
 
      printf("time1.c %s newer than time2.c\n",
        (interval < 0.0) ? "is" : "is not");
      return EXIT_SUCCESS;
   }
   else
      return EXIT_FAILURE;
}
/* Output
time1.c is not newer than time2.c */
/* End of File */

Listing 7 touch.c —通过覆盖旧文件或者创建一个新的文件来更新时间戳

/* touch.c: Update a file's time stamp */
 
#include <stdio.h>
 
void touch(char *fname)
{
   FILE *f = fopen(fname,"r+b");
   if (f != NULL)
   {
      char c = getc(f);
      rewind(f);
      putc(c,f);
   }
   else
      fopen(fname,"wb");
 
   fclose(f);
}
 
/* End of File */

 

发表于 2005-03-17 12:34 燕七 阅读(11650) | 评论 (322)编辑 收藏

 

Duane Burton Sr. Technical Marketing Engineer
Intel Corporation

Jim Huang
Sr. Technical Marketing Engineer
Intel Corporation

2002年六月

应用于
Microsoft® Windows® XP

摘要: 本文介绍了怎样利用实时通信(RTC)应用编程接口(API)来实现音视频会议、应用程序共享、白板、简单的点对点聊天和音视频调节向导。RTC API提供了卓越的基于PC的通信上的革新,这可应用于所有基于Microsoft Windows XP应用程序。

下载 RTCSampleCode.zip.

目录

绪论
RTC
客户端接口
初始化
会话
处理RTC
事件
创建会话
处理实时流会话事件
关闭
会话
平台性能
结论
参考资料

绪论

微软的实时通信(RTC)应用编程接口(API)提供了显著的基于PC的通信上的革新—即时消息、音视频会议和应用程序共享/合作,这可应用于所有基于Microsoft® Windows® XP的应用程序。

使用RTCAPI来进行通信是一个非常简单的过程。
1
、客户端应用程序确定参与通信的平台的性能。
2
、应用程序选择通信中首选的视音频设备。
3
、应用程序初始化会话。
4
RTC层调整数据的获取、压缩和传输,这使得应用程序不用负责这一任务。使用哪一种音视频的编码解码器 由通信双方的连接质量决定。
5
参与会话的应用程序接受、解压并重放被传输的数据。

插图见连接:
图一、音视频会议的界面

本文描述了怎样为一个应用程序添加PC-to-PCRTC基本能力;我们假定你对使用COM对象开发Windows应用程序已经很熟悉。本文所讨论的源代码可在本文开始所给出的连接里获得。我们以后将会讨论PC-to-Phone、现场能力和XML配置。

例子代码展示了使用实时通信API实现音视频会议、应用程序共享、白板、简单的点对点聊天和音视频调节向导的基本要素。其他RTC 支持但本文没有讨论的特征有回波抵消(AEC)、前向错误校验(FEC)、 带宽估计、动态抖动缓冲管理、自动增益控制(AGC)和服务质量(QC)控制算法。在《Microsoft Windows的实时通信客户端的媒体支持》中描述了上述特征。

RTC客户端接口

所需头文件: rtccore.h

你的应用程序需要通过CoCreateInstance()来获得RTC接口CLSID_RTCClientGUID = {7a42ea29-a2b7-40c4-b091-f6f024aa89be})作为参数。一旦获得了接口,用Initialize()来初始化COM对象,以确定该平台的通信能力。

   // Initialize the RTC COM object

   hr = CoCreateInstance (CLSID_RTCClient, NULL,

      CLSCTX_INPROC_SERVER, IID_IRTCClient,

      (LPVOID *)&m_pClient);     

 

      // Initialize the client interface

hr = m_pClient->Initialize();

选择通信能力

下一步是选择首选的通信类型和相关设备(摄像机、麦克风等)。缺省配置是激活所有通信类型。如果会话的参与者能共享应用程序、传送即时消息和音视频会议,那么这些通信类型自动被激活。如果某一参与者不支持某种通信类型,那么所有的参与者都不能激活该类型。

   m_pClient->SetPreferredMediaTypes ( RTCMT_ALL, VARIANT_TRUE );

会议参与者的平台性能和可用带宽决定了使用哪一种codec

·                     视频:Windows RTC 客户端支持分辨率为QCIF176×144)的H.261H.263 codecs 这些比特率可变的codecs6125 Kbps传送视频数据。使用IRTCClient方法中的put_MaxBitRateput_TemporalSpatialTradeOff有可能会影响到视频传送的空间和瞬时清晰度。

·                     音频:Windows RTC客户端支持许多音频codec。音频codec由连接的两端共同决定。下表列出了所支持的音频codec

CODEC

取样率(kHz)

比特率(Kbps)

帧长 (msec)

G.711

8

64

20

G.722.1

16

24

20

G.723

8

6.4

30, 60, or 90

GSM

8

13

20

DVI4

8

32

20

SIREN

16

16

20, or 40

调整通信设备

选择了首选的通信类型和相关设备之后,调整通信设备。RTCAPI提供了向导对摄像机和麦克风进行调整。使用RTCClient的方法InvokeTuningWizard()可调整它们的设置。

插图见连接:
2:摄像机调节向导

3:麦克风调节向导

初始化会话

在应用程序与其他参与者连接之前,它必须能够处理会话中的RTC事件。PC-to-PC的通信中,应用程序捕获即时消息事件、音量事件、媒体事件、客户端消息事件和会话状态改变事件。下述代码展示了怎样创建一个事件过滤器来捕获RTC客户端的特定事件。

lEventMask设置了一组应用程序感兴趣的事件。(为获得全部的事件列表,可在MSDN站点搜索RTC_EVENT,这样可以获得关于每一事件的更多信息。CRTCEvents 类在客户端之间分配事件。 RTCEvents 对象在应用程序和 IRTCEventNotification接口之间创建接口。 所有的RTC事件由RTCEvents 类处理。

    // Set the event filter to listen for RTC events

    // Using RTCEF_ALL will listen for all events

    // For the sample application, we will demonstrate how to set the

    // event listener for a limited set of events.

    long lEventMask = RTCEF_SESSION_STATE_CHANGE |

                 RTCEF_MESSAGING |

                 RTCEF_MEDIA |

                 RTCEF_INTENSITY |

                 RTCEF_CLIENT;

 

    hr = m_pClient->put_EventFilter( lEventMask );

 

    // Create the event sink object

    m_pEvents = new CRTCEvents;

 

    // initialize the event handler

    hr = m_pEvents->Advise( m_pClient, m_hWnd );

 

    // Set the listen mode for RTC client

    // RTCLM_BOTH opens the standard SIP port 5060, as well as

    // a dynamic port.

    hr = m_pClient->put_ListenForIncomingSessions(RTCLM_BOTH);

音视频的媒体类型可在会话过程中添加或删除,因此客户端必须能监听这些类型的事件。在“处理实时流会话事件”这一节中可获得关于状态改变和事件处理的更多信息。

处理RTC事件

一旦事件处理者IRTCEventNotification接收器中进行了注册,接收和处理RTC事件就变得相当的容易了。当例子程序接收到RTC事件时,应用程序的事件处理者就对应用程序的消息处理者发一个消息。OnRTCEvent() 处理应用程序接收到的所有事件。

OnRTCEvent(UINT message, WPARAM wParam, LPARAM lParam)

{

 

    // Based on the RTC_EVENT type, query for the

    // appropriate event interface and call a helper

    // method to handle the event

    switch ( wParam )

    {

       ….

                ….

       ….

        case RTCE_MEDIA:

            {

                IRTCMediaEvent * pEvent = NULL;

 

                hr = pDisp->QueryInterface( IID_IRTCMediaEvent,

                                            (void **)&pEvent );

 

                if (SUCCEEDED(hr))

                {

                    OnRTCMediaEvent(pEvent);

                    SAFE_RELEASE(pEvent);

                }

            }

            break;

                ….

                ….

       ….

}

创建一个会话

当你在RTC中发起一个呼叫之前,你必须创建并且初始化一个会话。 然后你可以输入参与者的IP地址来发起一个呼叫。可可能通过属于一个e-mail 地址或者一个电话号码来激活一个会话。然而,这一功能需要一个SIP注册服务器,对它的讨论超出了本文的范围。参阅MSDN可获得关于SIP注册服务器的更多信息。

RTC目前还不支持多方视频通话,因此应用程序在初始化一个新会话之前,必须保证没有视频会议在进行。在它第一个发布版本中,Windows RTC客户端只支持多方电话会议,并不支持多方音视频会话和视频会议。

为与另一台PC通话,确定RTC会话类型并且使用IRTCSession接口创建一个同类型的会话。下列代码展示了如何创建会话。

HRESULT CAVDConfDlg::MakeCall(RTC_SESSION_TYPE enType, BSTR bstrURI)

{

    ...

 

    // Create the session

    IRTCSession * pSession = NULL;

 

    hr = m_pClient->CreateSession(enType, NULL, NULL, 0, &pSession);

 

    // Add the participant to the session

    hr = pSession->AddParticipant(bstrURI, NULL, &m_Participant);

 

    ...

    return S_OK;

}

处理实时流会话事件

根据不同的会话类型,存在媒体事件、音量事件、即时消息事件和会话状态改变事件。

媒体事件

处理媒体事件需要得到媒体类型、事件类型和原因,然后发送消息给会话窗口。应用程序可以使用get_MediaType()从视频、音频、T120和实时传输协议(RTP)事件中接收消息。例子程序展示了如何获得媒体事件并将其发送给媒体对话框去处理。

void CAVDConfDlg::OnRTCMediaEvent(IRTCMediaEvent *pEvent)

{

    ...

 

    hr = pEvent->get_MediaType(&lMediaType);

 

    hr = pEvent->get_EventType(&enType);          

   

    hr = pEvent->get_EventReason(&enReason);

 

    if ((m_AVDlg) && (m_AVDlg.GetState () != RTCSS_IDLE))

    {

        // Deliver the media state to the session window

        m_AVDlg.DeliverMedia(lMediaType, enType, enReason);

    }

}

音量事件

当扬声器或者麦克风的音量水平发生变化时产生音量事件。应用程序可使用get_Direction()函数获得发生改变的音频设备。一旦确定了设备,应用程序可获得设备的属性并处理改变。应用程序可通过slider控件来显示音量的改变,或者显示给用户一个音量表。

void CAVDConfDlg::OnRTCIntensityEvent(IRTCIntensityEvent *pEvent)

{

    ...

 

    hr = pEvent->get_Direction(&enDevice);

 

    hr = pEvent->get_Level(&lLevel);

 

    hr = pEvent->get_Min(&lMin);

 

    hr = pEvent->get_Max(&lMax);

 

    if (m_AVDlg.GetState () != RTCSS_IDLE)

    {

        // Deliver the intensity state to the session window

        m_AVDlg.DeliverIntensity(enDevice, lLevel);

    }

}

即时消息事件

使用IRTCMessagingEvent可在会话参与者中传递即时消息。当一个消息事件产生时,应用程序必须获得会话和事件类型,得到相关会话中的参与者信息,以便能将消息传递给适当一方。事件处理者也能处理会话状态的改变。

HRESULT CAVDConfDlg::OnRTCMessagingEvent(IRTCMessagingEvent *pEvent)

{

    ...

 

    hr = pEvent->get_Session(&pSession);

 

    hr = pEvent->get_EventType(&enType);

 

    hr = pEvent->get_Participant(&pParticipant);

 

    if (enType == RTCMSET_MESSAGE)

    {

        hr = pEvent->get_MessageHeader(&bstrContentType);

 

        hr = pEvent->get_Message(&bstrMessage);

 

        // Deliver the message to the session window

        if (m_cMessageDlg)

      m_cMessageDlg.DeliverMessage(pParticipant, bstrContentType,

         bstrMessage);

 

    }

    else if (enType == RTCMSET_STATUS)

    {

        hr = pEvent->get_UserStatus(&enStatus);

 

        // Deliver the user status to the session window

        m_cMessageDlg.DeliverUserStatus(pParticipant, enStatus);

    }

   return S_OK;

}

会话状态改变事件

会话状态改变事件的处理过程与其他RTC事件相同。会话状态的改变包括建立一个新的音/视频会话,或者通知客户端一个到来的即时消息。下列例子展示了当请求会话时所作的处理;客户端通过一阵铃声被通知,请求被应答,然后会话开始。

Void CAVDConfDlg::OnRTCSessionStateChangeEvent(IRTCSessionStateChangeEvent

   *pEvent)

{

    ...

 

    hr = pEvent->get_State(&enState);

 

    hr = pEvent->get_Session(&pSession);

 

    switch ( enState )

   {

   case RTCSS_INCOMING:

        {

      ...

 

            // This event is called when an incoming call occurs

            RTC_SESSION_TYPE enType;

 

            hr = pSession->get_Type(&enType);

 

            // Ring the bell

            m_pClient->PlayRing(RTCRT_PHONE, VARIANT_TRUE);

 

            // Accept the session

            hr = pSession->Answer();

         }

     }

    ...

}

应用程序共享

开启T120应用程序共享非常容易,只需调用IRTCClient 接口的StartT120Applet 方法。

hr = m_pClient->StartT120Applet ( RTCTA_APPSHARING );

白板支持

在应用程序中支持白板,需要调用StartT120Applet 方法,使用 RTCTA_WHITEBOARD 枚举作为参数。

hr = m_pClient->StartT120Applet ( RTCTA_WHITEBOARD );

关闭会话

要关闭一个会话,所有正在运行T120的应用程序必须被关闭。然后RTC 客户端接口调用ShutDown()并完成关闭会话的过程。

平台性能

使用RTC进行通信需要处理器具有适当的性能。下列例子中,一个1 GHzPentium® III处理器和一个2.2 GHzPentium 4处理器用于确定当使用RTC特征时处理器的利用率。下表描述了使用本文介绍的RTC特征时处理器的利用率。

任务

P4 处理器 at 2.2-GHz (% CPU 占用率n)1

P III 处理器 at 1.0-GHz (% CPU 占用率)2

音视频会议

9%

22%

添加程序共享(共享IE浏览器)

10%

35%

增加白板

12%

37%

增加即时

12%

37%

1 P4处理器配置:Intel®主板D850MV256MB PC800 RDRAM,主板集成声卡, nVidia* GeForce*2 Ultra; Windows XP专业版

2 P III处理器配置:Intel 主板VC820; 256MB PC133 SDRAM, nVidia* GeForce*2 Ultra, Creative* Sound Blaster* Live*, Windows XP 专业版

结论

通过使用实时通信客户端API,在Windows XP下开发通信工具已变得相当简单。开发者可迅速设计、配置和开发他们的应用程序。现有的音视频会议应用程序可通过添加RTC的丰富的通信特征而获益匪浅。使用RTC API进行开发的程序可从一个统一的通信协议中获益。这提高了你的程序与其他文本消息和音视频会议程序的互相合作的能力。将RTC APIIntel的处理器以及Microsoft Windows XP相结合,你正在传达着一种创新的通信体验。

参考资料

Intel Developer Services

Media Support in the Microsoft Windows Real-time Communications Client

Microsoft Platform SDK: Real-Time Communications Client

发表于 2005-03-17 12:19 燕七 阅读(7256) | 评论 (72)编辑 收藏