xiaoyue's profile我的空间我作主PhotosBlogLists Tools Help

Blog


    April 27

    生活啊生活!

        忙碌碌了半年了,原以为辞职到第二次工作期间会有一段时间
    可以好好休息了,结果未能如愿。匆匆忙忙地去了深圳报到,在
    遥远的他乡紧张地培训了20天,那边的同事很友好,但我仍然天
    天想回家。回家前一天晚上兴奋地未能睡好。回杭那天,一早的
    飞机,下午就赴公司报到。体会到了今后的工作节奏。之后便是
    正常的上班之日继续。每天挤公交上下班的都市人生活,我也加
    入了其中。(当然,在市区上班还是挺好的。)
        上班很忙,加班挺多,作为新员工,要学的太多,要做的也太多
    ,要跟上节奏,所以每天催自己要努力,要努力。工作上,不再
    像以前那样混日子了。没时间逛街,没时间旅游,没时间爬山,
    没时间看电影,没时间整理,没时间做做饭、看看烹饪电视节目
    。没有自己的时间~~~
        忙碌也好,不像以前那么多愁善感了!这段时间以来感觉自己过
    得挺开心,朋友说我健忘,相当的健忘!我承认,健忘,却没想
    法子来改善!健忘何尝不好呢?虽然生活上会有点糟糕,但心情
    上却相当的有利啊!常忘事情,也会常忘不开心。所以现在每天
    只关注眼前的事,每天过得很客观。某天同事说我,刚认识我的
    时候没觉得我那么调皮。突然,我感觉自己挺陌生。
    感觉现在自己的生活里没有自己了,全是别人的世界!亲人的,
    朋友的,工作的!是不是长大了,就有责任照顾好身边的一切?
    而要照顾那么多,就没有时间来照顾自己了?一直以来忙得没有
    时间思考!中午休息时听杰伦的情歌,听得眼泪直流!!!突然
    有点怀念过去的自己,在那一刻,觉得自己回来了!从来都是个
    爱哭的女孩,却很久没有哭过了。也许幸福得无需哭了,也或许
    原本的自己离开了很久了。好乱,搞不清楚状况了!
    好累,真的好累,好想大哭一场,我只是个小女人,只想要简简
    单单的生活,什么时候可以不用承受那么多,好好过过自己的生
    活?
    January 23

    新年晚会

      2008.01.21晚,在杭二中音乐厅,举行了东方通信07年的新年晚会,这是我来东方通信所参加的第一次新年晚会,也是最后一次,我们部门的节目是大合唱配手语表演,这次演出完成后,我在东信算是公德圆满了!
      下午1:30,我们一行演员就去音乐厅准备了,穿好了礼服马上进化装室化妆,在轮流等待化妆的空闲时间,我们吃了个面包,算是解决了晚饭了!妆是请本地的化妆师化的,价格便宜,妆也化得相当糟糕,后来还是请自己人给重新补了妆,不然真没脸见人的!我们是最后一个节目,所以还有时间再排练一下,说真的,人多,平时工作忙,还真没怎么练过,很紧张啊!
      下午18:00左右,晚会正式开始了,期间精彩的糟糕的节目都有,自己编排的,没有专业老师指导真的不一样,不过拉拉队还是相当卖力的,尤其是在抽奖的环节,气氛到达了顶峰!!!在最后一个抽奖环节开始的时候,我们就该到后台准备了。走上台,随着音乐声的响起......在灯光下,我已不记得自己是谁,只当自己是个演员,好也好,差也好,这是我们自己排的节目,有我们的汗水,我们的努力!我们很尽力地让自己发挥到最好,让自己配合着所有人!!!
       可以说,我们的节目并不是很成功,音乐的节奏没有卡准,我们缺少一个指挥!领队很不开心,不过我倒还好。在这次参与中感觉到,当一名演员,真不容易!
    January 17

    第一场雪

    2008-01-13 夜
    2008年的第一场雪,比以往时候来得都晚一些!
      两年没下雪了,当坐在公交车上突然发现窗外漫天飞舞的雪花时,抑制不住的疯狂的喜悦!庆幸今天晚上还在街上,庆幸不是象平常一样在阴雨天窝在屋里玩电脑,庆幸没有错过这第一场雪.
      从来没想过如今色彩缤纷的世界里,雪的莹白成了稀缺的颜色,也不曾想过有一天大雪纷飞的日子成了整个都市的节日,整车的人,街上的人,在寒冷的冬夜里都忘却了寒冷,为这迟来的新年礼物欢呼!
        记得小的时候年年都会下雪,因为冬天都很冷,下雪是每个冬天都会发生的事情。喜欢睡懒觉,从小就喜欢,一觉醒来,会发现外面完全变了一个世界,一片的雪白,整个世界都雪白雪白,真是每个冬天都会得到的惊喜!屋檐上还会结上长长的冰柱,一根跟,晶莹惕透!湖面上已经结上了厚厚薄薄的冰,一切都是静止的,世界是那么的安静,直到快乐的小伙伴们嬉戏的声音来打破,谱上新的一天的乐谱,冬天的乐谱!所以,我从来都记不住什么节气,一直认为,下了雪,冬天就来了。雪,是冬的使者!不知哪年开始,不再下雪了,虽然还是冷,但没有冬天的感觉,只是天气变冷了而已,人们加厚了衣服,围围巾,戴手套,而冬天,被遗忘了!没有雪的冬天,不像冬天。
        不过,今年终于下雪了,虽然第二天,世界并不是像期望的那样一片纯白,多少让人失望,但昨天晚上那灯光下漫天飞舞的雪花是真的,那车顶上堆积的雪是真的,我昨天捏的那个迟到了三年的雪球是真的!下雪了,下雪了,下雪了!
        希望再下一场大雪,铺天盖地的大雪,让世界银装素裹的大雪,希望每年的冬天都和以前一样,让雪来告知世人,冬天来了!
     
    2008-01-17
      今年的雪已经下了三场了,今天这场就果然是铺天盖地的那种大雪,世界也铺上了一层薄薄的棉絮,上帝正开始打造一个银装素裹的世界!以为今天这场大雪会让一切变得纯白纯白!早晨确实开心了一阵啊!
      可惜,中午再出来的时候,一切都恢复了原样,雪停了!还是缺少那么一点点力量啊!今年还会来一场真正的大雪吗?
    November 16

    [转]张爱玲之关于爱情

    [转]

    張愛玲的10大愛情名言

    20070801160036

    1.于千萬人之中遇見你所要遇見的人,于千萬年之中,時間的無涯的荒野裏,沒有早一步,也沒有晚一步,剛巧趕上了,沒有別的話可說,惟有輕輕地問一聲:「噢,你也在這裡?」

    張愛玲有那麼多的小說,我卻覺得《愛》最有意味。沒有華麗的辭藻,沒有刻意的修飾,也沒有曲折的情節,但是那輕輕地一句“噢,你也在這裡?”卻又代表了所有--所有的開始,所有的結局,和所有的人生……

    愛情在動靜之間:緣分在聚散之間。如果說愛情是源源不斷的小溪,緣分則是偶爾投到溪水中蕩起陣陣漣漪的石子。如果說愛情是一道美麗的風景。緣分則是偶爾光顧的浪跡四方的旅人,有緣人自會發現,無緣者任他尋千百度也會錯過。

    你剛巧趕上了嗎?

    2.遇見你我變得很低很低,一直低到塵埃裏去,但我的心是歡喜的。並且在那裏開出一朵花來。

    中毒裏,潔塵說:所有的愛情裏面都有卑微,份量不一而已!因為愛上一個人、在乎一個人,就有妥協,妥協自然就有卑微的感覺。在對等的情感關係中,這種卑微是相互的,是男女雙方對一份情感的努力和付出。

    可惜現實生活中的愛情並不是那麼的對等。當你愛的更多,付出更多的時候,你自己都會發覺自己的卑微!

    3.因為愛過,所以慈悲;因為懂得,所以寬容。

    也許愛玲就是這樣子的,她之於胡蘭成,不過就是懷著一顆慈悲的心去對待,所以才會有這麼多的容忍,只是,我不知道因她的慈悲,她是否就真的擁有了所想得到的愛情?愛情不是一種寬容,更不是一種容忍。愛情是絕對的自私、絕對的拔扈、絕對不容一粒沙子的。

    只是女人天性的柔弱,注意她們的愛情摻和了太多的縱容和被縱容的成份。回頭看看,所有學不會慈悲的女人一個個走了。紅樓夢裏的林黛玉就是這樣死的,奪走她生命的是心病,是她的計較。而只有慈悲的女人,依舊會在愛情的殿堂裏做自欺欺人的夢。

    4.我們再也回不去了!

    緣分依舊,而情已不再。這是失散了十幾年的戀人顧曼楨與沈世鈞,別後重逢時說的最動人最素樸,也最淒艷的一句話!人世的蒼涼,全括在了其中。這也該張愛玲小說個性的極致處:一句話,幾個字,足以引出世間的萬千苦辣,肝腸寸斷卻仍不能言說的酸楚。

    是的,回不去了,回不去了!因為他們之間隔著濤濤不盡的似水光陰。濤濤不盡的似水光陰。一段感情延續了十五年,一次等待也已經有了十五年。十五年,已經足夠一場轟轟烈烈的愛,塵埃落定。

    問世間,有多少愛可以重來?請珍惜眼前人。

    5.也許每一個男子全都有過這樣的兩個女人,少兩個。娶了紅致瑰,久而久之,紅的變了墻上的一抹蚊子血,白的還是窗前明月光;娶了白玫瑰,白的便是衣服上的一粒飯粘子,紅的卻是心口上的一顆硃砂痣。

    這是張愛玲書裏那段對男人最寫實精闢的描寫,雖然我是男人,也不得不佩服她可以將男人的心理描寫的如此透徹!

    男人初始時,大多是喜歡淡雅清麗的白玫瑰,皎潔的清香,像是冰涼的高山之雪,值得付出一生的代價,求得在這冰涼水流中的沉淪。

    然而,在度過如醉如癡欣喜若狂之後,男人漸漸變的不滿足。他開始想要一個快樂的艷麗夢幻,妖嬈的濃艷,搖曳在月的黃昏。紅色的玫瑰,芳香彌散,辛辣魅惑。

    其實,女人的美,從來蘊涵著千個面目,不是每個人都可以看到它。在一個足夠聰明的男子面前,它會展露給你世上最微妙的色彩。彼刻,純白艷紅,呈現另番甜美的面貌。那樣曼妙的花朵,需要刻骨的愛憐,聰慧的溫情,才可以灌溉。

    每一個女子的靈魂中都同時存在紅玫瑰與白玫瑰,但只有懂得愛的男子,才會令他愛的女子越來越美,即便是星光一樣寒冷的白色花朵,也同時可以嬌媚地盛放風情。

    可惜世間,懂得愛的男子實在是太少!在男人心裏真正完美的女人,總是隨著時間,閱歷的變化,不斷地變化著!你永遠達不到的。所以,不管是紅玫瑰,還是白玫瑰,都永遠有不能讓人滿足的遺憾和欠缺,所以男人總是永遠地渴望別的玫瑰媚惑的來臨。

    6.回憶永遠是惆悵的!愉快的使人覺得,可惜已經完了;不愉快的,想起來還是傷心。

    這個世界上有很多事情,你以為明天一定可以再繼續做的;有很多人你以為一定可以再見到面的,於是,在你暫時放下手,或者暫時轉過身的時候,你心中所有的,只是明日又重聚的希望,有時候甚至連這點希望也不會感覺到!

    因為,你以為日子既然這樣一天一天過來。當然也應該這樣一天一天過去,昨天,今天,明天應該是沒有什麼不同的。但是,就會有那麼一次,在你放手,一轉身的一剎那,有的事情就完全改變了。太陽落下去,而在它重新升起以前,有些人,就從此和你永遠分開了。

    於是,只能獨自呼喚你的名字。在冬夜裏取暖,空守一份寂寞,也是獨處時淡淡地回味。想你的時候,把你的名字寫在手心。攤開的是思念,緊握的是幸福。

    在心痛時療傷,靜靜的品味一份孤獨,也是遙望時濃濃地陶醉。牽掛的夜晚,把你的名字挂在窗前,凝視的是溫情,感懷的是甜蜜。

    7.生於這世上,沒有一樣感情不是千瘡百孔的。

    所謂的唯美只存在於劇情裏。因為不唯美,我們才會去苦苦追尋;因為不唯美,讓我們知道還有一種東西叫做希望。可是,其實感情不能貪心,“如果有誰認為有十全十美的愛情,他不是詩人,就是白癡。”所以,不要求愛的完美,只求實實在在的一種真實的、踏實的愛情來滌蕩心情,才是正確的感情態度。

    即使當它真的千瘡百孔的時候,也不要刻意挽留,因為每段感情開始的時候都有他存在的理由,結束時也有他結束的必然。寫出這句話時,又想到了《東邪西毒》中,黃藥師那句經典的臺詞:當你不能夠再擁有的時候,你唯一可以做的就是令自己不要忘記

    以前不知出處,總覺得這話精闢,也只有她這樣錦心繡口的女人才能道得出來。雖然公布於世的她的感情只幾樁,但她卻像是個老於世故的人,娓娓說出這樣一句話,直讓人感嘆。現在終於知道了,這句話出於短篇小說《留情》!

    8.日子過得真快,尤其對於中年以後的人,十年八年都好象是指顧間的事。可是對於年輕人,三年五載就可以是一生一世。

    前者因了歲月滄海,洞悉。後者因了歲月榮華,天真。

    夫妻原來都是極相愛的人,才有勇氣決定共度一生,但三年五年,十年八年,總會膩的,不分手也只是如親人一樣生活在一起。可如果是親人,又何必再做夫妻呢?你沒有答案,我也沒有。同樣張愛玲沒有。

    小時候樂聽童話,以為王子和灰姑娘走上紅地毯,一切都美滿了。故事的美好,在與人為的停駐了時間,有意識忽略之後幾十年的人生歲月。長大後,發現上當了。童話是美好的,現實是殘酷的。張愛玲以旁觀之清的凜然姿態,告訴我們,生命被無休止的瑣事填充,像一襲華美的袍,上面爬滿了無數憎恨的蝨子。

    9.愛你值不值得,其實你應該知道,愛就是不問值得不值得。

    張愛玲在自己23歲的時候曾經如癡如醉地愛上一個38歲的男人——胡蘭成。他是個有妻室的人,且是個地地道道的漢奸。

    我相信對於胡蘭成的品性、為人、政治立場,張愛玲自然是十分清楚的,可是她卻沒有辦法不讓自己陷入與胡的那場愛情深淵,也是她一生痛苦的深淵。戀愛的女人是管不住自己的,就仿佛吸煙的人明知吸煙有害照樣吞雲吐霧一樣。

    所以說,愛情是盲目的,沒有什麼值不值得的問題。當你愛上一個人的時候,你還會理性的思考嗎?如果是,那只能說明這不是愛情!

    10.女人一旦愛上一個男人,如賜予女人的一杯毒酒,心甘情願的以一種最美的姿勢一飲而盡,一切的心都交了出去,生死度外!

    而男人若愛上一個女人,如發現了自己一直尋找的光環!光環的美麗讓他陶醉其中,他為她獻出了很多的溫柔,女人被男人的溫柔所感,義無返顧的把自己獻給了男人,終於這個光環緊緊的套在自己的身上,....

    時光漫漫的流逝了去,光環慢慢的變的灰暗,男人的臉也漸顯些蒼老,在光環的陪伴下,男人也漸成熟了讀懂了很多世事!可是同時他也感覺到女人老了,失卻了往日的光華!

    好男人:他懂得女人的光華以緊緊的溶進了自己的生命!是女人的幸運!

    壞男人:他會厭倦,很輕易的把光環從自己身上剝離,然後拋棄!是女人的不幸

    這段話說的如此的形象和具體,一直被我所記住。多餘的話,我不想說了,只想問:你被光環套住了嗎?

     

    November 02

    我很忙

    杰伦的新专辑终于出来了,早已期待许久。很早就有说《我很忙》会有西部牛仔的风格在里面,一首主打歌果然就是轻快明朗的,听听让人很快乐!《甜甜的》让人有点出乎意外,原来杰伦也会唱响这样子的歌曲,感觉很新奇,不过表现得还是很不错的,感觉真的如歌名所说的,听着觉得很甜蜜蜜。不过最喜《青花瓷》,就像当初听到《菊花台》时的如痴如醉,歌词还是那么一种信手拈来的精致和美丽,听着感觉很醇厚,仿佛就是看到了一个画着仕女图的青花瓷瓶,透着远古的气息,缠绵着永久的爱情主题!
        继续聆听!!!!
    October 17

    生病的日子

        红眼睛,会传染,休息在家!像只兔子一样!
        不能看电脑、不能看电视,整天只是睡觉+吃饭。
        买了报纸看,一份,两份,好几份,看多了眼睛又痛,于是又睡觉!
        突然在想,如果有人给我读报纸该多好,或者,要有个读报器该多好,把他往整篇文章上一扫,他就能用美妙的声音朗读出来,那该多好。很多失明的人不也是可以多点生活乐趣吗?
         hoho,聪明的人们赶紧去发明吧,会有很大的市场吧!
        但愿自己的眼睛快点好起来!唉!!!
    September 25

    中秋月圆

    又一年,中秋月圆!
    路上,行人匆匆,都希望早点回家和家人团聚!
    城市里,高楼林立,也还有明月的一片天空,
    在雷峰塔侧,在断桥怀中,在三潭映月的倒影中
    在树影婆娑中闪烁,在怏怏湖水中荡漾
    中秋,是个团圆的日子,是个思亲的日子
    百忙中的你,是否有给家人打个电话!
    *********************************************************************************
    话外音:纪念杭州今日的交通特别拥堵,餐馆都排长队,最后快餐+一包糖炒栗子,作为今年的中秋盛宴!
    同样满足!!!
     
    September 11

    那首老歌

    酷暑渐去,秋意浓浓
    广场边,旗杆下,等车的人
    舞台上,音乐飘扬
    曾几何时,这首歌,听到心碎
    ......

    好的歌,永远经典
    好的歌,任何时候都让人怦然心动
    好的歌,那熟悉的旋律,一次次听得眼眶湿润
    原本是洒脱的词意,却勾起尘封的往事
    一幕幕
    就像在看一部感人的老片
    欢乐、感动、忧伤
    ......
     
    老电影,沉淀了情感和岁月
    格外地重
    格外地重
    同样的问候
    在不同的时候
    也会是不同的声音
    在过去是期待
    在如今是无奈
    既如此
    倒不如都让她随风去吧
     
    **********************************************
    还记得年少时的梦吗,象朵永远不凋零的花
    陪我经过那风吹雨打,看世事无常,看沧桑变化
    那些为爱所付出的代价,是永远都难忘的啊
    所有真心的痴心的话,永在我心中,虽然已没有他
    走吧,走吧,人总要学着自己长大
    走吧,走吧,人生难免经历苦痛挣扎
    走吧,走吧,为自己的心找一个家
    也曾伤心流泪,也曾黯然心碎,这是爱的代价

    也许我偶尔还是会想他,偶尔难免会惦记着他
    就当他是个老朋友啊,也让我心疼,也让我牵挂
    只是我心中不再有火花,让往事都随风去吧
    所有真心的痴心的话,都在我心中,虽然已没有他
    走吧,走吧,人总要学着自己长大
    走吧,走吧,人生难免经历苦痛挣扎
    走吧,走吧,为自己的心找一个家
    也曾伤心流泪,也曾黯然心碎,这是爱的代价
    ***********************************************
     
    August 21

    幸福

         朋友们都说我胖了,确实,半年多来真的胖了不少,体重增加了,连脸都圆了!爱吃不爱动,不胖才怪呢!天天叫着减肥减肥,可我看这肥是减不下去了。都说心宽体胖,这在我身上得到了很好的实践!
         虽然开始为自己的发福担心了,女人最担心的就是变老和发胖,呵呵!不过却是我最开心的日子!有个人照顾你,关心你,爱你,这比什么都觉得幸福!虽然他不是最帅最优秀的,却是我心里最好的。有他在,心里就满满的。觉得天塌下来也不用害怕,因为我不再孤单!常常为自己的这点小幸福感动不已,以前某个老师说了一句:“我常常因为自己而感动!”当时还引得大家大笑,可现在我觉得他说得很有道理呢,我也常常为自己而感动,也许这也是一种幸福的体现吧!
         平常的生活很平常,以前很讨厌一成不变的生活,隔三岔五地希望有点什么事情什么活动可以给自己带来点兴奋或者刺激。可现在不这样了,每天平常的生活都让我好珍惜,觉得日子过得太快,希望永远活在现在。虽然小小烦恼总是存在,但一到家里就觉得很轻松,很舒服!什么天大的事情都不存在了。做个小菜,看个电影,甚至就只在阳台吹吹风,看看繁华的街景,深邃的天空,一切都是那么美好!
         我很好,因为有你!!!
    July 30

    快乐很难,也可以很简单

        夏天到了,人长大了,有很多需要做的事情,顶着烈日奔波,把原本就显黑的自己晒得像包大人!
        喜欢睡懒觉,可一周里面有5/7必须要早起上班,把一天中最宝贵的时间奉献给工作,懒觉,成了早起前多睡5分钟的奢望!
        是个喜欢逛街的女人,可是好像已经很久没有去shopping了!
        很多时候要挑礼物,但想出一件称心的礼物很伤脑筋!
        每天要洗衣服,做家务,又花了不少一天中所剩无几的时间。
        开始发胖,比较头疼的事情!
        烦恼多多~~~~~~
     
    **************************************************************************************
      
        越晒越黑了,人们说,黑色是健康的肤色!回家来享受面膜的呵护!
        喜欢睡懒觉,改不了的爱好!一周里面还有两天可以睡到日上三杆!挺奢侈了!!!
        喜欢逛街,偶尔一次和好朋友的相约,让人从早上期待到晚上!
        给朋友挑礼物,精致的美丽的东西,想象着她收到礼物时的欣喜,很有成就感!
        女人爱厨房,一点没错。叮叮当当,做精致的菜肴和心爱的人一起吃,很甜!
        开始发胖,微微发胖,呵呵,花三天制作一个减肥计划,然后只坚持了一天!管他呢,还是那么喜欢美食,想吃就吃!
        其实快乐也很简单!!!
       
    June 29

    【转】CString操作指南——好文呐!

    通过阅读本文你可以学习如何有效地使用 CString。

      CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。
      使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册,但囊括了大部分常见基本问题。

    这篇文章包括以下内容:

    CString 对象的连接

    格式化字符串(包括 int 型转化为 CString )
    CString 型转化成 int 型
    CString 型和 char* 类型的相互转化

    char* 转化成 CString
    CString 转化成 char* 之一:使用LPCTSTR强制转化
    CString 转化成 char* 之二:使用CString对象的GetBuffer方法
    CString 转化成 char* 之三: 和控件的接口
    CString 型转化成 BSTR 型;
    BSTR 型转化成 CString 型;
    VARIANT 型转化成 CString 型;
    载入字符串表资源;
    CString 和临时对象;
    CString 的效率;
    总结
    下面我分别讨论。

     1、CString 对象的连接

      能体现出 CString 类型方便性特点的一个方面就字符串的连接,使用 CString 类型,你能很方便地连接两个字符串,正如下面的例子:

    CString gray("Gray");
    CString cat("Cat");
    CString graycat = gray + cat;
    要比用下面的方法好得多:

    char gray[] = "Gray";
    char cat[] = "Cat";
    char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
    strcpy(graycat, gray);
    strcat(graycat, cat);
     2、格式化字符串

      与其用 sprintf() 函数或 wsprintf() 函数来格式化一个字符串,还不如用 CString 对象的Format()方法:

    CString s;
    s.Format(_T("The total is %d"), total);
      用这种方法的好处是你不用担心用来存放格式化后数据的缓冲区是否足够大,这些工作由CString类替你完成。
      格式化是一种把其它不是字符串类型的数据转化为CString类型的最常用技巧,比如,把一个整数转化成CString类型,可用如下方法:

    CString s;
    s.Format(_T("%d"), total);
      我总是对我的字符串使用_T()宏,这是为了让我的代码至少有Unicode的意识,当然,关于Unicode的话题不在这篇文章的讨论范围。_T()宏在8位字符环境下是如下定义的:

    #define _T(x) x // 非Unicode版本(non-Unicode version)
    而在Unicode环境下是如下定义的:

    #define _T(x) L##x // Unicode版本(Unicode version)
    所以在Unicode环境下,它的效果就相当于:

    s.Format(L"%d", total);
      如果你认为你的程序可能在Unicode的环境下运行,那么开始在意用 Unicode 编码。比如说,不要用 sizeof() 操作符来获得字符串的长度,因为在Unicode环境下就会有2倍的误差。我们可以用一些方法来隐藏Unicode的一些细节,比如在我需要获得字符长度的时候,我会用一个叫做DIM的宏,这个宏是在我的dim.h文件中定义的,我会在我写的所有程序中都包含这个文件:

    #define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
      这个宏不仅可以用来解决Unicode的字符串长度的问题,也可以用在编译时定义的表格上,它可以获得表格的项数,如下:
    class Whatever { ... };
    Whatever data[] = {
       { ... },
        ...
       { ... },
    };
    for(int i = 0; i < DIM(data); i++) // 扫描表格寻找匹配项。

      这里要提醒你的就是一定要注意那些在参数中需要真实字节数的API函数调用,如果你传递字符个数给它,它将不能正常工作。如下:TCHAR data[20];
    lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!
    lstrcpyn(data, longstring, DIM(data) - 1); // RIGHT
    WriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!
    WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
    造成以上原因是因为lstrcpyn需要一个字符个数作为参数,但是WriteFile却需要字节数作为参数。
    同样需要注意的是有时候需要写出数据的所有内容。如果你仅仅只想写出数据的真实长度,你可能会认为你应该这样做:

    WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
    但是在Unicode环境下,它不会正常工作。正确的做法应该是这样:

    WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
      因为WriteFile需要的是一个以字节为单位的长度。(可能有些人会想“在非Unicode的环境下运行这行代码,就意味着总是在做一个多余的乘1操作,这样不会降低程序的效率吗?”这种想法是多余的,你必须要了解编译器实际上做了什么,没有哪一个C或C++编译器会把这种无聊的乘1操作留在代码中。在Unicode环境下运行的时候,你也不必担心那个乘2操作会降低程序的效率,记住,这只是一个左移一位的操作而已,编译器也很乐意为你做这种替换。)
      使用_T宏并不是意味着你已经创建了一个Unicode的程序,你只是创建了一个有Unicode意识的程序而已。如果你在默认的8-bit模式下编译你的程序的话,得到的将是一个普通的8-bit的应用程序(这里的8-bit指的只是8位的字符编码,并不是指8位的计算机系统);当你在Unicode环境下编译你的程序时,你才会得到一个Unicode的程序。记住,CString 在 Unicode 环境下,里面包含的可都是16位的字符哦。

     3、CString 型转化成 int 型

      把 CString 类型的数据转化成整数类型最简单的方法就是使用标准的字符串到整数转换例程。
      虽然通常你怀疑使用_atoi()函数是一个好的选择,它也很少会是一个正确的选择。如果你准备使用 Unicode 字符,你应该用_ttoi(),它在 ANSI 编码系统中被编译成_atoi(),而在 Unicode 编码系统中编译成_wtoi()。你也可以考虑使用_tcstoul()或者_tcstol(),它们都能把字符串转化成任意进制的长整数(如二进制、八进制、十进制或十六进制),不同点在于前者转化后的数据是无符号的(unsigned),而后者相反。看下面的例子:

    CString hex = _T("FAB");
    CString decimal = _T("4011");
    ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
     4、CString 型和 char* 类型的相互转化

      这是初学者使用 CString 时最常见的问题。有了 C++ 的帮助,很多问题你不需要深入的去考虑它,直接拿来用就行了,但是如果你不能深入了解它的运行机制,又会有很多问题让你迷惑,特别是有些看起来没有问题的代码,却偏偏不能正常工作。
    比如,你会奇怪为什么不能写向下面这样的代码呢:

    CString graycat = "Gray" + "Cat";
    或者这样:

    CString graycat("Gray" + "Cat");
      事实上,编译器将抱怨上面的这些尝试。为什么呢?因为针对CString 和 LPCTSTR数据类型的各种各样的组合,“ +” 运算符 被定义成一个重载操作符。而不是两个 LPCTSTR 数据类型,它是底层数据类型。你不能对基本数据(如 int、char 或者 char*)类型重载 C++ 的运算符。你可以象下面这样做:

    CString graycat = CString("Gray") + CString("Cat");
    或者这样:

    CString graycat = CString("Gray") + "Cat";
    研究一番就会发现:“ +”总是使用在至少有一个 CString 对象和一个 LPCSTR 的场合。

    注意,编写有 Unicode 意识的代码总是一件好事,比如:

    CString graycat = CString(_T("Gray")) + _T("Cat");
    这将使得你的代码可以直接移植。

    char* 转化为 CString

      现在你有一个 char* 类型的数据,或者说一个字符串。怎么样创建 CString 对象呢?这里有一些例子:

    char * p = "This is a test";
    或者象下面这样更具有 Unicode 意识:

    TCHAR * p = _T("This is a test")

    LPTSTR p = _T("This is a test");
    你可以使用下面任意一种写法:

    CString s = "This is a test"; // 8-bit only
    CString s = _T("This is a test"); // Unicode-aware
    CString s("This is a test"); // 8-bit only
    CString s(_T("This is a test")); // Unicode-aware
    CString s = p;
    CString s(p);
      用这些方法可以轻松将常量字符串或指针转换成 CString。需要注意的是,字符的赋值总是被拷贝到 CString 对象中去的,所以你可以象下面这样操作:

    TCHAR * p = _T("Gray");
    CString s(p);
    p = _T("Cat");
    s += p;
    结果字符串肯定是“GrayCat”。

    CString 类还有几个其它的构造函数,但是这里我们不考虑它,如果你有兴趣可以自己查看相关文档。

    事实上,CString 类的构造函数比我展示的要复杂,比如:

    CString s = "This is a test";
      这是很草率的编码,但是实际上它在 Unicode 环境下能编译通过。它在运行时调用构造函数的 MultiByteToWideChar 操作将 8 位字符串转换成 16 位字符串。不管怎样,如果 char * 指针是网络上传输的 8 位数据,这种转换是很有用的。

    CString 转化成 char* 之一:强制类型转换为 LPCTSTR;

      这是一种略微硬性的转换,有关“正确”的做法,人们在认识上还存在许多混乱,正确的使用方法有很多,但错误的使用方法可能与正确的使用方法一样多。
      我们首先要了解 CString 是一种很特殊的 C++ 对象,它里面包含了三个值:一个指向某个数据缓冲区的指针、一个是该缓冲中有效的字符记数以及一个缓冲区长度。 有效字符数的大小可以是从0到该缓冲最大长度值减1之间的任何数(因为字符串结尾有一个NULL字符)。字符记数和缓冲区长度被巧妙隐藏。
      除非你做一些特殊的操作,否则你不可能知道给CString对象分配的缓冲区的长度。这样,即使你获得了该0缓冲的地址,你也无法更改其中的内容,不能截短字符串,也 绝对没有办法加长它的内容,否则第一时间就会看到溢出。
      LPCTSTR 操作符(或者更明确地说就是 TCHAR * 操作符)在 CString 类中被重载了,该操作符的定义是返回缓冲区的地址,因此,如果你需要一个指向 CString 的 字符串指针的话,可以这样做:


    CString s("GrayCat");
    LPCTSTR p = s;
      它可以正确地运行。这是由C语言的强制类型转化规则实现的。当需要强制类型转化时,C++规测容许这种选择。比如,你可以将(浮点数)定义为将某个复数 (有一对浮点数)进行强制类型转换后只返回该复数的第一个浮点数(也就是其实部)。可以象下面这样:

    Complex c(1.2f, 4.8f);
    float realpart = c;
    如果(float)操作符定义正确的话,那么实部的的值应该是1.2。
      这种强制转化适合所有这种情况,例如,任何带有 LPCTSTR 类型参数的函数都会强制执行这种转换。 于是,你可能有这样一个函数(也许在某个你买来的DLL中):

    BOOL DoSomethingCool(LPCTSTR s);
    你象下面这样调用它:

    CString file("c:\\myfiles\\coolstuff")
    BOOL result = DoSomethingCool(file);
      它能正确运行。因为 DoSomethingCool 函数已经说明了需要一个 LPCTSTR 类型的参数,因此 LPCTSTR 被应用于该参数,在 MFC 中就是返回的串地址。

    如果你要格式化字符串怎么办呢?

    CString graycat("GrayCat");
    CString s;
    s.Format("Mew! I love %s", graycat);
      注意由于在可变参数列表中的值(在函数说明中是以“...”表示的)并没有隐含一个强制类型转换操作符。你会得到什么结果呢?
      一个令人惊讶的结果,我们得到的实际结果串是:

    "Mew! I love GrayCat"。
      因为 MFC 的设计者们在设计 CString 数据类型时非常小心, CString 类型表达式求值后指向了字符串,所以这里看不到任何象 Format 或 sprintf 中的强制类型转换,你仍然可以得到正确的行为。描述 CString 的附加数据实际上在 CString 名义地址之后。
      有一件事情你是不能做的,那就是修改字符串。比如,你可能会尝试用“,”代替“.”(不要做这样的,如果你在乎国际化问题,你应该使用十进制转换的 National Language Support 特性,),下面是个简单的例子:

    CString v("1.00"); // 货币金额,两位小数
    LPCTSTR p = v;
    p[lstrlen(p) - 3] = '','';
      这时编译器会报错,因为你赋值了一个常量串。如果你做如下尝试,编译器也会错:

    strcat(p, "each");
      因为 strcat 的第一个参数应该是 LPTSTR 类型的数据,而你却给了一个 LPCTSTR。

      不要试图钻这个错误消息的牛角尖,这只会使你自己陷入麻烦!

      原因是缓冲有一个计数,它是不可存取的(它位于 CString 地址之下的一个隐藏区域),如果你改变这个串,缓冲中的字符计数不会反映所做的修改。此外,如果字符串长度恰好是该字符串物理限制的长度(梢后还会讲到这个问题),那么扩展该字符串将改写缓冲以外的任何数据,那是你无权进行写操作的内存(不对吗?),你会毁换坏不属于你的内存。这是应用程序真正的死亡处方。

    CString转化成char* 之二:使用 CString 对象的 GetBuffer 方法;

      如果你需要修改 CString 中的内容,它有一个特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一个可写的缓冲指针。 如果你只是打算修改字符或者截短字符串,你完全可以这样做:

    CString s(_T("File.ext"));
    LPTSTR p = s.GetBuffer();
    LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...
    if(p != NULL)
    *p = _T(''\0'');
    s.ReleaseBuffer();
      这是 GetBuffer 的第一种用法,也是最简单的一种,不用给它传递参数,它使用默认值 0,意思是:“给我这个字符串的指针,我保证不加长它”。当你调用 ReleaseBuffer 时,字符串的实际长度会被重新计算,然后存入 CString 对象中。
      必须强调一点,在 GetBuffer 和 ReleaseBuffer 之间这个范围,一定不能使用你要操作的这个缓冲的 CString 对象的任何方法。因为 ReleaseBuffer 被调用之前,该 CString 对象的完整性得不到保障。研究以下代码:

    CString s(...);

    LPTSTR p = s.GetBuffer();

    //... 这个指针 p 发生了很多事情

    int n = s.GetLength(); // 很糟D!!!!! 有可能给出错误的答案!!!

    s.TrimRight(); // 很糟!!!!! 不能保证能正常工作!!!!

    s.ReleaseBuffer(); // 现在应该 OK

    int m = s.GetLength(); // 这个结果可以保证是正确的。

    s.TrimRight(); // 将正常工作。
      假设你想增加字符串的长度,你首先要知道这个字符串可能会有多长,好比是声明字符串数组的时候用:

    char buffer[1024];
    表示 1024 个字符空间足以让你做任何想做得事情。在 CString 中与之意义相等的表示法:

    LPTSTR p = s.GetBuffer(1024);
      调用这个函数后,你不仅获得了字符串缓冲区的指针,而且同时还获得了长度至少为 1024 个字符的空间(注意,我说的是“字符”,而不是“字节”,因为 CString 是以隐含方式感知 Unicode 的)。
      同时,还应该注意的是,如果你有一个常量串指针,这个串本身的值被存储在只读内存中,如果试图存储它,即使你已经调用了 GetBuffer ,并获得一个只读内存的指针,存入操作会失败,并报告存取错误。我没有在 CString 上证明这一点,但我看到过大把的 C 程序员经常犯这个错误。
      C 程序员有一个通病是分配一个固定长度的缓冲,对它进行 sprintf 操作,然后将它赋值给一个 CString:

    char buffer[256];
    sprintf(buffer, "%......", args, ...); // ... 部分省略许多细节
    CString s = buffer;
    虽然更好的形式可以这么做:

    CString s;
    s.Format(_T("%...."), args, ...);
    如果你的字符串长度万一超过 256 个字符的时候,不会破坏堆栈。

      另外一个常见的错误是:既然固定大小的内存不工作,那么就采用动态分配字节,这种做法弊端更大:

    int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;

    char * buffer = new char[len];

    sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);

    CString s = buffer;

    ......

    delete [] buffer;
    它可以能被简单地写成:

    CString s;

    s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
      需要注意 sprintf 例子都不是 Unicode 就绪的,尽管你可以使用 tsprintf 以及用 _T() 来包围格式化字符串,但是基本 思路仍然是在走弯路,这这样很容易出错。

    CString to char * 之三:和控件的接口;

      我们经常需要把一个 CString 的值传递给一个控件,比如,CTreeCtrl。MFC为我们提供了很多便利来重载这个操作,但是 在大多数情况下,你使用“原始”形式的更新,因此需要将墨某个串指针存储到 TVINSERTITEMSTRUCT 结构的 TVITEM 成员中。如下:

    TVINSERTITEMSTRUCT tvi;
    CString s;
    // ... 为s赋一些值。
    tvi.item.pszText = s; // Compiler yells at you here
    // ... 填写tvi的其他域
    HTREEITEM ti = c_MyTree.InsertItem(&tvi);
      为什么编译器会报错呢?明明看起来很完美的用法啊!但是事实上如果你看看 TVITEM 结构的定义你就会明白,在 TVITEM 结构中 pszText 成员的声明如下:

    LPTSTR pszText;
    int cchTextMax;
      因此,赋值不是赋给一个 LPCTSTR 类型的变量,而且编译器无法知道如何将赋值语句右边强制转换成 LPCTSTR。好吧,你说,那我就改成这样:

    tvi.item.pszText = (LPCTSTR)s; //编译器依然会报错。
      编译器之所以依然报错是因为你试图把一个 LPCTSTR 类型的变量赋值给一个 LPTSTR 类型的变量,这种操作在C或C++中是被禁止的。你不能用这种方法 来滥用常量指针与非常量指针概念,否则,会扰乱编译器的优化机制,使之不知如何优化你的程序。比如,如果你这么做:

    const int i = ...;
    //... do lots of stuff
    ... = a[i]; // usage 1
    // ... lots more stuff
    ... = a[i]; // usage 2
      那么,编译器会以为既然 i 是 const ,所以 usage1和usage2的值是相同的,并且它甚至能事先计算好 usage1 处的 a[i] 的地址,然后保留着在后面的 usage2 处使用,而不是重新计算。如果你按如下方式写的话:

    const int i = ...;
    int * p = &i;
    //... do lots of stuff
    ... = a[i]; // usage 1
    // ... lots more stuff
    (*p)++; // mess over compiler''s assumption
    // ... and other stuff
    ... = a[i]; // usage 2
      编译器将认为 i 是常量,从而 a[i] 的位置也是常量,这样间接地破坏了先前的假设。因此,你的程序将会在 debug 编译模式(没有优化)和 release 编译模式(完全优化)中反映出不同的行为,这种情况可不好,所以当你试图把指向 i 的指针赋值给一个 可修改的引用时,会被编译器诊断为这是一种伪造。这就是为什么(LPCTSTR)强制类型转化不起作用的原因。
      为什么不把该成员声明成 LPCTSTR 类型呢?因为这个结构被用于读写控件。当你向控件写数据时,文本指针实际上被当成 LPCTSTR,而当你从控件读数据 时,你必须有一个可写的字符串。这个结构无法区分它是用来读还是用来写。

    因此,你会常常在我的代码中看到如下的用法:

    tvi.item.pszText = (LPTSTR)(LPCTSTR)s;
      它把 CString 强制类型转化成 LPCTSTR,也就是说先获得改字符串的地址,然后再强制类型转化成 LPTSTR,以便可以对之进行赋值操作。 注意这只有在使用 Set 或 Insert 之类的方法才有效!如果你试图获取数据,则不能这么做。
      如果你打算获取存储在控件中的数据,则方法稍有不同,例如,对某个 CTreeCtrl 使用 GetItem 方法,我想获取项目的文本。我知道这些 文本的长度不会超过 MY_LIMIT,因此我可以这样写:

    TVITEM tvi;
    // ... assorted initialization of other fields of tvi
    tvi.pszText = s.GetBuffer(MY_LIMIT);
    tvi.cchTextMax = MY_LIMIT;
    c_MyTree.GetItem(&tvi);
    s.ReleaseBuffer();
      可以看出来,其实上面的代码对所有类型的 Set 方法都适用,但是并不需要这么做,因为所有的类 Set 方法(包括 Insert方法)不会改变字符串的内容。但是当你需要写 CString 对象时,必须保证缓冲是可写的,这正是 GetBuffer 所做的事情。再次强调: 一旦做了一次 GetBuffer 调用,那么在调用 ReleaseBuffer 之前不要对这个 CString 对象做任何操作。

     5、CString 型转化成 BSTR 型

      当我们使用 ActiveX 控件编程时,经常需要用到将某个值表示成 BSTR 类型。BSTR 是一种记数字符串,Intel平台上的宽字符串(Unicode),并且 可以包含嵌入的 NULL 字符。

    你可以调用 CString 对象的 AllocSysString 方法将 CString 转化成 BSTR:

    CString s;
    s = ... ; // whatever
    BSTR b = s.AllocSysString();
      现在指针 b 指向的就是一个新分配的 BSTR 对象,该对象是 CString 的一个拷贝,包含终结 NULL字符。现在你可以将它传递给任何需要 BSTR 的接口。通常,BSTR 由接收它的组件来释放,如果你需要自己释放 BSTR 的话,可以这么做:

    ::SysFreeString(b);
      对于如何表示传递给 ActiveX 控件的字符串,在微软内部曾一度争论不休,最后 Visual Basic 的人占了上风,BSTR(“Basic String”的首字母缩写)就是这场争论的结果。

     6、BSTR 型转化成 CString 型

      由于 BSTR 是记数 Unicode 字符串,你可以用标准转换方法来创建 8 位的 CString。实际上,这是 CString 内建的功能。在 CString 中 有特殊的构造函数可以把 ANSI 转化成 Unicode,也可以把Unicode 转化成 ANSI。你同样可以从 VARIANT 类型的变量中获得 BSTR 类型的字符串,VARIANT 类型是 由各种 COM 和 Automation (自动化)调用返回的类型。

    例如,在一个ANSI程序中:

    BSTR b;
    b = ...; // whatever
    CString s(b == NULL ? L"" : b)
      对于单个的 BSTR 串来说,这种用法可以工作得很好,这是因为 CString 有一个特殊的构造函数以LPCWSTR(BSTR正是这种类型) 为参数,并将它转化成 ANSI 类型。专门检查是必须的,因为 BSTR 可能为空值,而 CString 的构造函数对于 NULL 值情况考虑的不是很周到,(感谢 Brian Ross 指出这一点!)。这种用法也只能处理包含 NUL 终结字符的单字符串;如果要转化含有多个 NULL 字符 串,你得额外做一些工作才行。在 CString 中内嵌的 NULL 字符通常表现不尽如人意,应该尽量避免。
      根据 C/C++ 规则,如果你有一个 LPWSTR,那么它别无选择,只能和 LPCWSTR 参数匹配。

    在 Unicode 模式下,它的构造函数是:

    CString::CString(LPCTSTR);
    正如上面所表示的,在 ANSI 模式下,它有一个特殊的构造函数:

    CString::CString(LPCWSTR);
      它会调用一个内部的函数将 Unicode 字符串转换成 ANSI 字符串。(在Unicode模式下,有一个专门的构造函数,该函数有一个参数是LPCSTR类型——一个8位 ANSI 字符串 指针,该函数将它加宽为 Unicode 的字符串!)再次强调:一定要检查 BSTR 的值是否为 NULL。
      另外还有一个问题,正如上文提到的:BSTRs可以含有多个内嵌的NULL字符,但是 CString 的构造函数只能处理某个串中单个 NULL 字符。 也就是说,如果串中含有嵌入的 NUL字节,CString 将会计算出错误的串长度。你必须自己处理它。如果你看看 strcore.cpp 中的构造函数,你会发现 它们都调用了lstrlen,也就是计算字符串的长度。
      注意从 Unicode 到 ANSI 的转换使用带专门参数的 ::WideCharToMultiByte,如果你不想使用这种默认的转换方式,则必须编写自己的转化代码。
      如果你在 UNICODE 模式下编译代码,你可以简单地写成:


    CString convert(BSTR b)
    {
        if(b == NULL)
            return CString(_T(""));
        CString s(b); // in UNICODE mode
        return s;
    }

      如果是 ANSI 模式,则需要更复杂的过程来转换。注意这个代码使用与 ::WideCharToMultiByte 相同的参数值。所以你 只能在想要改变这些参数进行转换时使用该技术。例如,指定不同的默认字符,不同的标志集等。 CString convert(BSTR b)
    {
        CString s;
        if(b == NULL)
           return s; // empty for NULL BSTR
    #ifdef UNICODE
        s = b;
    #else
        LPSTR p = s.GetBuffer(SysStringLen(b) + 1);
        ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page
                              0,                 // no flags
                              b,                 // source widechar string
                              -1,                // assume NUL-terminated
                              p,                 // target buffer
                              SysStringLen(b)+1, // target buffer length
                              NULL,              // use system default char
                              NULL);             // don''t care if default used
        s.ReleaseBuffer();
    #endif
        return s;
    }

      我并不担心如果 BSTR 包含没有映射到 8 位字符集的 Unicode 字符时会发生什么,因为我指定了::WideCharToMultiByte 的最后两个参数为 NULL。这就是你可能需要改变的地方。

     7、VARIANT 型转化成 CString 型

      事实上,我从来没有这么做过,因为我没有用 COM/OLE/ActiveX 编写过程序。但是我在microsoft.public.vc.mfc 新闻组上看到了 Robert Quirk 的一篇帖子谈到了这种转化,我觉得把他的文章包含在我的文章里是不太好的做法,所以在这里多做一些解释和演示。如果和他的文章有相孛的地方可能是我的疏忽。
      VARIANT 类型经常用来给 COM 对象传递参数,或者接收从 COM 对象返回的值。你也能自己编写返回 VARIANT 类型的方法,函数返回什么类型 依赖可能(并且常常)方法的输入参数(比如,在自动化操作中,依赖与你调用哪个方法。IDispatch::Invoke 可能返回(通过其一个参数)一个 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 类型的结果,(详见 MSDN 上的 VARIANT 结构的定义)。在下面的例子中,假设 类型是一个BSTR的变体,也就是说在串中的值是通过 bsrtVal 来引用,其优点是在 ANSI 应用中,有一个构造函数会把 LPCWCHAR 引用的值转换为一个 CString(见 BSTR-to-CString 部分)。在 Unicode 模式中,将成为标准的 CString 构造函数,参见对缺省::WideCharToMultiByte 转换的告诫,以及你觉得是否可以接受(大多数情况下,你会满意的)。VARIANT vaData;
    vaData = m_com.YourMethodHere();
    ASSERT(vaData.vt == VT_BSTR);
    CString strData(vaData.bstrVal);
    你还可以根据 vt 域的不同来建立更通用的转换例程。为此你可能会考虑:


    CString VariantToString(VARIANT * va)
    {
        CString s;
        switch(va->vt)
          { /* vt */
           case VT_BSTR:
              return CString(vaData->bstrVal);
           case VT_BSTR | VT_BYREF:
              return CString(*vaData->pbstrVal);
           case VT_I4:
              s.Format(_T("%d"), va->lVal);
              return s;
           case VT_I4 | VT_BYREF:
              s.Format(_T("%d"), *va->plVal);
           case VT_R8:
              s.Format(_T("%f"), va->dblVal);
              return s;
           ... 剩下的类型转换由读者自己完成
           default:
              ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
              return CString("");
          } /* vt */
    }

     8、载入字符串表资源

      如果你想创建一个容易进行语言版本移植的应用程序,你就不能在你的源代码中直接包含本土语言字符串 (下面这些例子我用的语言都是英语,因为我的本土语是英语),比如下面这种写法就很糟:CString s = "There is an error";
      你应该把你所有特定语言的字符串单独摆放(调试信息、在发布版本中不出现的信息除外)。这意味着向下面这样写比较好:

    s.Format(_T("%d - %s"), code, text);
      在你的程序中,文字字符串不是语言敏感的。不管怎样,你必须很小心,不要使用下面这样的串:

    // fmt is "Error in %s file %s"
    // readorwrite is "reading" or "writing"
    s.Format(fmt, readorwrite, filename);
      这是我的切身体会。在我的第一个国际化的应用程序中我犯了这个错误,尽管我懂德语,知道在德语的语法中动词放在句子的最后面,我们的德国方面的发行人还是苦苦的抱怨他们不得不提取那些不可思议的德语错误提示信息然后重新格式化以让它们能正常工作。比较好的办法(也是我现在使用的办法)是使用两个字符串,一个用 于读,一个用于写,在使用时加载合适的版本,使得它们对字符串参数是非敏感的。也就是说加载整个格式,而不是加载串“reading”,“writing”:

    // fmt is "Error in reading file %s"
    // "Error in writing file %s"
    s.Format(fmt, filename);
      一定要注意,如果你有好几个地方需要替换,你一定要保证替换后句子的结构不会出现问题,比如在英语中,可以是主语-宾语,主语-谓语,动词-宾语的结构等等。
      在这里,我们并不讨论 FormatMessage,其实它比 sprintf/Format 还要有优势,但是不太容易和CString 结合使用。解决这种问题的办法就是我们按照参数出现在参数表中的位置给参数取名字,这样在你输出的时候就不会把他们的位置排错了。
      接下来我们讨论我们这些独立的字符串放在什么地方。我们可以把字符串的值放入资源文件中的一个称为 STRINGTABLE 的段中。过程如下:首先使用 Visual Studio 的资源编辑器创建一个字符串,然后给每一个字符串取一个ID,一般我们给它取名字都以 IDS_开头。所以如果你有一个信息,你可以创建一个字符串资源然后取名为 IDS_READING_FILE,另外一个就取名为 IDS_WRITING_FILE。它们以下面的形式出现在你的 .rc 文件中:

    STRINGTABLE
    IDS_READING_FILE "Reading file %s"
    IDS_WRITING_FILE "Writing file %s"
    END
    注意:这些资源都以 Unicode 的格式保存,不管你是在什么环境下编译。他们在Win9x系统上也是以Unicode 的形式存在,虽然 Win9x 不能真正处理 Unicode。
    然后你可以这样使用这些资源:
    // 在使用资源串表之前,程序是这样写的:


       CString fmt;
          if(...)
            fmt = "Reading file %s";
         else
           fmt = "Writing file %s";
      ...
        // much later
      CString s;
      s.Format(fmt, filename);

    // 使用资源串表之后,程序这样写:     CString fmt;
            if(...)
               fmt.LoadString(IDS_READING_FILE);
            else
               fmt.LoadString(DS_WRITING_FILE);
        ...
          // much later
        CString s;
        s.Format(fmt, filename);

      现在,你的代码可以移植到任何语言中去。LoadString 方法需要一个字符串资源的 ID 作为参数,然后它从 STRINGTABLE 中取出它对应的字符串,赋值给 CString 对象。 CString 对象的构造函数还有一个更加聪明的特征可以简化 STRINGTABLE 的使用。这个用法在 CString::CString 的文档中没有指出,但是在 构造函数的示例程序中使用了。(为什么这个特性没有成为正式文档的一部分,而是放在了一个例子中,我记不得了!)——【译者注:从这句话看,作者可能是CString的设计者。其实前面还有一句类似的话。说他没有对使用GetBuffer(0)获得的指针指向的地址是否可读做有效性检查 】。这个特征就是:如果你将一个字符串资源的ID强制类型转换为 LPCTSTR,将会隐含调用 LoadString。因此,下面两个构造字符串的例子具有相同的效果,而且其 ASSERT 在debug模式下不会被触发:CString s;
    s.LoadString(IDS_WHATEVER);
    CString t( (LPCTSTR)IDS_WHATEVER );
    ASSERT(s == t);//不会被触发,说明s和t是相同的。
      现在,你可能会想:这怎么可能工作呢?我们怎么能把 STRINGTABLE ID 转化成一个指针呢?很简单:所有的字符串 ID 都在1~65535这个范围内,也就是说,它所有的高位都是0,而我们在程序中所使用的指针是不可能小于65535的,因为程序的低 64K 内存永远也不可能存在的,如果你试图访问0x00000000到0x0000FFFF之间的内存,将会引发一个内存越界错误。所以说1~65535的值不可能是一个内存地址,所以我们可以用这些值来作为字符串资源的ID。
      我倾向于使用 MAKEINTRESOURCE 宏显式地做这种转换。我认为这样可以让代码更加易于阅读。这是个只适合在 MFC 中使用的标准宏。你要记住,大多数的方法即可以接受一个 UINT 型的参数,也可以接受一个 LPCTSTR 型的参数,这是依赖 C++ 的重载功能做到的。C++重载函数带来的 弊端就是造成所有的强制类型转化都需要显示声明。同样,你也可以给很多种结构只传递一个资源名。

    CString s;
    s.LoadString(IDS_WHATEVER);
    CString t( MAKEINTRESOURCE(IDS_WHATEVER));
    ASSERT(s == t);
      告诉你吧:我不仅只是在这里鼓吹,事实上我也是这么做的。在我的代码中,你几乎不可能找到一个字符串,当然,那些只是偶然在调试中出现的或者和语言无关的字符串除外。

     9、CString 和临时对象

      这是出现在 microsoft.public.vc.mfc 新闻组中的一个小问题,我简单的提一下,这个问题是有个程序员需要往注册表中写入一个字符串,他写道:
      我试着用 RegSetValueEx() 设置一个注册表键的值,但是它的结果总是令我困惑。当我用char[]声明一个变量时它能正常工作,但是当我用 CString 的时候,总是得到一些垃圾:"&Yacute;&Yacute;&Yacute;&Yacute;...&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;"为了确认是不是我的 CString 数据出了问题,我试着用 GetBuffer,然后强制转化成 char*,LPCSTR。GetBuffer 返回的值是正确的,但是当我把它赋值给 char* 时,它就变成垃圾了。以下是我的程序段:

    char* szName = GetName().GetBuffer(20);
    RegSetValueEx(hKey, "Name", 0, REG_SZ,
                 (CONST BYTE *) szName,
                 strlen (szName + 1));
    这个 Name 字符串的长度小于 20,所以我不认为是 GetBuffer 的参数的问题。

    真让人困惑,请帮帮我。

    亲爱的 Frustrated,

    你犯了一个相当微妙的错误,聪明反被聪明误,正确的代码应该象下面这样:


    CString Name = GetName();
    RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,
                        (CONST BYTE *) (LPCTSTR)Name,
                        (Name.GetLength() + 1) * sizeof(TCHAR));

      为什么我写的代码能行而你写的就有问题呢?主要是因为当你调用 GetName 时返回的 CString 对象是一个临时对象。参见:《C++ Reference manual》§12.2
      在一些环境中,编译器有必要创建一个临时对象,这样引入临时对象是依赖于实现的。如果编译器引入的这个临时对象所属的类有构造函数的话,编译器要确保这个类的构造函数被调用。同样的,如果这个类声明有析构函数的话,也要保证这个临时对象的析构函数被调用。
      编译器必须保证这个临时对象被销毁了。被销毁的确切地点依赖于实现.....这个析构函数必须在退出创建该临时对象的范围之前被调用。
      大部分的编译器是这样设计的:在临时对象被创建的代码的下一个执行步骤处隐含调用这个临时对象的析构函数,实现起来,一般都是在下一个分号处。因此,这个 CString 对象在 GetBuffer 调用之后就被析构了(顺便提一句,你没有理由给 GetBuffer 函数传递一个参数,而且没有使用ReleaseBuffer 也是不对的)。所以 GetBuffer 本来返回的是指向这个临时对象中字符串的地址的指针,但是当这个临时对象被析构后,这块内存就被释放了。然后 MFC 的调试内存分配器会重新为这块内存全部填上 0xDD,显示出来刚好就是“&Yacute;”符号。在这个时候你向注册表中写数据,字符串的内容当然全被破坏了。
      我们不应该立即把这个临时对象转化成 char* 类型,应该先把它保存到一个 CString 对象中,这意味着把临时对象复制了一份,所以当临时的 CString 对象被析构了之后,这个 CString 对象中的值依然保存着。这个时候再向注册表中写数据就没有问题了。
      此外,我的代码是具有 Unicode 意识的。那个操作注册表的函数需要一个字节大小,使用lstrlen(Name+1) 得到的实际结果对于 Unicode 字符来说比 ANSI 字符要小一半,而且它也不能从这个字符串的第二个字符起开始计算,也许你的本意是 lstrlen(Name) + 1(OK,我承认,我也犯了同样的错误!)。不论如何,在 Unicode 模式下,所有的字符都是2个字节大小,我们需要处理这个问题。微软的文档令人惊讶地对此保持缄默:REG_SZ 的值究竟是以字节计算还是以字符计算呢?我们假设它指的是以字节为单位计算,你需要对你的代码做一些修改来计算这个字符串所含有的字节大小。

     10、CString 的效率

      CString 的一个问题是它确实掩藏了一些低效率的东西。从另外一个方面讲,它也确实可以被实现得更加高效,你可能会说下面的代码:CString s = SomeCString1;
    s += SomeCString2;
    s += SomeCString3;
    s += ",";
    s += SomeCString4;
    比起下面的代码来,效率要低多了:

    char s[1024];
    lstrcpy(s, SomeString1);
    lstrcat(s, SomeString2);
    lstrcat(s, SomeString 3);
    lstrcat(s, ",");
    lstrcat(s, SomeString4);
      总之,你可能会想,首先,它为 SomeCString1 分配一块内存,然后把 SomeCString1 复制到里面,然后发现它要做一个连接,则重新分配一块新的足够大的内存,大到能够放下当前的字符串加上SomeCString2,把内容复制到这块内存 ,然后把 SomeCString2 连接到后面,然后释放第一块内存,并把指针重新指向新内存。然后为每个字符串重复这个过程。把这 4 个字符串连接起来效率多低啊。事实上,在很多情况下根本就不需要复制源字符串(在 += 操作符左边的字符串)。
      在 VC++6.0 中,Release 模式下,所有的 CString 中的缓存都是按预定义量子分配的。所谓量子,即确定为 64、128、256 或者 512 字节。这意味着除非字符串非常长,连接字符串的操作实际上就是 strcat 经过优化后的版本(因为它知道本地的字符串应该在什么地方结束,所以不需要寻找字符串的结尾;只需要把内存中的数据拷贝到指定的地方即可)加上重新计算字符串的长度。所以它的执行效率和纯 C 的代码是一样的,但是它更容易写、更容易维护和更容易理解。
      如果你还是不能确定究竟发生了怎样的过程,请看看 CString 的源代码,strcore.cpp,在你 vc98的安装目录的 mfc\src 子目录中。看看 ConcatInPlace 方法,它被在所有的 += 操作符中调用。

    啊哈!难道 CString 真的这么"高效"吗?比如,如果我创建

    CString cat("Mew!");
      然后我并不是得到了一个高效的、精简的5个字节大小的缓冲区(4个字符加一个结束字符),系统将给我分配64个字节,而其中59个字节都被浪费了。
      如果你也是这么想的话,那么就请准备好接受再教育吧。可能在某个地方某个人给你讲过尽量使用少的空间是件好事情。不错,这种说法的确正确,但是他忽略了事实中一个很重要的方面。
      如果你编写的是运行在16K EPROMs下的嵌入式程序的话,你有理由尽量少使用空间,在这种环境下,它能使你的程序更健壮。但是在 500MHz, 256MB的机器上写 Windows 程序,如果你还是这么做,它只会比你认为的“低效”的代码运行得更糟。
      举例来说。字符串的大小被认为是影响效率的首要因素,使字符串尽可能小可以提高效率,反之则降低效率,这是大家一贯的想法。但是这种想法是不对的,精确的内存分配的后果要在程序运行了好几个小时后才能体现得出来,那时,程序的堆中将充满小片的内存,它们太小以至于不能用来做任何事,但是他们增加了你程序的内存用量,增加了内存页面交换的次数,当页面交换的次数增加到系统能够忍受的上限,系统则会为你的程序分配更多的页面,直到你的程序占用了所有的可用内存。由此可见,虽然内存碎片是决定效率的次要因素,但正是这些因素实际控制了系统的行为,最终,它损害了系统的可靠性,这是令人无法接受的。
      记住,在 debug 模式下,内存往往是精确分配的,这是为了更好的排错。
      假设你的应用程序通常需要连续工作好几个月。比如,我常打开 VC++,Word,PowerPoint,Frontpage,Outlook Express,Forté Agent,Internet Explorer和其它的一些程序,而且通常不关闭它们。我曾经夜以继日地连续用 PowerPoint 工作了好几天(反之,如果你不幸不得不使用像 Adobe FrameMaker 这样的程序的话,你将会体会到可靠性的重要;这个程序机会每天都要崩溃4~6次,每次都是因为用完了所有的空间并填满我所有的交换页面)。所以精确内存分配是不可取的,它会危及到系统的可靠性,并引起应用程序崩溃。
      按量子的倍数为字符串分配内存,内存分配器就可以回收用过的内存块,通常这些回收的内存块马上就可以被其它的 CString 对象重新用到,这样就可以保证碎片最少。分配器的功能加强了,应用程序用到的内存就能尽可能保持最小,这样的程序就可以运行几个星期或几个月而不出现问题。
      题外话:很多年以前,我们在 CMU 写一个交互式系统的时候,一些对内存分配器的研究显示出它往往产生很多内存碎片。Jim Mitchell,现在他在 Sun Microsystems 工作,那时侯他创造了一种内存分配器,它保留了一个内存分配状况的运行时统计表,这种技术和当时的主流分配器所用的技术都不同,且较为领先。当一个内存块需要被分割得比某一个值小的话,他并不分割它,因此可以避免产生太多小到什么事都干不了的内存碎片。事实上他在内存分配器中使用了一个浮动指针,他认为:与其让指令做长时间的存取内存操作,还不如简单的忽略那些太小的内存块而只做一些浮动指针的操作。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是对的。
      永远不要认为所谓的“最优化”是建立在每一行代码都高速且节省内存的基础上的,事实上,高速且节省内存应该是在一个应用程序的整体水平上考虑的。在软件的整体水平上,只使用最小内存的字符串分配策略可能是最糟糕的一种方法。
      如果你认为优化是你在每一行代码上做的那些努力的话,你应该想一想:在每一行代码中做的优化很少能真正起作用。你可以看我的另一篇关于优化问题的文章《Your Worst Enemy for some thought-provoking ideas》。
      记住,+= 运算符只是一种特例,如果你写成下面这样:

    CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
    则每一个 + 的应用会造成一个新的字符串被创建和一次复制操作。

     总结

      以上是使用 CString 的一些技巧。我每天写程序的时候都会用到这些。CString 并不是一种很难使用的类,但是 MFC 没有很明显的指出这些特征,需要你自己去探索、去发现。

    June 27

    【转】VC常用数据类型转换详解

     VC常用数据类型使用转换详解
    读者层次:初学
    刚接触VC编程的朋友往往对许多数据类型的转换感到迷惑不解,本文将介绍一些常用数据类型的使用。


    CString passwd = "123456";

    long    pwd    = atol(passwd.GetBuffer(0));
    passwd.ReleaseBuffer(); 

    理解上面代码:

    首先要明白的是CString 是一个类,long是一种数据类型。

    先取得CString 对象中的内容,再将它转化即可。

    注意:包含头文件<stdlib.h>

    2.CString 转换到unsigned long

    void CString2Long(CString str,unsigned long lVal) 
    {
             sscanf((LPSTR)(LPCTSTR)str,   "%d",   &lVal);
    }

    3. CString-->char *

    CString str("Test Building");
    char *pchar=str.GetBuffer(str.GetLength());
    str.ReleaseBuffer();
     

    CString str;

    char * p="string";

    str.Format(p);

    5.Int,Float,Char *,CString之间的转换

    Int 转CString :

    int n=1;

    CString str;

    str.Format("%d",n);

    ____________________________________________________________________________________

    CString 转 Int::

    CString str="1";

    int n=atoi(str.GetBuffer(0));


    char * 转 CString:

    char sz[128];

    CString str;

    str.Format("%s",sz);


    CString 转 char *:

    CString str;

    int nLength=str.GetLength();

    char * sz=new char[nLength];

    sz=str.GetBuffer(0);


    Float 转 CString:

    float f=0.0;

    CString str;

    str.Format("%f",f);


     CString 转 Float:

    CString str="0.0";

    float f=atof(str.GetBuffer(0));

    6.CString::Format 格式化双精度为整型数值

    CString::Format("%d",xx);其中xx不能为双精度,不然会转化错误,应该使用CString::Format("%d",(LONG)xx);
    例:
    DOUBLE d = 22.443;
    CString str;
    str.Format("%d", d);转化后的str = "1924145349",这是错误的。

    我们先定义一些常见类型变量借以说明

    int i = 100;
    long l = 2001;
    float f=300.2;
    double d=12345.119;
    char username[]="程佩君";
    char temp[200];
    char *buf;
    CString str;
    _variant_t v1;
    _bstr_t v2;

    一、其它数据类型转换为字符串


    短整型(int)
    itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制
    itoa(i,temp,2); ///按二进制方式转换
    长整型(long)
    ltoa(l,temp,10);
    浮点数(float,double)
    用fcvt可以完成转换,这是MSDN中的例子:
    int decimal, sign;
    char *buffer;
    double source = 3.1415926535;
    buffer = _fcvt( source, 7, &decimal, &sign );
    运行结果:source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0
    decimal表示小数点的位置,sign表示符号:0为正数,1为负数
    CString变量
    str = "2008北京奥运";
    buf = (LPSTR)(LPCTSTR)str;
    BSTR变量
    BSTR bstrValue = ::SysAllocString(L"程序员");
    char * buf = _com_util::ConvertBSTRToString(bstrValue);
    SysFreeString(bstrValue);
    AfxMessageBox(buf);
    delete(buf);
    CComBSTR变量
    CComBSTR bstrVar("test");
    char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
    AfxMessageBox(buf);
    delete(buf);

    _bstr_t变量
    _bstr_t类型是对BSTR的封装,因为已经重载了=操作符,所以很容易使用
    _bstr_t bstrVar("test");
    const char *buf = bstrVar;///不要修改buf中的内容
    AfxMessageBox(buf);


    通用方法(针对非COM数据类型)
    用sprintf完成转换
    char  buffer[200];
    char  c = '1';
    int   i = 35;
    long  j = 1000;
    float f = 1.7320534f;
    sprintf( buffer, "%c",c);
    sprintf( buffer, "%d",i);
    sprintf( buffer, "%d",j);
    sprintf( buffer, "%f",f);

    二、字符串转换为其它数据类型
    strcpy(temp,"123");

    短整型(int)
    i = atoi(temp);
    长整型(long)
    l = atol(temp);
    浮点(double)
    d = atof(temp);
    CString变量
    CString name = temp;
    BSTR变量
    BSTR bstrValue = ::SysAllocString(L"程序员");
    ...///完成对bstrValue的使用
    SysFreeString(bstrValue);

    CComBSTR变量
    CComBSTR类型变量可以直接赋值
    CComBSTR bstrVar1("test");
    CComBSTR bstrVar2(temp);

    _bstr_t变量
    _bstr_t类型的变量可以直接赋值
    _bstr_t bstrVar1("test");
    _bstr_t bstrVar2(temp);


    三、其它数据类型转换到CString
    使用CString的成员函数Format来转换,例如:


    整数(int)
    str.Format("%d",i);
    浮点数(float)
    str.Format("%f",i);
    字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值
    str = username;
    对于Format所不支持的数据类型,可以通过上面所说的关于其它数据类型转化到char *的方法先转到char *,然后赋值给CString变量。

    四、BSTR、_bstr_t与CComBSTR


    CComBSTR 是ATL对BSTR的封装,_bstr_t是C++对BSTR的封装,BSTR是32位指针,但并不直接指向字串的缓冲区。
    char *转换到BSTR可以这样:
    BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib
    SysFreeString(bstrValue);
    反之可以使用
    char *p=_com_util::ConvertBSTRToString(b);
    delete p;
    具体可以参考一,二段落里的具体说明。

    CComBSTR与_bstr_t对大量的操作符进行了重载,可以直接进行=,!=,==等操作,所以使用非常方便。
    特别是_bstr_t,建议大家使用它。


    五、VARIANT 、_variant_t 与 COleVariant


    VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。
    对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型,再对联合结构中相同数据类型的变量赋值,举个例子:
    VARIANT va;
    int a=2001;
    va.vt=VT_I4;///指明整型数据
    va.lVal=a; ///赋值

    对于不马上赋值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系:

    Byte bVal;  // VT_UI1.
    Short iVal;  // VT_I2.
    long lVal;  // VT_I4.
    float fltVal;  // VT_R4.
    double dblVal;  // VT_R8.
    VARIANT_BOOL boolVal;  // VT_BOOL.
    SCODE scode;  // VT_ERROR.
    CY cyVal;  // VT_CY.
    DATE date;  // VT_DATE.
    BSTR bstrVal;  // VT_BSTR.
    DECIMAL FAR* pdecVal  // VT_BYREF|VT_DECIMAL.
    IUnknown FAR* punkVal;  // VT_UNKNOWN.
    IDispatch FAR* pdispVal;  // VT_DISPATCH.
    SAFEARRAY FAR* parray;  // VT_ARRAY|*.
    Byte FAR* pbVal;  // VT_BYREF|VT_UI1.
    short FAR* piVal;  // VT_BYREF|VT_I2.
    long FAR* plVal;  // VT_BYREF|VT_I4.
    float FAR* pfltVal;  // VT_BYREF|VT_R4.
    double FAR* pdblVal;  // VT_BYREF|VT_R8.
    VARIANT_BOOL FAR* pboolVal;  // VT_BYREF|VT_BOOL.
    SCODE FAR* pscode;  // VT_BYREF|VT_ERROR.
    CY FAR* pcyVal;  // VT_BYREF|VT_CY.
    DATE FAR* pdate;  // VT_BYREF|VT_DATE.
    BSTR FAR* pbstrVal;  // VT_BYREF|VT_BSTR.
    IUnknown FAR* FAR* ppunkVal;  // VT_BYREF|VT_UNKNOWN.
    IDispatch FAR* FAR* ppdispVal;  // VT_BYREF|VT_DISPATCH.
    SAFEARRAY FAR* FAR* pparray;  // VT_ARRAY|*.
    VARIANT FAR* pvarVal;  // VT_BYREF|VT_VARIANT.
    void FAR* byref;  // Generic ByRef.
    char cVal;  // VT_I1.
    unsigned short uiVal;  // VT_UI2.
    unsigned long ulVal;  // VT_UI4.
    int intVal;  // VT_INT.
    unsigned int uintVal;  // VT_UINT.
    char FAR * pcVal;  // VT_BYREF|VT_I1.
    unsigned short FAR * puiVal;  // VT_BYREF|VT_UI2.
    unsigned long FAR * pulVal;  // VT_BYREF|VT_UI4.
    int FAR * pintVal;  // VT_BYREF|VT_INT.
    unsigned int FAR * puintVal;  //VT_BYREF|VT_UINT.


    _variant_t是VARIANT的封装类,其赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
    使用时需加上#include <comdef.h>
    例如:
    long l=222;
    ing i=100;
    _variant_t lVal(l);
    lVal = (long)i;


    COleVariant的使用与_variant_t的方法基本一样,请参考如下例子:
    COleVariant v3 = "字符串", v4 = (long)1999;
    CString str =(BSTR)v3.pbstrVal;
    long i = v4.lVal;


    六、其它一些COM数据类型

    根据ProgID得到CLSID
    HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
    CLSID clsid;
    CLSIDFromProgID( L"MAPI.Folder",&clsid);

    根据CLSID得到ProgID
    WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
    例如我们已经定义了 CLSID_IApplication,下面的代码得到ProgID
    LPOLESTR pProgID = 0;
    ProgIDFromCLSID( CLSID_IApplication,&pProgID);
    ...///可以使用pProgID
    CoTaskMemFree(pProgID);//不要忘记释放

    七、ANSI与Unicode
    Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。

    将ANSI转换到Unicode
    (1)通过L这个宏来实现,例如: CLSIDFromProgID( L"MAPI.Folder",&clsid);
    (2)通过MultiByteToWideChar函数实现转换,例如:
    char *szProgID = "MAPI.Folder";
    WCHAR szWideProgID[128];
    CLSID clsid;
    long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
    szWideProgID[lLen] = '\0';
    (3)通过A2W宏来实现,例如:
    USES_CONVERSION;
    CLSIDFromProgID( A2W(szProgID),&clsid);
    将Unicode转换到ANSI
    (1)使用WideCharToMultiByte,例如:
    // 假设已经有了一个Unicode 串 wszSomeString...
    char szANSIString [MAX_PATH];
    WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
    (2)使用W2A宏来实现,例如:
    USES_CONVERSION;
    pTemp=W2A(wszSomeString);
    八、其它

    对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如:
    LPARAM lParam;
    WORD loValue = LOWORD(lParam);///取低16位
    WORD hiValue = HIWORD(lParam);///取高16位


    对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如:
    WORD wValue;
    BYTE loValue = LOBYTE(wValue);///取低8位
    BYTE hiValue = HIBYTE(wValue);///取高8位


    两个16位数据(WORD)合成32位数据(DWORD,LRESULT,LPARAM,或WPARAM)
    LONG MAKELONG( WORD wLow, WORD wHigh );
    WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
    LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
    LRESULT MAKELRESULT( WORD wLow, WORD wHigh );


    两个8位的数据(BYTE)合成16位的数据(WORD)
    WORD MAKEWORD( BYTE bLow, BYTE bHigh );


    从R(red),G(green),B(blue)三色得到COLORREF类型的颜色值
    COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );
    例如COLORREF bkcolor = RGB(0x22,0x98,0x34);


    从COLORREF类型的颜色值得到RGB三个颜色值
    BYTE Red = GetRValue(bkcolor); ///得到红颜色
    BYTE Green = GetGValue(bkcolor); ///得到绿颜色
    BYTE Blue = GetBValue(bkcolor); ///得到兰颜色


    九、注意事项
    假如需要使用到ConvertBSTRToString此类函数,需要加上头文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, "comsupp.lib" )


    (转)对话框及其空间颜色改变

    要改变对话框的一般颜色,可以在C***App的InitInstance函数里加入如下代码:
    SetDialogBkColor(RGB(
    0,255,255),RGB(255,0,0));//背景青蓝、文字红色


    但是这样不能改变对话框中的图形控件的颜色;
    每个控件在dialog中都是一个窗口,当要绘制控件时,会发出一个WM_CTLCOLOR消息给它的父窗口(对话框本身).消息映射及响应函数如下:
    ON_WM_CRLCOLOR()  //反映WM_CTLCOLOR消息
    afx_msg HBRUSH CWnd::OnCrlColor(CDC* pDC,CWnd* pWnd,UINT nCtlColor);
    //
    nCtlColor类型:
    CTLCOLOR_DLG对话框本身,不含有所有的控件
    CTLCOLOR_STATIC 所有包含static text控件的设置(也包括无效的edit box、combo box的eidt box)
    CTLCOLOR_EDIT     edit box与combo box的eidt box部分
    CTLCOLOR_LIST     list box与combo box的edit box部分
    CTLCOLOR_SCROLLBAR  Scroll bar的空白区

    我们也可以单独改变某一个控件的颜色.OnCtrlColor函数的pWnd成员变量是各控件的窗口指针,以pWnd->GetDlgCtrlID()可以获得调用此函数的控件ID,将它与已知ID比较,就可以改变这个控件的颜色。另外一个作法是比较pWnd;例如:如果一个edit box控件的成员变量m_edit1被声明为CEdit类,那么把m_edit1.m_hWnd与pWnd->m_hWnd比较,也可以知道是哪个控件.
    下面是一个例子:

    HBRUSH C***Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
    {
        HBRUSH hbr 
    =
     CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
        
        
    // TODO: Change any attributes of the DC here

    if(nCtlColor== CTLCOLOR_LISTBOX)
     {
      HBRUSH m_hbrush;
      m_hbrush
    =CreateSolidBrush(RGB(0,0,0
    ));
      pDC
    ->
    SetBkMode(TRANSPARENT);
      pDC
    ->SetTextColor(RGB(255,255,255
    ));
      
    return
     m_hbrush;
     }
    CBrush m_cBrush;
        m_cBrush.CreateSolidBrush(RGB(
    0,255,255
    ));
        
    switch
    (nCtlColor)
        {
        
    case
     CTLCOLOR_DLG:
            
    return
     m_cBrush;
            
    break
    ;
        
    case
     CTLCOLOR_STATIC:
            pDC
    ->SetTextColor(RGB(255,0,0
    ));
            pDC
    ->SetBkColor(RGB(0,255,255
    ));
            
    return
     (HBRUSH)m_cBrush;
            
    break
    ;
        }
        DeleteObject(m_cBrush);
        
    // TODO: Return a different brush if the default is not desired

        return hbr;
    }

    VC中遍历文件夹下的所有文件和文件夹

    (转)在VC中怎么遍历一个文件夹下的所有文件和文件夹?

     find(char * lpPath)
    {
        char szFind[MAX_PATH];
        WIN32_FIND_DATA FindFileData;

        strcpy(szFind,lpPath);
        strcat(szFind,"\\*.*");

        HANDLE hFind=::FindFirstFile(szFind,&FindFileData);
        if(INVALID_HANDLE_VALUE == hFind)    return;
       
        while(TRUE)
        {
            if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if(FindFileData.cFileName[0]!='.')
                {
                    strcpy(szFile,lpPath);
                    strcat(szFile,"\\");
                    strcat(szFile,FindFileData.cFileName);
                    find(szFile);
                }
            }
            else
            {
                cout << FindFileData.cFileName;
            }
            if(!FindNextFile(hFind,&FindFileData))    break;
        }
        FindClose(hFind);
    }

    (转)VC 获得系统时间、程序运行时间、本机计算机名、IP

    1.使用CTime类

    CString str;
     //获取系统时间
     CTime tm;
     tm=CTime::GetCurrentTime();
     str=tm.Format("现在时间是%Y年%m月%d日  %X");
     MessageBox(str,NULL,MB_OK);

    2: 得到系统时间日期(使用GetLocalTime)

    SYSTEMTIME st;
    CString strDate,strTime;
    GetLocalTime(&st);
    strDate.Format("%4d-%2d-%2d",st.wYear,st.wMonth,st.wDay);
    strTime.Format("%2d:%2d:%2d",st.wHour,st.wMinute,st.wSecond);

    3.使用GetTickCount
    //获取程序运行时间
     long t1=GetTickCount();//程序段开始前取得系统运行时间(ms)
     Sleep(500);
     long t2=GetTickCount();();//程序段结束后取得系统运行时间(ms)
     str.Format("time:%dms",t2-t1);//前后之差即 程序运行时间
     AfxMessageBox(str);
    //获取系统运行时间
    long t=GetTickCount();
     CString str,str1;
     str1.Format("系统已运行 %d时",t/3600000);
     str=str1;
     t%=3600000;
     str1.Format("%d分",t/60000);
     str+=str1;
     t%=60000;
     str1.Format("%d秒",t/1000);
     str+=str1;
     AfxMessageBox(str);

    *******************************************************************************

    4、获得本地计算机名和IP地址

    CString m_IP;//IP地址临时变量
     //获得本地计算机名
     char PCnameBuffer[128];
     if(0==gethostname(PCnameBuffer,128))
     {
      m_PCname=PCnameBuffer;
      //获得本地IP地址
      struct hostent* pHost;
      int i;
      pHost=gethostbyname(PCnameBuffer);//pHost返回的是指向主机的列表
      for (i=0;pHost!=NULL&&pHost->h_addr_list[i]!=NULL;i++)
      {
       LPCSTR psz = inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]);//得到指向ip的psz变量
       m_IP += psz;   
      }
      m_LocalIP=m_IP;
     }
     else
     {
      AfxMessageBox("获取本地IP失败!");
      return;
     }

    April 10

    遐想

        上班仍旧很无聊,《鬼吹灯》看了好久也没看完,拿起VC,办公室里只能看看这类书了。像在大学里自习一样,看专业书看着看着,思想就飞走了,看了许久仍然停留在那一页上面,于是常常抛开必须要看书的压力,让思绪就这么飘开去好了,爱想什么想什么。。。。。。自毕业以来近两年了,总觉得自己过得很忙碌,像是赶着跑着地过日子,应酬太多聚会太多,虽然开心可真觉得有点累不过来,好想歇一歇停一停过过自己安排的生活。人是群居动物,喜欢热闹,可有时候真的需要独处一会儿,喘口气儿!就像坐公交车,虽然每天要早起,要挤来挤去,我还是喜欢坐公交车,上下班的途中要坐近一个小时的车,和车上的人都不认识,不需要应酬那么多话题,沿途就静静看窗外的风景,静静想自己想要想的事,虽然并不安静,但那一刻似乎真的很宁静!在现在喧闹紧张的时间空间里,想要寻找片刻的安宁,已经不是那么容易的事情了。所以,突然寻找到片刻的自己的空间的时候,很高兴!
    March 29

    江西婺源-中国最美丽的乡村D3

         总没有时间好好坐下来写写博客,今天总算可以来完成这次婺源之行了,免得又像上次南京之行一样,到后来就不想写了!
    D3:
         为了想要在出发前争取点时间再看看美丽的古村落,为了能在一切苏醒之前看看宁静的乡村,我和飞鱼昨晚约定了第二天要早起。第二天6点不到,我们就起来了。走出帐篷,映入眼帘的是昨晚见到的那棵千年古樟,昨天因为晚上到的,所以就感觉它像一定大黑伞,庄重而沉寂。今天早晨,则是白雾蒙蒙之中一片苍翠,像一个老人,静静地等待世界的苏醒。大致整理好帐篷之后,我和飞鱼一人叼着一根火腿肠,走近雾蒙蒙的村庄。虹关村子不大,和晓起比起来,显得更古老一些,一条条的小巷子从中间向两侧延伸,直伸到村子外围的田野,每条巷子的头上都标明了巷子的名字,看来这里不久以后,也会像晓起一样成为景区了吧,那时候还会是这么宁静古朴的小村子吗?有时候出来玩,看到尚未开发的原生态,显得是那么惊喜,就像昨天白天看到的那个不知名的村庄一样,捕捉到的那种古朴自然的美丽,不是语言和相机所能记录的。然而一旦有了商业化的气息之后,原本的那种美丽就像是化了妆,虽然也很漂亮,却失却了那种自然,感觉一切都是雕刻出来的。这次来到婺源,比较庆幸的一点就是这里大多还没有被商业化,看到的古村落还保持着那种自然的生活方式,所以感觉一切都还比较真实,我真的有点担心游人多了以后,这里就会变个味了。
         8点半左右,我们的行李都叫了另一辆车给运出去了,我们一行人则找了个向导,从山间的小路走出去,到浙源就会有我们的车子了。接下来的两个多小时也就是我们这次出行的唯一一次徒步了。之间穿过了两个村子,上了两次小山坡,再走一段大路,就到了。这一路的风景还真是不错的,发现这一带有很多大大小小的古村落,都沿河而建,有些村子里几乎每家院子或门前都会种有梨树,此刻梨花已经开了,之间还有一些桃花盛开,合着青山和白墙黛瓦的房屋,倒映在水中,真有种世外桃源的感觉。我们穿行在油菜花和古村落中间,两个小时的行程一会儿就过去了。
          到了浙源后稍作休息,我们坐车返回,打算找一处干净的水源,解决我们的午饭问题。这里到处都是青山绿水,找一处干净的活水也并不难,很快,我们就在一处大溪流的旁边开锅造饭,很多人还趟过溪去对面的石滩上野炊。我们和小牛等几个找了一处平整的地方,煮我们的多料方便面,旁边就是油菜花田,还有一畦青菜,我们到里面割了几株,将硬币放在每株菜的根上,就当跟农民伯伯买的了。一顿午饭吃得很开心。在对岸的一行人趟过溪来的时候,我们都拿石块往溪水里面扔,过去容易返回难啊,要让他们懂得这个道理。^_^
          下午1:30,坐上大巴,回杭!途径安徽,沿着盘山公路上上下下,还有漫山遍野甚至种到山顶上去的油菜花,叹为观止!
    March 27

    江西婺源——最美丽的乡村2

    D2:
        不等西瓜掀帐篷,我们6点钟就醒了。虽然只睡了4个小时,但精神总体还是不
    错的。钻出帐篷,从学校的走廊上望出去,雾蒙蒙的便是一片青山绿树。下了一夜的雨,现在已经停了,樟树间雾气缭绕,似真似幻。梳洗完毕,大家伙儿都煮起了早饭,我和飞鱼胡乱吃了点干粮,带上相机,迫不及待地想要去看看清晨的晓起。(梳洗处隔壁就是小学生的宿舍,一溜儿的横铺;门口栓着的绳子上打着一个个的结,仔细看却是袜子,原来他们是这样子晾袜子的。)
        飞鱼念念不忘她的花生,就这么一路提着磕过去了。走出小学门口右手边便是
    一片田园,种着金黄的油菜花,原来学校就建在油菜花丛中了。中间一条小路,通往下晓起,我们住的地方是上晓起。左手边看过去便是一片白墙黑瓦的上晓起了,中间一条巷子蜿蜒至村里深处。雨洗过的青石板路格外的干净,我们踩着青石板路,走进了村子。婺源这边都是典型的徽派建筑,白墙黑瓦,骑马墙,小开窗,门厅里有个天井,让雨水流进来,意即财源滚滚来。即使是新造的房子也都还继承了这种建筑风格,只是省去了天井,把窗户开得大了些。这时候,晓起人很多都已经起
    床了,开店的都摆出了摊子,我们买了几个热乎乎的包子,慰劳一下我们的胃。上晓起不大,我们兜了一圈就回来了,本想沿着小路再往野外去看看的,可半路上被一条横躺着的大蚯蚓吓着了,头也不回的就往回走。
         回到小学,大家已经准备行装准备离开了,我们也赶紧收拾好,和大家一起在小学的小型篮球场上合了影。(之所以称之为小型篮球场,不单单因为场地小,就连那篮球架也特别小,连我这么个个儿都能轻松灌篮了^_^)。
         把行装都放到车上后,我们轻装上路,再细细地浏览一下晓起村。早上已经走
    了一遍上晓起了,于是我们绕上了小路,前往下晓起。这时下起了细细的雨丝,像给大地拢上了一层轻纱,迷迷蒙蒙地更显乡村的美丽与清新。这时路上的行人已经多起来了,有本地的村民,也有像我们一样来玩的游客,宁静的乡村一时间热闹起来了。一路傍着油菜花,弯弯曲曲地走到了下晓起。村口有一条大溪,中间有落差的上方铺着一条木板的小桥,溪水的两岸各有一棵大樟树,树枝伸向河面,构成一道“门”。因下过了雨,水流稍微大一点,从小桥上过去,弄湿了行人的鞋子。沿
    着溪边一路走过去,才发现溪水稍微上游一点的地方还有一座石桥的,为什么这里的人都走那条木片子小桥呢,误导我弄湿了鞋子。
         大约一个半小时后,我们告别了晓起,驱车前往油菜花观赏圣地——江岭。一
    路上,已经对满山遍野的金黄色唏嘘不已。到了目的地后,大伙儿都迫不及待想要融入这万花丛中去了。道路在修,有点泥泞,丝毫不影响游人的兴致,也正好免去了灰尘,我们沿着大路上山,越往上走,就越多的惊喜。江岭的油菜花是梯田式的种植方式,所以从下面往上看就是一条条的黄线,而从上面俯瞰的话,便是层层叠叠一片金黄。四周青山环绕,雾气蒸腾,山脚下金黄一片,几个村落点缀其中,一条溪水从中穿过,不知道陶渊明见到的桃花源是否便是这个样子?我们看过了全景
    之后,当然要近距离地去感受一下置身于花丛中的感觉。沿着田塍一路向下,估计飞鱼也和我一样,全然忘记了外面的世界,眼中只有这油菜花了。
         中午FB了一餐,在农家吃农家饭。
         饭后本要前往官坑口,但我们的车太大,开不过去,开了一半只能折回,没想
    到上山容易下山难,过了中午时间游人越来越多,本就不大的一条山路上挤满了车,费了好大劲儿,才把车从这山路上开出去,我们齐声为师傅喝彩!
        下午就这么坐着车一路过去,到了彩虹桥,因门票与景色不值,所以没有去看那座廊桥,掉转回头去我们今天的营地——虹关,半路上看到一处景色甚美,于是把车停靠一边,去那个不知名的村落兜转一圈。村子不大,依山傍水,河旁边一弯月牙形的油菜花,格外引人注目。村子里的房子也是清一色的徽派建筑,很多家门都没有关,我们在猜测不知是家里有人还是应了那句话:夜不闭户!
         去虹关原来是那么不容易,从大马路上绕进一条山路,就跟我们这车一边宽,
    师傅就这么把个金龙大巴沿着弯弯曲曲的山路开进去了,有几处落差大的地方,看不到路面,一车人就这么提着心的到达了目的地。这一段插曲也为我们的旅程增添了一点刺激。
         虹关有一棵大樟树,足有一千多年的历史了,我们十个人才能环抱过来。樟树
    过去,就是虹关村子了,也是清一色的白墙黑瓦,青石板的小巷。晚上我们夜宿樟树旁边的篮球场。
    March 26

    江西婺源-美丽的乡村

        和一群玩户外的朋友一起,走进了中国最美丽的乡村——江西婺源。
    D1:周五晚7:30出发,在一路欢声笑语中,我们的大巴,踏上了前往江西婺源的旅途。领队中的西瓜老大,很快就把气氛调动起来,一开始的破冰游戏,让一群互不认识的人,很快成为了旅途上的伙伴。我们将在一起度过两天的时间。一路游戏做做,路上的6个小时显得不是那么长了,到晓起村已是0:30,因下着小雨,我们就驻扎在村里的一个小学的教室里,搭好帐篷睡时已是凌晨1点多了,赶紧睡觉,明天还得起个早来看看清晨的乡村呢!
     
    March 12

    植树节

    今天植树节,种下2007年的愿望:
     
    希望自己、家人和朋友都身体健康,每天都心情愉快!
    希望有一份好工作,并且乐在其中!
    希望大家爱情甜蜜,不想再听到谁谁谁分手了,希望听到谁谁谁要结婚了!
    希望和朋友长相聚,多吃饭,少喝酒!
    希望所有人都没病没灾,希望杭州不要这么拥挤,地铁赶快造好,公交车不要这么拥挤,交通不再堵塞!
    希望物价低一点,房价降一点,每个人都居有其所,大家生活好一点,牢骚抱怨少一点!
    希望地球环境回到20年前,夏天有暴雨,冬天有白雪!
     
    今天种下希望,用一生来栽培!