2008年08月19日

梦见喜鹊,代表好运与感情。
梦见喜鹊闹枝头,预示着做梦人有好的事情即将来到;
梦见喜鹊在自由自在地飞,表明做梦人的运气越来越好;
梦见喜鹊搭成鹊桥,预示着做梦人将有一段刻骨铭心的感情。
“梦见喜鹊”的解梦原理:喜鹊是中国的吉祥鸟,它象征着富贵吉祥、幸福美满、喜事临门。
来源周公解梦

2007年09月25日

原帖地址不详, 摘自<拂晓雅阁>

http://bbs.055.cc/viewthread.php?tid=390662&extra=&page=1

1.豪勇七蛟龙The Magnificent Seven
大型颁奖晚会最喜欢用的背景音乐。《七侠荡寇志》(1960)主题曲,著名电影音乐作曲家埃尔默.伯恩斯坦创作
http://www.aebc.com/~shtuet/m7.mp3

2.故乡的原风景
神雕侠侣》多次引用,哀伤感人。出自日本作曲家宗次郎1991年的专辑《木道》
http://www.guilin163.net/chat/music/lei/dz.mp3

3.CCTV《天气预报》主题曲
据说是迄今为止中央电视台唯一没有改变过的背景音乐,《天气预报》一直使用它。
http://bbs.958shop.com/webdesign/uploadothers/20061111330132901338540437.mp3
《渔舟唱晚》(即天气预报背景音乐),是当年在上海颇有名气的电子琴演奏家浦琪璋根据同名民族乐曲改编演奏的。
渔舟唱晚(电子版) -浦琪璋http://bbs.958shop.com/webdesign/uploadothers/20061111330132901338540437.mp3
渔舟唱晚(古筝版)
http://www.mengshan.jsol.net/jsyd/yykt/mp3/yuzhouwanchang.mp3
渔舟唱晚(轻音乐) (此版本较少)
http://bbs.958shop.com/webdesign/uploadothers/20061111330132901338540437.mp3

4.简单的礼物(Simple Gifts)
美国VOA广播电台(美国之音)的SPECIAL ENGLISH(慢速音乐)节目的背景音乐吗?太熟悉了,只不过电台版的速度要比这个快一些。
http://ftp.21xp.net:89/mlnn/%C3%83%C3%8E%C3%80%C3%AF%C3%84%C3%98%C3%A0%C2%AB/%C2%B1%C2%B3%C2%BE%C2%B0%C3%92%C3%B4%C3%80%C3%96%C3%96%C2%AE%C3%82%C3%83/%C2%BC%C3%B2%C2%B5%C2%A5%C2%B5%C3%84%C3%80%C3%B1%C3%8E%C3%AF(Simple-Gifts).mp3

5.雪的梦幻(Snowdreams)
这首《雪的梦幻》(Snowdreams)出自班德瑞的春野这张专辑。相当经典的纯音乐,被电台和电视台使用的次数已经无法统计,常在一些情感类(尤其爱情,有一点淡淡的哀伤)的播讲中充当背景音乐。
http://218.14.146.210/show/W10034/snowdreams.mp3

6.童年(Childhood Memory)
这首《童年》(Childhood Memory)出自班德瑞的《日光海岸》这张专辑。确实曲如其名,让人回想起过去的时光,听了有种想哭的冲动……
http://www1.hrbust.edu.cn/zuzhijigou/gonghui/music/03/02.mp3

7.宋家王朝(THE SOONG SISTER)
这首《宋家王朝》出自日本作曲大师喜多郎之手,个人感觉既恢弘又凄婉,港台的电视剧多爱用此背景音乐,比如李若彤版《神雕侠侣》。
http://www.lsty.cn/bbs/uploadfile/2006-3/20063232275357.mp3

8.你的笑颜(Your Smile)
这首曲子出自班德瑞的《仙境》这张专辑,似乎常被用电台作为午夜节目的背景音乐。相对于其它几首情感节目的背景音乐,这首曲子并不显得特别悲伤和哀婉,但是一样会触到你的神经
http://music.7xy.com.cn/08.YOUR%20SMILE.mp3

9.春野(ONE DAY IN SPRING)
《春野》(ONE DAY IN SPRING)出自班德瑞的专辑《春野》,常被用作节目开头的曲子或是新闻、起床曲的开头背景音乐。是一首非常舒缓柔美的经典乐
http://www.xlgt.com/file/music/spring/spring01.mp3

10.安妮的仙境(Annie’’s Wonderland
《安妮的仙境》(Annies Wonderland)出自班德瑞的《仙境》这张专辑,柔美而有力度,适合朗诵配乐。
http://www.gzjzl.com/back.mp3

11.凤凰卫视《天气预报》主题曲
这首《和兰花在一起》(With an orchid)出自Yanni(雅尼)的专辑《If I Could Tell You》,被凤凰卫视用于天气预报的背景音乐。
http://club.lanyue.com/upload/user/51/332031_26175144.mp3

12.神秘园之歌(Song From A Secret Garden
这首《神秘园之歌》出自神秘园的第一张专辑《SONGS FROM A SECRETGARDEN》。据说这首歌伤感能杀死人,让人象迷失在神秘的丛林里,黑暗的看不到天……
http://download.52samsung.com:82/52yingyin/sanwu/SG03.mp3

13.Windancer
这首曲子出自神秘园的专辑《White stone》(白石),悠扬舒畅,有品味的书店和FLASH动画都比较偏爱这首曲子。
http://www.kxcyz.com/ghhomepage/tiantzhy/windancer.mp3

14.火之战车CHARIOTS OF FIRE
很雄壮有力的曲子,大家应该很熟悉,OSCAR影片《火之战车》的主题曲,体育台的常用背景音乐
http://liren.51liao.com/gongju/zhanche.mp3

15.泪花(TEARS)
纯美的钢琴曲,经常能听到它。多年后,如果我们相逢,我将以何来面汝,以沉默以眼泪…… 忧伤的琴键中,我却觉得自己被安慰
http://www.xgjk.com/music/tears.mp3

16.再见police(goodbye)
似乎是一首意大利歌曲,然后被国产影视作品广为引用,最明显的就是《无间道》里《再见police》这首背景音乐使用的就是它。原曲《Amazing Grace》中文翻译为《奇异恩典
http://kfcs1.56.com/audiomail/streams/d3/3/25/andyzcool@56.com_56flv_1152944299_518.mp3

17.柔板(Adagio)
《Adagio》(柔板)很柔美,就像它的名字一样,也是很能引发感触的好作品,听了居然不免要落泪,足见其感染力之强!有一些广告就用其作为背景音乐,另外电台的一些情感节目也有用其作为背景音乐。而且,该曲也是朗诵配乐不可多得的好曲子(该音乐出自专辑《Songs From A Secret Garden》)。
http://www.nihao8.com/qq47329193/music/adagio.mp3

18. 英雄的黎明
很恢弘的一首曲子,在日本动画片《三国志》中出现过,也在95版《神雕侠侣》出现过。
http://www.huaee.cn/music/sgz.mp3

19.王都炎上
新白娘子传奇里用过,记得青姐花园站的青姐弹过的,叫断桥。水漫金山后许仙与白娘子在断桥相见时用到过。
http://images.bcct.cn/resourceDir/music/2587.mp3

20.爱的罗曼史(Romance De Amor)
学吉他的朋友必弹的曲目。经常在爱情电视剧和青春电视剧出现的曲目。
http://bbs.leowood.net/web/music/romance.mp3

21.反璞归真(Return To Innocence)
英格玛《The cross of changes》里的经典曲目,中国人寿保险在CCTV上做的广告就用其作背景音乐.
http://202.201.0.179/mp3/commend/enigma.mp3

22.天地孤影任我行
东邪西毒-《天地孤影任我行》,那电影开场时开山碎石的背景音乐,极其苍凉雄浑,后来大话西游里紫霞临死这段音乐飘扬的时候,我已忍不住潸然泪下
http://www.qhtz.com/UploadFiles/200671011584888.mp3

23. Go West-向西行
-各电视台体育节目常用,Pet Shop Boys(宠物店小子)经典曲目
http://bbs.ziboren.com/down/media/2006/PETSHOPBOYSGoWest.mp3

24.Forrest Gump – Suite Forrest Gump
阿甘正传一首令人感到温暖的交响乐,它将和电影一起深深埋藏在我们的灵魂深处,每当听起这首曲子,我便想起了阿甘,他用自己的坎坷经历让我们懂得了如何珍惜眼前的一切,也让我们明白了逆境并不能将我们打垮,我们应该去努力寻找自己的幸福…
http://bbs.hifi168.com/bbs/Upload/200641523153391570.mp3

25.Variations on the Kanon by Pachelbel
在电影《我的野蛮女友》中,当全智贤的钢琴声在大学阶梯课室里响起的时候,满脸尴尬的车太贤情不自禁穿过人群,将准备以久的玫瑰送给全智贤。这时满场一片喝彩鼓掌之声,也让我们对爱情充满了无尽的幻想
http://www.v181.com/vip/clon/music/ym.mp3

26.Daydream 白日梦
英国BBC广播网夜间节目配乐常客
http://www.baiduedu.com/soft/UploadFile/yuwen/@baiduedu2006415173328681_com.mp3

27.Greek Meditation 古诗
各电视台情感节目用曲
http://chatyy.com/bbs/xupload/demo/08_Greek_Meditation-9273.mp3

28.闲云孤鹤
央视节目间过度背景音乐-闲云孤鹤-刘星作品,音乐发烧友肯定都有他的唱片
http://hd4.15150.com/music10/2006_4_14/36060_100326_10615.mp3

29.印度之花
Blossoms From India-Sea of Flower 印度之花,在孟买土生土长的瑞肯(REIKAN)深受古老印度精神之启发,在5岁时即开始接触音乐,后来在各名西塔琴音乐家RANDIT 的启蒙下完成它的,来自远古的声音,曾作为德国BOSS香水广告配乐,混杂了东方的神秘与西方的现代。
http://tin8.com/UpLoadMtv/200611381187969.mp3

30.喜多郎 silk road
乐曲“丝绸之路”,是当时只有27岁的喜多郎的成功之作。当时这位年轻的日本音乐人并没有来过中国,也没有在这条千年古道上行走,但是喜多郎凭借着对中国音乐的间接认识和音乐人对历史特殊感悟,创作出了充满中国韵味的丝绸之路乐曲,并由此一举成名。
http://file.maxduo.com/0892/0615/pkkp1121/eb685966-043b-4823-b835-234ed5d230a2.mp3

31.Caribbean_Blue加勒比海蓝
加勒比海蓝,经典再现-Moonlight Bay
http://www.dbjc.net/vip/py/%C2%BC%C3%93%C3%80%C3%95%C2%B1%C3%88%C2%BA%C2%A3%C3%80%C2%B6.mp3

32.神思者 sens kaishin
日本的乐团-(神思)专集kaishin [海神]中的 – Aphrodite阿普洛迪,发烧唱片《悲情城市》该知道吧,就是sens的作品。奇怪的是该曲与《迷情仙境》那张唱片中的一曲Returning Heart Garden(重返心灵花园)几乎一模一样,可曲名又不同,还望高人指点。还有啊,配乐大师詹姆斯.霍纳为《勇敢的心》创作的主题曲怎么和这首曲子也有几分相似呢?
http://www.hgtw.org/bbs/images/upfile/2006-5/200652820118.mp3

33.Bamboo Dance竹舞
水光涟漪之竹舞Bamboo Dance 邵容演奏,我喜欢,我推荐,相信我,没错的
http://www.gaoguomodel.com/music/banboo.mp3

34.SONG FOR THE LORD
SONG FOR THE LORD献给阁下的歌-班得瑞音乐-寂静山林
http://www.dhxx.net.cn/bbs/images/upfile/2005-2/200522617546.mp3

35.弥撒(The Mass)
各电视台军事节目-弥撒(The Mass)
http://www.ebc5.com/uploadfiles/2005119123028_366901123532_Era-The_mass.mp3

36.Nightingale夜莺
雅尼 [Yanni]-Nightingale夜莺
http://www.cqcdbs.com/bskj/bsxxkjw/wangye/yinyue/mp3/yani00.mp3

37.Children
在瑞典出生的Robert Miles,在九十年代以一曲Children大受人们瞩目。此碟更被誉为是“全欧史上最畅销跳舞细碟”,Robert Miles 也因“Children”一曲而声名大噪!虽为舞蹈曲目,但好多电台DJ都拿它作动感的背景音乐,尤其是汽车音乐,爽呆咯
http://www.sxgroups.com.cn/images/Children.mp3

38.CCTV动物世界片头曲 Just Blue
这首Just Blue 出自Space乐队的1978年专辑《Just Blue》。 Space,法国著名的流行电子乐队(synth-pop ),这张专辑是他们最好的专辑,也是最“太空”的专辑,音乐天马行空,旋律美妙无比,在欧洲拥有一大批忠实听众。下面是2个版本,第一个是片中音乐。
http://hbcw.hndt.com/bbs/UploadFile/2006-4/200641813264983257.mp3

片中音乐
http://hbcw.hndt.com/bbs/UploadFile/2006-4/200641813264983257.mp3
原版音乐
http://ftp.21xp.net:89/mlnn/%C3%83%C3%8E%C3%80%C3%AF%C3%84%C3%98%C3%A0%C2%AB/%C2%B1%C2%B3%C2%BE%C2%B0%C3%92%C3%B4%C3%80%C3%96%C3%96%C2%AE%C3%82%C3%83/CCTV%C2%B6%C2%AF%C3%8E%C3%AF%C3%8A%C3%80%C2%BD%C3%A7%C3%86%C2%AC%C3%8D%C2%B7(Just-Blue).mp3

39.CCTV动物世界片尾曲 we stay
这首《we stay》,选自专辑《The Arctic Circle 》,来自英国的SKY乐队。音乐属于介于轻音乐和NEW AGE MUSIC之间,探索的成分较浓。音乐中运用了多种乐器,糅合了自然声息强烈的流行音乐风味音律流畅!
http://ftp.21xp.net:89/mlnn/%C3%83%C3%8E%C3%80%C3%AF%C3%84%C3%98%C3%A0%C2%AB/%C2%B1%C2%B3%C2%BE%C2%B0%C3%92%C3%B4%C3%80%C3%96%C3%96%C2%AE%C3%82%C3%83/CCTV%C2%B6%C2%AF%C3%8E%C3%AF%C3%8A%C3%80%C2%BD%C3%A7%C3%86%C2%AC%C3%8E%C2%B2(we-stay).mp3

40.LOVE SONGS FEAST(爱的筵席),CCTV《对话》节目插曲
又是一首哼唱音乐,直入灵魂深处…… 本曲是CCTV《对话》节目插曲,在每次介绍对话嘉宾取得辉煌成就的录像里,常常就是这首空灵的,充满谜一样绚丽色彩的曲子。该曲名为《LOVE SONGS FEAST》(爱的筵席)。莎拉布莱曼专辑《重回伊甸园》中的曲目《SCENE DAMOUR》(爱情场景)也是该曲目。鸡和蛋谁先有,无法考证
http://xihai.vip.sina.com/down/Love%20Songs%20Feast.mp3

41.One Man’s Dream CCTV《对话》主题曲
雅尼最为精彩的一张现场专辑——《Yanni Live At The Acropolis》中的一首《One man’s dream》,每次从cctv-2看那档《对话》的节目时,心里都是一阵莫明的激动。看似卑微的人类,可以在一生中有如此的成就。尤其是当他们年长的时候,回头看看时,或者和我们这些正处于卑微状态中的人对话时。
http://hbcw.hndt.com/bbs/UploadFile/2005-10/200510231014594042.mp3

42.Short Trip Home(回家的路很短)
最早听到这曲就是荷兰队被拒在02年世界杯门外时候CCTV5放了这一曲子。后来在世界杯的时候每每强队要告别世界杯时,这支哀怨的曲子就随之响起,它将人的心情渲染得淋漓尽致。这首Short Trip Home 是专辑《HEARTLAND 》里边的曲子,专辑是约夏贝尔,马友友(大提琴),艾格麦尔(蓝调+摇滚派的低音大提琴演奏家)及马克欧康(乡村小提琴)几个人的精选合成集子。
http://www.fifabbs.com/thx/shorttriphome.mp3

43.永恒依然-汪正正在奥运期间推出的新曲
“是谁波动天地之旅,燃起力量穿越无边……当一切慢慢遥远,永恒依然。”歌手汪正正在奥运期间推出的新曲《永恒依然》近日在中央电视台的几个频道同时播出,成为申奥的代表歌曲和未来广告公司的形象代言歌曲。
http://blog.chinaitlab.com/user1/335946/upload/20062198517.mp3

44.Don’t turn off the light 不要关灯-央视体育频道天下足球背景音乐
拉美音乐领域最炙手可热的人物enrique iglesias,其父就是大名鼎鼎julio iglesias(胡里奥·伊格莱西亚斯)。
http://www.yinducotton.com/music/enriqueiglesias_dontturnoffthelight.mp3

45.MagicBoulevard —世界影视博览的背景音乐-法语歌曲
这首歌是Francois Feldman 98年同名专辑Magic’boul’vard中的一首,
《Magic boulevard》(魔力大道)描述的是关于一个电影院领座员唯美而忧伤的心情。
http://zyx.jxfz.gov.cn/music/MagicBoulevard.mp3

46. Through The Arbor穿过树荫-央视节目间过度背景音乐
著名钢琴家凯文·柯恩Kevin Kern 以他享有盛誉的美丽钢琴调子来捕捉我们的心,这是他最具有代表性之经典作,曲调不经修饰,却能轻轻松松地令我们不期然地跟着他走进“满园春色关不住”的林荫,不愧是钢琴大师。
http://www.cat-nest.com/mp3/Kevin%20Kern/1996-In%20the%20Enchanted%20Garden/01%20Through%20the%20Arbor.MP3

47.《勇闯夺命岛》(THE ROCK)主题音乐Rock House Jail
HOLLYWOOD 动作影片《勇闯夺命岛》(THE ROCK)中的第3段配乐,本曲相当有气魄,节奏感很强,他的主创 Hans Zimmer。这段音乐已经被CCT那个V给用滥了,节目里有警-察追击的镜头出现,往往就能听到这段音乐,估计没交版权费;国内有个垃圾电视剧也曾经盗用过,好可耻,我的鼻子就是那时给气歪了
http://www.tfmm.net/music/admin/uploadsong/02.%20Rock%20House%20Jail.mp3

48.Bluebird 蓝鸟
各电视台节目大量引用 ,出自詹姆斯·拉斯特乐队,最受欢迎、流行最广的《蓝鸟》(Bluebird),它不仅保持了乐团原有特色,而且在风格上与其他改编作品有所不同。
http://www.bbsland.org/music/bluebird/bluebird.mp3

49.North Gate – Ron Carnel-TVB瞬间看地球背景音乐
凤凰卫视TVB瞬间看地球的背景音乐
http://www.pctt.com.cn/yinyue/157.mp3

50.Star Of Baghdad巴格达之星
《巴格达之星》(Star Of Baghdad)出自班德瑞的专辑《仙境》,依然是听了让人觉得有点伤感的曲子
http://www1.hrbust.edu.cn/zuzhijigou/gonghui/music/02/17.mp3

51.THE GREAT SMOKY MOUNTAINS- 大烟山-美国国家公园系列
探索频道配乐大师融合全球五大洲音乐元素,呈现精彩听觉冒险。
http://fzqq.cn/dayanshan.mp3

2007年08月14日

此帖为转贴,非本人作品,谢谢!
星际、魔兽3、红警完全对比
这是一篇足够搞笑的文章,特献给热爱即时战略的玩家们!
相信您对这个主题一定有很多自己的意见和看法,建议您在看完本文章后再发表:)

星际、魔兽3、红警完全对比(一)
  魔兽娱乐性强 比较搞笑 你常常越玩越轻松
  星际竞技性强 比较严肃 你常常越玩越紧张
  红警政治性强 比较偏激 你常常越玩越气愤
    
  玩魔兽 就像唱卡拉ok 普通人练一首歌半个月 已经能赢得同伴的掌声
  玩星际 就像唱京戏 曲不离口的练上一年 可能还唱不上调子
  玩红警 就像说话 不用练就差不多水平 练了很多年说话的水平不见得高多少。
  
  学习魔兽 你能打赢两家疯狂电脑的时候 你和真人打就能取胜了
  学习星际 你能打赢七家电脑 你还纳闷怎么还打不过真人
  学习红警 你能打赢七家电脑1000000次 不见得和真人玩过。

  学习魔兽 两个小时你能死在相同的战术上八次 毫无还手之力
  学习星际 两个小时你能死在迥异的战术上八次 毫无还手之力
  学习红警 两年你都死在相同的得战术上无数次 毫无还手之力
  
  魔兽里面 你专心练一个族往往就能够应付对同族异族4种情况打法
  星际里面 人打虫的高手往往曾经就是虫打人的高手
  红警里面 你学会一个国家就等于学会了所有国家
    
  魔兽里面 熟练了几种套路就可以取胜
  星际里面 熟练了几种套路还是被随机应变的对手牵着鼻子走
  红警里面 熟练了几种套路,你会发现根本没用,只要熟练一种就可以了。
    
  魔兽里面 你利用计谋伏击或者包围了对方主力 对方却掏出回程扬长而走
  星际里面 你会发现不仅有游击战还有阵地战、伏击战、空投战……
  红警里面 你会发现什么战都是多余的。人多才是硬道理。
    
  魔兽里面 敌人无论离家多远都可以十秒内回救被你偷袭的基地
  星际里面 你稍不留神就中了声东击西的诡计
  红警里面 你必须时刻留神你得矿车
    
  魔兽里面 你5分钟侦察一次还能对敌人兵种搭配了如指掌
  星际里面 你5分钟侦察五次说不定得到的还是假情报
  红警里面 你5分钟侦查一次,然后就再也用不着侦查了。
   
  魔兽里面 5分钟不侦察你还能猜出来敌人部队构成
  星际里面 3分钟不侦察出门就可能全是克制你的兵种
  红警里面 不用你去侦查地图上就能看见敌人的情况
    
  魔兽里面 赢了一场大战就可以松口气 因为几乎稳操胜券
  星际里面 赢了一场大战 正得意一下却发现刚刚大战中被一支奇兵偷袭的经济全毁
  红警里面 赢了一场大战 你会觉得很幸运 你好多天都没有打过大战了。
    
  魔兽里面 大战对决常常形势一边倒
  星际里面 大战对决常常双方两败俱伤
  红警里面 大战对决常常就像已经知道了结局颁奖典礼
    
  魔兽里面 一次全军覆没99%可以打GG
  星际里面 十次全军覆说不定都不知鹿死谁手
  红警里面 经常全军覆没是一种战斗方式
  
  魔兽里面 你郁闷于虽然有顽强精神却难以在劣势中翻盘
  星际里面 你郁闷于有优势却被有顽强精神的对手翻盘
  红警里面 你郁闷于必须有对方不知道的战术才能翻盘
    
  魔兽里面 录像看到一半往往能知道结局
  星际里面 录像看到结局你才发现开始的判断错了
  红警里面 录像是什么都不知道
    
  魔兽里面 初始的基地被拆毁就失去了希望
  星际里面 两个人鏖战到调换基地位置甚至四海为家也不稀奇
  红警里面 初始基地不仅可以被拆 还可以被占 被偷 被炸 自己还可以逃跑
    
  魔兽里面 初始矿采完基本胜负就见分晓
  星际里面 全地图的资源耗尽说不定才换来一个平局
  红警里面 大家一直在抢资源很少出现平局
    
  魔兽里面 你可以龟缩防守、偏安一隅
  星际里面 你如果不及时扩张 除了初始矿点 其他矿点都有对方采矿的农民
  红警里面 你必须去抢矿 这也是一种必须走的形势。
    
  魔兽里面 你把基地门口造满防御 敌人骂你猥琐赖皮
  星际里面 你把基地门口造满防御 敌人不是直接空投到你家里就是直接一颗核弹敲开大门
  红警里面 你必须在基地里面造满防御 敌人的飞机 飞行兵才不会占到便宜
  
  魔兽里面 你可以用高级兵种轻松欺负低级兵种
  星际里面 你发现原来小机枪也能“以小反上”地打航母
  红警里面 你发现只有高级兵种才是王者
    
  魔兽里面 没有对空部队看到空军常常就要选择逃跑
  星际里面 你刚出来4个飞龙却被3队不对空的小狗强拆了基地
  红警里面 飞行兵就是制胜的关键,别的都是摆设
    
  魔兽里面 你会质疑“量变引起质变”的法则
  星际里面 你会验证“量变引起质变”的法则
  红警里面 你会质疑“有名气的公司比较负责”
    
  魔兽里面 死掉一个兵会心痛半天
  星际里面 你知道什么叫做前仆后继
  红警里面 兵就是为了死掉的。
    
  魔兽里面 作战部队不敢过于分散
  星际里面 作战常常要地图各点全面开花
  红警里面 作战就是在几个特定的地方进行

星际、魔兽3、红警完全对比(二)
  魔兽里面 即使知道敌人什么兵种配置有时候也赢不了
  星际里面 知己知彼才真的百战不殆
  红警里面 看到对方的兵种配置就知道对方的水平了
    
  魔兽里面 规矩多 玩家发挥余地小 按部就班往往比突发奇想更奏效
  星际里面 规矩少 玩家发挥余地大 按部就班往往陷于被动
  红警里面 没规矩 玩家战术就一种 突发奇想只有在对菜鸟的时候才能用
    
  魔兽里面 以不变应万变
  星际里面 以万变应不变
  红警里面 永远不变
    
  魔兽里面 1个英雄、道具可以四两拨千斤
  星际里面 1个隐形的单位可以四两拨千斤
  红警里面 1个高手对菜鸟可以四两拨千斤
    
  魔兽里面 你为那个用光环照耀部队、高人一等的英雄而感到骄傲
  星际里面 你才发现引爆地雷和对方坦克同归于尽的那个小狂徒才是真正的英雄
  红警里面 你为飞行兵拿下矿区而骄傲
    
  魔兽里面 你会发现操作被人性化设计之后 如同一部傻瓜相机
  星际里面 你会发现最简单的细节你也要亲手去处理
  红警里面 你会发现可以自己处理的事情不是很多。可以边吃零食,边和高手对战。
    
  魔兽里面 你会发现apm150的时候已经会无聊到插旗
  星际里面 你会发现apm150的时候才能勉强用用神族
  红警里面 你会发现apm150是什么你都不知道,只是到手快很有用。

  魔兽里面 你觉得12个女巫按了12次O之后同时变了对方12个羊很有成就感
  星际里面 你发现原来12运输机的地毯式空降也仅仅是操作的基本功而已
  红警里面 你认为可以让12个坦克移动中躲掉攻击,就是操作了
    
  魔兽里面 你觉得操作2队多部队围杀、齐射、魔法、道具是多么华丽
  星际里面 你才知道就连让4队雷车、2队坦克整齐行进都不容易
  红警里面 你订着炮弹看,快落地的时候让自己的坦克躲,炮弹多的时候还真不容易
    
  魔兽里面 连流星陨石都认识自己人和友军
  星际里面 一个闪电放不好 可能自己被电死的比敌人的还多
  红警里面 除了少数几个枪法好的兵种,其他都经常误伤自己人
    
  魔兽里面 常常讲这是理所当然
  星际里面 常常讲这也不是不可能
  红警里面 常常讲这是不可能的
    
  魔兽里面 常有某个玩家用某某流战术把所用的种族用成所在版本的王者之族
  星际里面 你突然发现昨天似乎无敌的偶像今天就输在某个黑马手
  红警里面 你知道自己只剩下一种战术的时候,你就是高手了。
    
  魔兽玩久了 才知道 效率是第一
  星际玩久了 才知道 数量是第一
  红警玩久了 才知道 经验是第一
    
  魔兽玩久了 才知道 等级是第一
  星际玩久了 才知道 经济是第一
  红警玩久了 才知道 兵力是第一
     
  魔兽玩久了 才知道 稳定娴熟是第一
  星际玩久了 才知道 侦察应变是第一
  红警玩久了 才知道 对偷袭方法了解是第一
  
  魔兽玩久了 才知道什么叫做战斗
  星际玩久了 才知道什么叫做战略
  红警玩久了 才知道什么叫做按部就班
    
  魔兽玩久了 你发现地图到现在为止还停留在在陆战
  星际玩久了 你发现从WCG2001开始官方地图就有岛战
  红警玩久了 你发现地图是永远不变的
    
  魔兽玩久了 你发现看rep要变换版本和收集地图实在厌烦
  星际玩久了 你发现一个400k的rep记录了一场3小时的比赛
  红警玩久了 你发现rep是什么你都不知道
  
  魔兽玩久了 你会发现总有或多或少冷板凳单位
  星际玩久了 你会发现没有一个单位是多余的
  红警玩久了 你发现高手对战大多数单位都是多余的
    
  魔兽玩久了 你会发现你所了解的魔兽知识越来越多
  星际玩久了 你会发现你所不懂的星际知识越来越多
  红警玩久了 你发现你所知道的红警知识没用的越来越多
  
  魔兽玩久了 仿佛在考验你的耐心和熟练程度一般
  星际玩久了 总有出乎你意料的东西令你眼前一亮
  红警玩久了 想睡觉

星际、魔兽3、红警完全对比(三)
  魔兽玩久了 你发现刚练熟的高效打法随着版本更新、单位修改而不再应验
  星际玩久了 你发现不但新战术发明的越来越快,而且被破解的也越来越快
  红警玩久了 你发现战术越来越单一,破解方法越来越无用。
    
  魔兽玩久了 你发现战术大多跟着补丁变
  星际玩久了 你发现战术大多跟着玩家变
  红警玩久了 你发现战术就是偷袭和反偷袭
    
  魔兽玩久了 你发现魔兽的未来掌握在补丁手里
  星际玩久了 你发现星际的未来掌握在玩家手里
  红警玩久了 你发现红警的未来掌握在新游戏手里
    
  魔兽玩久了 觉得人在被魔兽玩
  星际玩久了 觉得是人在玩星际
  红警玩久了 觉得人和红警都在被游戏公司玩
    
  魔兽玩久了 天天盼望下一个版本升级补丁调整单位属性
  星际完久了 天天盼望不要出现bug这样就不用再有新补丁诞生
  红警玩久了 天天盼望不要出新补丁,这样bug就没了。
    
  魔兽玩久了 忽然想起冰封王座1.07诞生到1.20几乎版版不同
  星际玩久了 回忆起母巢之战1.04到1.08只做过两次单位属性变动就稳定至今
  红警玩久了 算了一下10年了就出过一次补丁,还没把bug改掉
    
  魔兽玩久了 才知道魔兽三确实比星际一画面好
  星际玩久了 才知道魔兽在用孙子辈的游戏和星际一代的产品比较画面
  红警玩久了 才知道同样是爷爷辈的游戏,差距怎么就那么大呢?
    
  魔兽玩久了 才知道魔兽玩家说魔兽好 却很多都没玩过甚至听说过魔兽III的爷爷和爸爸
  星际玩久了 才知道星际的第一代已经快八岁了
  红警玩久了 才知道红警已经六年没人玩了
    
  魔兽玩久了 避免不了争论种族平衡性、英雄兵种单位bug性的口水战
  星际完久了 你问哪个族最强 大家会告诉你三族一样厉害 根据兴趣爱好选择
  红警玩久了 总是想说,咱们出飞行兵了,换种打法吧。
    
  魔兽玩久了 你不知道为什么魔兽玩家似乎也分了种族
  星际玩久了 你会发现三族来自不同星球但各族玩家却似兄弟
  红警玩久了 你会觉得每个国家几乎没有区别
  
  魔兽玩久了 你发现各族玩家往往在为维护自己所用种族而争辩
  星际玩久了 你发现无论何族玩家都在为维护共同的星际而争辩
  红警玩久了 你会发现这个游戏一直在维护某些国家的政治利益
    
  魔兽玩久了 你会品味什么是流行
  星际玩久了 你会体会什么是经典
  红警玩久了 你会明白什么是猥琐
    
  魔兽玩久了 你才知道为什么魔兽如此热门
  星际玩久了 你才知道为什么星际如此冷门
  红警玩久了 你才知道为什么红警如此热门却没有人玩
    
  魔兽玩久了 你会喜欢上魔兽 别人说魔兽不好 你会火冒三丈 恨不得打骂他
  星际玩久了 你会喜欢上星际 别人说星际不好 你会一笑而过 不屑和他争辩
  红警玩久了 你会喜欢上红警 别人说红警不好 你会火冒三丈 不知道怎么争辩
    
  魔兽玩久了 你慢慢体会到魔兽真的是一款好游戏
  星际玩久了 你慢慢体会到星际越来越不像一款游戏
  红警玩久了 你慢慢体会到一个好的公司比一款好的游戏重要的多
    
  魔兽玩久了 你发现魔兽是如此精彩的游戏 给我们带来快乐
  星际玩久了 你发现生活和思维方式已经有了星际的烙印
  红警玩久了 你发现思维方式越来越简单了
    
  魔兽玩久了 才发现原来有很多初中小朋友加入魔兽玩家行列
  星际玩久了 才发现原来有很多成家立业的“大叔”还没退出星际玩家行列
  红警玩久了 才发现原来有很多初中的小朋友和成家立业的大叔,不断加入和迅速退出这红警玩的行列 
   
  魔兽玩久了 才知道世界上最远的距离不是中国电信和网通 而是魔兽精灵玩家和兽人玩家的心
  星际玩久了 才知道 星品不好人品就不好
  红警玩久了 利用bug在红警里不算人品太不好
  
  魔兽玩久了 才知道 魔兽是暴雪制造出来的最流行的精品大作
  星际玩久了 才知道 星际是上帝借暴雪之手赐予玩家们的杰作
  红警玩久了 才知道 西屋为什么会输给暴雪

感谢耐心看完的朋友,特别的向:
  看了此帖后没有得意忘形的成熟的星际玩家致敬!  
  看了此贴后没有暴跳如雷的成熟的魔兽玩家致敬!  
  所以不要把星际当作魔兽玩,也不要把魔兽当作星际玩,各得其乐吧。祝福星际玩家、魔兽玩家都可以玩的愉快。
  
最后许下几个愿望
  对星际玩家的话:菜鸟无罪,希望星际老玩家不要歧视菜鸟,毕竟菜鸟才是星际的未来;
  对魔兽玩家的话:你的心中可以把魔兽当作神圣不可侵犯,但是你不能毁灭客观事实,客观理智的面对现实往往比敏感、脆弱、冲动更有风度,相反你就会变得非常狭隘……
  共同的就是,希望星际和魔兽作弊的越来越少。

2007年07月10日

CListCtrl使用技巧

以下未经说明,listctrl默认view 风格为report


1. CListCtrl 风格

      LVS_ICON: 为每个item显示大图标
      LVS_SMALLICON: 为每个item显示小图标
      LVS_LIST: 显示一列带有小图标的item
      LVS_REPORT: 显示item详细资料

      直观的理解:windows资源管理器,“查看”标签下的“大图标,小图标,列表,详细资料”


2. 设置listctrl 风格及扩展风格

      LONG lStyle;
      lStyle = GetWindowLong(m_list.m_hWnd, GWL_STYLE);//获取当前窗口style
      lStyle &= ~LVS_TYPEMASK; //清除显示方式位
      lStyle |= LVS_REPORT; //设置style
      SetWindowLong(m_list.m_hWnd, GWL_STYLE, lStyle);//设置style
 
      DWORD dwStyle = m_list.GetExtendedStyle();
      dwStyle |= LVS_EX_FULLROWSELECT;//选中某行使整行高亮(只适用与report风格的listctrl)
      dwStyle |= LVS_EX_GRIDLINES;//网格线(只适用与report风格的listctrl)
      dwStyle |= LVS_EX_CHECKBOXES;//item前生成checkbox控件
      m_list.SetExtendedStyle(dwStyle); //设置扩展风格
 
      注:listview的style请查阅msdn
      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceshellui5/html/wce50lrflistviewstyles.asp

 


3. 插入数据

      m_list.InsertColumn( 0, "ID", LVCFMT_LEFT, 40 );//插入列
      m_list.InsertColumn( 1, "NAME", LVCFMT_LEFT, 50 );
      int nRow = m_list.InsertItem(0, “11”);//插入行
      m_list.SetItemText(nRow, 1, “jacky”);//设置数据

 


4. 一直选中item

    选中style中的Show selection always,或者在上面第2点中设置LVS_SHOWSELALWAYS


5. 选中和取消选中一行

    int nIndex = 0;
    //选中
    m_list.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
    //取消选中
    m_list.SetItemState(nIndex, 0, LVIS_SELECTED|LVIS_FOCUSED);
 


6. 得到listctrl中所有行的checkbox的状态

      m_list.SetExtendedStyle(LVS_EX_CHECKBOXES);
      CString str;
      for(int i=0; i<m_list.GetItemCount(); i++)
      {
           if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED || m_list.GetCheck(i))
           {
                str.Format(_T("第%d行的checkbox为选中状态"), i);
                AfxMessageBox(str);
           }
      }


7. 得到listctrl中所有选中行的序号


      方法一:
      CString str;
      for(int i=0; i<m_list.GetItemCount(); i++)
      {
           if( m_list.GetItemState(i, LVIS_SELECTED) == LVIS_SELECTED )
           {
                str.Format(_T("选中了第%d行"), i);
                AfxMessageBox(str);
           }
      }

      方法二:
      POSITION pos = m_list.GetFirstSelectedItemPosition();
      if (pos == NULL)
           TRACE0("No items were selected!\n");
      else
      {
           while (pos)
           {
                int nItem = m_list.GetNextSelectedItem(pos);
                TRACE1("Item %d was selected!\n", nItem);
                // you could do your own processing on nItem here
           }
      }


8. 得到item的信息

      TCHAR szBuf[1024];
      LVITEM lvi;
      lvi.iItem = nItemIndex;
      lvi.iSubItem = 0;
      lvi.mask = LVIF_TEXT;
      lvi.pszText = szBuf;
      lvi.cchTextMax = 1024;
      m_list.GetItem(&lvi);

      关于得到设置item的状态,还可以参考msdn文章
      Q173242: Use Masks to Set/Get Item States in CListCtrl
               http://support.microsoft.com/kb/173242/en-us


9. 得到listctrl的所有列的header字符串内容

      LVCOLUMN lvcol;
      char  str[256];
      int   nColNum;
      CString  strColumnName[4];//假如有4列

      nColNum = 0;
      lvcol.mask = LVCF_TEXT;
      lvcol.pszText = str;
      lvcol.cchTextMax = 256;
      while(m_list.GetColumn(nColNum, &lvcol))
      {
           strColumnName[nColNum] = lvcol.pszText;
           nColNum++;
      }


10. 使listctrl中一项可见,即滚动滚动条

    m_list.EnsureVisible(i, FALSE);


11. 得到listctrl列数

    int nHeadNum = m_list.GetHeaderCtrl()->GetItemCount();


12. 删除所有列

      方法一:
         while ( m_list.DeleteColumn (0))
       因为你删除了第一列后,后面的列会依次向上移动。

      方法二:
      int nColumns = 4;
      for (int i=nColumns-1; i>=0; i–)
          m_list.DeleteColumn (i);


13. 得到单击的listctrl的行列号

      添加listctrl控件的NM_CLICK消息相应函数
      void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           // 方法一:
           /*
           DWORD dwPos = GetMessagePos();
           CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
  
           m_list.ScreenToClient(&point);
  
           LVHITTESTINFO lvinfo;
           lvinfo.pt = point;
           lvinfo.flags = LVHT_ABOVE;
    
           int nItem = m_list.SubItemHitTest(&lvinfo);
           if(nItem != -1)
           {
                CString strtemp;
                strtemp.Format("单击的是第%d行第%d列", lvinfo.iItem, lvinfo.iSubItem);
                AfxMessageBox(strtemp);
           }
          */
  
          // 方法二:
          /*
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           if(pNMListView->iItem != -1)
           {
                CString strtemp;
                strtemp.Format("单击的是第%d行第%d列",
                                pNMListView->iItem, pNMListView->iSubItem);
                AfxMessageBox(strtemp);
           }
          */
           *pResult = 0;
      }

 


14. 判断是否点击在listctrl的checkbox上

      添加listctrl控件的NM_CLICK消息相应函数
      void CTest6Dlg::OnClickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           DWORD dwPos = GetMessagePos();
           CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
  
           m_list.ScreenToClient(&point);
  
           LVHITTESTINFO lvinfo;
           lvinfo.pt = point;
           lvinfo.flags = LVHT_ABOVE;
    
           UINT nFlag;
           int nItem = m_list.HitTest(point, &nFlag);
           //判断是否点在checkbox上
           if(nFlag == LVHT_ONITEMSTATEICON)
           {
                AfxMessageBox("点在listctrl的checkbox上");
           }
           *pResult = 0;
      }


15. 右键点击listctrl的item弹出菜单

      添加listctrl控件的NM_RCLICK消息相应函数
      void CTest6Dlg::OnRclickList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           if(pNMListView->iItem != -1)
           {
                DWORD dwPos = GetMessagePos();
                CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
   
                CMenu menu;
                VERIFY( menu.LoadMenu( IDR_MENU1 ) );
                CMenu* popup = menu.GetSubMenu(0);
                ASSERT( popup != NULL );
                popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this );
           }
           *pResult = 0;
  }

 


16. item切换焦点时(包括用键盘和鼠标切换item时),状态的一些变化顺序

      添加listctrl控件的LVN_ITEMCHANGED消息相应函数
      void CTest6Dlg::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
      {
           NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
           // TODO: Add your control notification handler code here
   
           CString sTemp;
 
           if((pNMListView->uOldState & LVIS_FOCUSED) == LVIS_FOCUSED &&
            (pNMListView->uNewState & LVIS_FOCUSED) == 0)
           {
                sTemp.Format("%d losted focus",pNMListView->iItem);
           }
           else if((pNMListView->uOldState & LVIS_FOCUSED) == 0 &&
               (pNMListView->uNewState & LVIS_FOCUSED) == LVIS_FOCUSED)
           {
                sTemp.Format("%d got focus",pNMListView->iItem);
           }
 
           if((pNMListView->uOldState & LVIS_SELECTED) == LVIS_SELECTED &&
            (pNMListView->uNewState & LVIS_SELECTED) == 0)
           {
                sTemp.Format("%d losted selected",pNMListView->iItem);
           }
           else if((pNMListView->uOldState & LVIS_SELECTED) == 0 &&
            (pNMListView->uNewState & LVIS_SELECTED) == LVIS_SELECTED)
           {
                sTemp.Format("%d got selected",pNMListView->iItem);
           }
   
           *pResult = 0;
      }


17. 得到另一个进程里的listctrl控件的item内容

http://www.codeproject.com/threads/int64_memsteal.asp


18. 选中listview中的item

Q131284: How To Select a Listview Item Programmatically
http://support.microsoft.com/kb/131284/en-us


19. 如何在CListView中使用CListCtrl的派生类

http://www.codeguru.com/cpp/controls/listview/introduction/article.php/c919/


20. listctrl的subitem添加图标

      m_list.SetExtendedStyle(LVS_EX_SUBITEMIMAGES);
      m_list.SetItem(..); //具体参数请参考msdn

 


21. 在CListCtrl显示文件,并根据文件类型来显示图标

      网上找到的代码,share
      BOOL CTest6Dlg::OnInitDialog()
      {
           CDialog::OnInitDialog();
  
           HIMAGELIST himlSmall;
           HIMAGELIST himlLarge;
           SHFILEINFO sfi;
           char  cSysDir[MAX_PATH];
           CString  strBuf;
 
           memset(cSysDir, 0, MAX_PATH);
  
           GetWindowsDirectory(cSysDir, MAX_PATH);
           strBuf = cSysDir;
           sprintf(cSysDir, "%s", strBuf.Left(strBuf.Find("\\")+1));
 
           himlSmall = (HIMAGELIST)SHGetFileInfo ((LPCSTR)cSysDir, 
                      0, 
                      &sfi,
                      sizeof(SHFILEINFO), 
                      SHGFI_SYSICONINDEX | SHGFI_SMALLICON );
  
           himlLarge = (HIMAGELIST)SHGetFileInfo((LPCSTR)cSysDir, 
                      0, 
                      &sfi, 
                      sizeof(SHFILEINFO), 
                      SHGFI_SYSICONINDEX | SHGFI_LARGEICON);
  
           if (himlSmall && himlLarge)
           {
                ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,
                             (WPARAM)LVSIL_SMALL, (LPARAM)himlSmall);
                ::SendMessage(m_list.m_hWnd, LVM_SETIMAGELIST,
                             (WPARAM)LVSIL_NORMAL, (LPARAM)himlLarge);
           }
           return TRUE;  // return TRUE  unless you set the focus to a control
      }
 
      void CTest6Dlg::AddFiles(LPCTSTR lpszFileName, BOOL bAddToDocument)
      {
           int nIcon = GetIconIndex(lpszFileName, FALSE, FALSE);
           CString strSize;
           CFileFind filefind;
 
           //  get file size
           if (filefind.FindFile(lpszFileName))
           {
                filefind.FindNextFile();
                strSize.Format("%d", filefind.GetLength());
           }
           else
                strSize = "0";
  
           // split path and filename
           CString strFileName = lpszFileName;
           CString strPath;
 
           int nPos = strFileName.ReverseFind(‘\\’);
           if (nPos != -1)
           {
                strPath = strFileName.Left(nPos);
                strFileName = strFileName.Mid(nPos + 1);
           }
  
           // insert to list
           int nItem = m_list.GetItemCount();
           m_list.InsertItem(nItem, strFileName, nIcon);
           m_list.SetItemText(nItem, 1, strSize);
           m_list.SetItemText(nItem, 2, strFileName.Right(3));
           m_list.SetItemText(nItem, 3, strPath);
      }
 
      int CTest6Dlg::GetIconIndex(LPCTSTR lpszPath, BOOL bIsDir, BOOL bSelected)
      {
           SHFILEINFO sfi;
           memset(&sfi, 0, sizeof(sfi));
  
           if (bIsDir)
           {
            SHGetFileInfo(lpszPath, 
                         FILE_ATTRIBUTE_DIRECTORY, 
                         &sfi, 
                         sizeof(sfi), 
                         SHGFI_SMALLICON | SHGFI_SYSICONINDEX |
                         SHGFI_USEFILEATTRIBUTES |(bSelected ? SHGFI_OPENICON : 0)); 
            return  sfi.iIcon;
           }
           else
           {
            SHGetFileInfo (lpszPath, 
                         FILE_ATTRIBUTE_NORMAL, 
                         &sfi, 
                         sizeof(sfi), 
                         SHGFI_SMALLICON | SHGFI_SYSICONINDEX | 
                         SHGFI_USEFILEATTRIBUTES | (bSelected ? SHGFI_OPENICON : 0));
            return   sfi.iIcon;
           }
           return  -1;
      }


22. listctrl内容进行大数据量更新时,避免闪烁

      m_list.SetRedraw(FALSE);
      //更新内容
      m_list.SetRedraw(TRUE);
      m_list.Invalidate();
      m_list.UpdateWindow();
 
或者参考

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_cwnd.3a3a.setredraw.asp


23. listctrl排序

Q250614:How To Sort Items in a CListCtrl in Report View
http://support.microsoft.com/kb/250614/en-us


24. 在listctrl中选中某个item时动态改变其icon或bitmap

Q141834: How to change the icon or the bitmap of a CListCtrl item in Visual C++
http://support.microsoft.com/kb/141834/en-us


25. 在添加item后,再InsertColumn()后导致整列数据移动的问题

Q151897: CListCtrl::InsertColumn() Causes Column Data to Shift
http://support.microsoft.com/kb/151897/en-us


26. 关于listctrl第一列始终居左的问题

解决办法:把第一列当一个虚列,从第二列开始插入列及数据,最后删除第一列。
     
具体解释参阅   http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/structures/lvcolumn.asp

 


27. 锁定column header的拖动

http://msdn.microsoft.com/msdnmag/issues/03/06/CQA/


28. 如何隐藏clistctrl的列

    把需隐藏的列的宽度设为0,然后检测当该列为隐藏列时,用上面第27点的锁定column 的拖动来实现


29. listctrl进行大数据量操作时,使用virtual list   

http://www.codeguru.com/cpp/controls/listview/advanced/article.php/c4151/
http://www.codeproject.com/listctrl/virtuallist.asp


30. 关于item只能显示259个字符的问题

解决办法:需要在item上放一个edit。


31. 响应在listctrl的column header上的鼠标右键单击

Q125694: How To Find Out Which Listview Column Was Right-Clicked
http://support.microsoft.com/kb/125694/en-us


32. 类似于windows资源管理器的listview

Q234310: How to implement a ListView control that is similar to Windows Explorer by using DirLV.exe
http://support.microsoft.com/kb/234310/en-us

 


33. 在ListCtrl中OnTimer只响应两次的问题

Q200054:
PRB: OnTimer() Is Not Called Repeatedly for a List Control
http://support.microsoft.com/kb/200054/en-us


34. 以下为一些为实现各种自定义功能的listctrl派生类

          (1)    拖放       
                   http://www.codeproject.com/listctrl/dragtest.asp

                   在CListCtrl和CTreeCtrl间拖放
                   http://support.microsoft.com/kb/148738/en-us
 
          (2)    多功能listctrl
                   支持subitem可编辑,图标,radiobutton,checkbox,字符串改变颜色的类
                   http://www.codeproject.com/listctrl/quicklist.asp
 
                   支持排序,subitem可编辑,subitem图标,subitem改变颜色的类
                   http://www.codeproject.com/listctrl/ReportControl.asp

          (3)    subitem中显示超链接
                   http://www.codeproject.com/listctrl/CListCtrlLink.asp

          (4)    subitem的tooltip提示
                   http://www.codeproject.com/listctrl/ctooltiplistctrl.asp

          (5)    subitem中显示进度条   
                   http://www.codeproject.com/listctrl/ProgressListControl.asp
                   http://www.codeproject.com/listctrl/napster.asp
                   http://www.codeguru.com/Cpp/controls/listview/article.php/c4187/

          (6)    动态改变subitem的颜色和背景色
                    http://www.codeproject.com/listctrl/highlightlistctrl.asp
                    http://www.codeguru.com/Cpp/controls/listbox/colorlistboxes/article.php/c4757/
 
          (7)    类vb属性对话框
                    http://www.codeproject.com/listctrl/propertylistctrl.asp
                    http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c995/
                    http://www.codeguru.com/Cpp/controls/listview/propertylists/article.php/c1041/
 
          (8)    选中subitem(只高亮选中的item)
                    http://www.codeproject.com/listctrl/SubItemSel.asp
                    http://www.codeproject.com/listctrl/ListSubItSel.asp
 
          (9)    改变行高
                    http://www.codeproject.com/listctrl/changerowheight.asp
 
          (10)   改变行颜色
                    http://www.codeproject.com/listctrl/coloredlistctrl.asp
 
          (11)   可编辑subitem的listctrl
                    http://www.codeproject.com/listctrl/nirs2000.asp
                    http://www.codeproject.com/listctrl/editing_subitems_in_listcontrol.asp
 
          (12)   subitem可编辑,插入combobox,改变行颜色,subitem的tooltip提示
                    http://www.codeproject.com/listctrl/reusablelistcontrol.asp
 
          (13)   header 中允许多行字符串
                    http://www.codeproject.com/listctrl/headerctrlex.asp
 
          (14)   插入combobox
                    http://www.codeguru.com/Cpp/controls/listview/editingitemsandsubitem/article.php/c979/
 
          (15)   添加背景图片
                    http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c4173/
                    http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/
                    http://www.vchelp.net/vchelp/archive.asp?type_id=9&class_id=1&cata_id=1&article_id=1088&search_term=
   
          (16)  自适应宽度的listctrl
                    http://www.codeproject.com/useritems/AutosizeListCtrl.asp

          (17)  改变ListCtrl高亮时的颜色(默认为蓝色)
                   处理 NM_CUSTOMDRAW
          
http://www.codeproject.com/listctrl/lvcustomdraw.asp

2007年03月27日

响应KeyPress事件

    /*全角字符从的unicode编码从65281~65374   
      半角字符从的unicode编码从               33~126   
     * 差值65248
      空格比较特殊,全角为       12288,半角为       32 
     
*/
    
public char FullCodeToHalfCode(char c)
    {
        
//得到c的编码
        byte[] bytes = System.Text.Encoding.Unicode.GetBytes(c.ToString());

        int H = Convert.ToInt32(bytes[1]);
        
int L = Convert.ToInt32(bytes[0]);

        //得到unicode编码
        int value = H * 256 + L;

        //是全角
        if (value >= 65281 && value <= 65374)
        {
            
int halfvalue = value - 65248;//65248是全半角间的差值。
            byte halfL = Convert.ToByte(halfvalue);

            bytes[0= halfL;
            bytes[
1= 0;
        }
        
else if (value == 12288)
        {
            
int halfvalue = 32;
            
byte halfL = Convert.ToByte(halfvalue);

            bytes[0= halfL;
            bytes[
1= 0;
        }
        
else
        {
            
return c;
        }

        //将bytes转换成字符
        string ret = System.Text.Encoding.Unicode.GetString(bytes);

        return Convert.ToChar(ret);
    }

 

C#中文乱码解决:UTF8 转 UNICODE

XML文件可以采用多种编码,但是经过不同的编码后对于中文会出现乱码问题,比如“骞垮憡涓戦椈”,对于此问题的解决如下:

static void Main()
      {
         string utf8String = "骞垮憡涓戦椈";

         // Create two different encodings.
         Encoding utf8= Encoding.UTF8;
         Encoding defaultCode= Encoding.Default;

         // Convert the string into a byte[].
         byte[] utf8Bytes = default.GetBytes(utf8String );

         // Perform the conversion from one encoding to the other.
         byte[] defaultBytes = Encoding.Convert(utf8, defaultCode, utf8Bytes );
           
         // Convert the new byte[] into a char[] and then into a string.
         // This is a slightly different approach to converting to illustrate
         // the use of GetCharCount/GetChars.
         char[] defaultChars = new char[defaultCode.GetCharCount(defaultBytes , 0, defaultBytes .Length)];
         defaultCode.GetChars(defaultBytes , 0, defaultBytes .Length, defaultChars , 0);
         string defaultString = new string(defaultChars );

         // Display the strings created before and after the conversion.
         Console.WriteLine("Original string: {0}", utf8String);
         Console.WriteLine("Ascii converted string: {0}", defaultString);

//或者如下:
         byte[] buffer1 = Encoding.Default.GetBytes(utf8String );
         byte[] buffer2 = Encoding.Convert(Encoding.UTF8, Encoding.Default, buffer1, 0, buffer1.Length);
         string strBuffer = Encoding.Default.GetString(buffer2, 0, buffer2.Length);
      }

2007年03月14日

浅谈C#托管程序中的资源释放问题

便于对文章的开展,需要先明确两个概念。
第一个就是很多人用.Net写程序,会谈到托管这个概念。那么.Net所指的资源托管到底是什么意思,是相对于所有资源,还是只限于某一方面资源?很多人对此不是很了解,其实.Net所指的托管只是针对内存这一个方面,并不是对于所有的资源;因此对于Stream,数据库的连接,GDI+的相关对象,还有Com对象等等,这些资源并不是受到.Net管理而统称为非托管资源。而对于内存的释放和回收,系统提供了GC-Garbage Collector,而至于其他资源则需要手动进行释放。

那么第二个概念就是什么是垃圾,通过我以前的文章,会了解到.Net类型分为两大类,一个就是值类型,另一个就是引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成。GC的全称为“Garbage Collector,顾名思义就是垃圾回收器,那么只有被称为垃圾的对象才能被GC回收。也就是说,一个引用类型对象所占用的内存需要被GC回收,需要先成为垃圾。那么.Net如何判定一个引用类型对象是垃圾呢,.Net的判断很简单,只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。

明确了这两个基本概念,接下来说说GC的运作方式以及其的功能。内存的释放和回收需要伴随着程序的运行,因此系统为GC安排了独立的线程。那么GC的工作大致是,查询内存中对象是否成为垃圾,然后对垃圾进行释放和回收。那么对于GC对于内存回收采取了一定的优先算法进行轮循回收内存资源。其次,对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。

很明显得知,对于某个具体的资源,无法确切知道,对象析构函数什么时候被调用,以及GC什么时候会去释放和回收它所占用的内存。那么对于从CC++之类语言转换过来的程序员来说,这里需要转变观念。

那么对于程序资源来说,我们应该做些什么,以及如何去做,才能使程序效率最高,同时占用资源能尽快的释放。前面也说了,资源分为两种,托管的内存资源,这是不需要我们操心的,系统已经为我们进行管理了;那么对于非托管的资源,这里再重申一下,就是Stream,数据库的连接,GDI+的相关对象,还有Com对象等等这些资源,需要我们手动去释放。

如何去释放,应该把这些操作放到哪里比较好呢。.Net提供了三种方法,也是最常见的三种,大致如下:
<!–[if !supportLists]–>1.  <!–[endif]–>析构函数;
<!–[if !supportLists]–>2.  <!–[endif]–>继承IDisposable接口,实现Dispose方法;
<!–[if !supportLists]–>3.  <!–[endif]–>提供Close方法。

经过前面的介绍,可以知道析构函数只能被GC来调用的,那么无法确定它什么时候被调用,因此用它作为资源的释放并不是很合理,因为资源释放不及时;但是为了防止资源泄漏,毕竟它会被GC调用,因此析构函数可以作为一个补救方法。而CloseDispose这两种方法的区别在于,调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象被销毁了,不能再被使用。例如,常见SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收。明白了这两种方法的意思后,大家在往自己的类中添加的接口时候,不要歪曲了这两者意思。

接下来说说这三个函数的调用时机,我用几个试验结果来进行说明,可能会使大家的印象更深。
首先是这三种方法的实现,大致如下:

    ///<summary>

    /// The class to show three disposal function

    ///</summary>

    public class DisposeClass:IDisposable

    {

        public void Close()

        {

            Debug.WriteLine( "Close called!" );

        }

        ~DisposeClass()

        {

            Debug.WriteLine( "Destructor called!" );

        }

        #region IDisposable Members

        public void Dispose()

        {

            // TODO:  Add DisposeClass.Dispose implementation

            Debug.WriteLine( "Dispose called!" );

        }

        #endregion

    }

对于Close来说不属于真正意义上的释放,除了注意它需要显示被调用外,我在此对它不多说了。而对于析构函数而言,不是在对象离开作用域后立刻被执行,只有在关闭进程或者调用GC.Collect方法的时候才被调用,参看如下的代码运行结果。

        private void Create()

        {

            DisposeClass myClass = new DisposeClass();

        }

        private void CallGC()

        {

            GC.Collect();

        }

        // Show destructor

        Create();

        Debug.WriteLine( "After created!" );

        CallGC();

运行的结果为:

After created!

Destructor called!

显然在出了Create函数外,myClass对象的析构函数没有被立刻调用,而是等显示调用GC.Collect才被调用。

对于Dispose来说,也需要显示的调用,但是对于继承了IDisposable的类型对象可以使用using这个关键字,这样对象的Dispose方法在出了using范围后会被自动调用。例如:

    using( DisposeClass myClass = new DisposeClass() )

    {

        //other operation here

    }

如上运行的结果如下:
Dispose called!

那么对于如上DisposeClass类型的Dispose实现来说,事实上GC还需要调用对象的析构函数,按照前面的GC流程来说,GC对于需要调用析构函数的对象来说,至少经过两个步骤,即首先调用对象的析构函数,其次回收内存。也就是说,按照上面所写的Dispose函数,虽说被执行了,但是GC还是需要执行析构函数,那么一个完整的Dispose函数,应该通过调用GC.SuppressFinalize(this )来告诉GC,让它不用再调用对象的析构函数中。那么改写后的DisposeClass如下:

    ///<summary>

    /// The class to show three disposal function

    ///</summary>

    public class DisposeClass:IDisposable

    {

        public void Close()

        {

            Debug.WriteLine( "Close called!" );

        }

        ~DisposeClass()

        {

            Debug.WriteLine( "Destructor called!" );

        }

        #region IDisposable Members

        public void Dispose()

        {

            // TODO:  Add DisposeClass.Dispose implementation

            Debug.WriteLine( "Dispose called!" );

            GC.SuppressFinalize( this );

        }

        #endregion

    }

通过如下的代码进行测试。

        private void Run()

        {

            using( DisposeClass myClass = new DisposeClass() )

            {

                //other operation here

            }

        }

        private void CallGC()

        {

            GC.Collect();

        }

        // Show destructor

        Run();

        Debug.WriteLine( "After Run!" );

        CallGC();

运行的结果如下:

Dispose called!

After Run!

显然对象的析构函数没有被调用。通过如上的实验以及文字说明,大家会得到如下的一个对比表格。

析构函数

Dispose方法

Close方法

意义

销毁对象 销毁对象 关闭对象资源

调用方式

不能被显示调用,会被GC调用 需要显示调用
或者通过using语句
需要显示调用

调用时机

不确定 确定,在显示调用或者离开using程序块 确定,在显示调用时

那么在定义一个类型的时候,是否一定要给出这三个函数地实现呢。

我的建议大致如下。
<!–[if !supportLists]–>1.<!–[endif]–>提供析构函数,避免资源未被释放,主要是指非内存资源;
<!–[if !supportLists]–>2.<!–[endif]–>对于DisposeClose方法来说,需要看所定义的类型所使用的资源(参看前面所说),而决定是否去定义这两个函数;
<!–[if !supportLists]–>3.<!–[endif]–>在实现Dispose方法的时候,一定要加上“GC.SuppressFinalize( this )”语句,避免再让GC调用对象的析构函数。

C#程序所使用的内存是受托管的,但不意味着滥用,好地编程习惯有利于提高代码的质量以及程序的运行效率。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1023352

2007年03月13日

   很多年以后,我无比想念桃花林里那个妖精。我知道,作为一个“佛”,这很不应该。我应该把一切都忘掉,把所有的爱和恨,悲和喜,功业和理想,都忘掉。
  但我清楚,就算我把自己也忘了,当那朵红霞拂过我的窗前,我还是会想起三千年前,那张美丽的脸,那双透明的眼睛,那银铃一般的歌声。
  “我吃了你好不好?”
  “不好。”
  “为什么?”
  “我还没洗澡呢。”
  她咯咯地笑,粉红色的长裙轻轻摆动,象一朵美丽的红霞。
  (一)
  孙悟空有几百年没来看我了。西天路上,我这徒弟曾无数次救过我的命。我们没想到那是游戏,我们如此投入,抱头痛哭,相对嘻笑,但直到结局才明白,一切原来都是虚幻。取经路上的一切山,一切水,一切妖魔鬼怪,都是如来设的障眼法。
  “这么多年了,你还在生气?”三百年前,我问他。
  他长叹,“你知道我最后悔的是什么事?”
  “是什么?”
  “我真后悔认识你,师父。”
  这句话是我多年以前对他说过的,那时他还是一只猴子。
  猴子和人的差别有多大?孙悟空说:“只隔一张纸。”
  “你把这张纸揭开,里面有一个秘密。”三千年前,那只猴子躺在树下微笑着说。
  我把封印揭开。
  轰隆隆一声巨响,霎那间天崩地裂,一道金光从山谷里升腾而起,那只猴子一飞冲天,坐在云端大笑。
  “哈哈哈,你救了我,但我决定要杀了你,你有什么话说?”
  “我真后悔认识你,猴子。”
  他没有杀我,他成了我的徒弟。
  很多年之后我知道那也是如来的安排,他不愿意看一个人演的戏,那不够精彩。
  女妖桃儿在一个美丽的夜里俘虏了我,她挥了挥手,我就动弹不得。
  “你要带我到哪里去?”我挣扎着问,天上有一轮瓦蓝瓦蓝的月亮。
  她重重打了我一耳光,“不许说话!再罗嗦我杀了你!”
  我们在林间飞翔,昆虫唧唧鸣叫,树叶轻轻飘动,她身上散发出淡淡的香气,我心里有一点害怕,但更多的是惆怅,这一切,多象我少年时常做的那个梦呵。
  “和尚,你死了么?”她忽然问我。
  “还没有,活着多好啊,你死了我都不会死。”我笑着说。
  她转回身,劈面又打了我一个耳光,半边脸热辣辣地疼,“不许你说话,你还敢说?!”
  “阿弥陀佛,善哉善哉。”
  “啪!”又一耳光。
   “阿弥陀佛!!”我大怒,“善哉善哉!!”
  “啪!”
  “阿弥陀佛阿弥陀佛!!!!”
  “啪!啪!”
  “阿弥陀佛阿弥陀佛阿弥陀佛阿弥陀佛阿弥陀佛阿弥陀佛阿弥陀佛………”
  她咯咯地笑了,“我从来没见过你这样的倔和尚,好吧,我不打你了,但你也不许罗嗦。”
  这是第一天,她打了我五个耳光,但最后我赢了。
  (二)
  成佛后,世界一片寂静。把灯点燃,把灯熄灭,世界一片光明,所有的经文都是帮助你忘却的。忘却过去,忘却自己,忘却经文本身。
  又见离欲,常处空闲,深修禅定,得五神通。
  又见菩萨,安禅合掌,以千万偈,赞诸法王。
  复见菩萨,智深志固,能问诸佛,闻悉受持。
  又见佛子,…………
  我轻轻地念着,感觉自己渐渐走远,歌声在星斗间缥缈散尽,人间花谢花开、悲欢聚散,都象是耳后的微风。
  有一段时间,我以为我真的已经忘记了一切。我安静而幸福,恒河的沙子堆成了高山,但我一粒也不要。
  “师父!”
  我抬起头来,眼前是一只猴子。
  “你找谁?”我问他。
  “师父,你不记得了?我是悟空啊。”
  “悟…空…,悟空?”
  记忆的海水漫卷而来,拍打着光阴的墙壁。那些模糊的记忆渐渐清晰,我看见眼前的猴子眼含热泪。
  “师父啊~!”他号啕大哭,“这就是我们一直追求的幸福生活吗?”
  走出桃花林时,我们两个都没有说话。过了很久,他忽然问我:“师父,什么叫作幸福?”
  “幸福只是一种感觉,也许到了西天,我们就会明白。”
  “那么,你现在幸福吗?”他歪着猴头看我。
  我的眼圈一下子红了,“操你娘!闭嘴!”我粗鲁地说。
  “操你娘”这三个字是我教妖精桃儿的。
  在桃林深处,有一座美丽的花园。青青的草地上落满了花瓣。
  “到家喽!”桃儿长出一口气,把我重重地扔到地上,“你可真重,倔和尚。”
  我不理她,嘴角渗出丝丝血迹,被她打的。
  “你不理我吗,倔和尚?”
  我把脸也转过去。
  “你敢不理我,不怕我打你?”她威胁我。
  我哼了一声。
  “对了,你不怕打。”她自言自语,“那么我骂你了啊!”
  “哼!”
  “骂你什么呢?你们人类是怎么骂的?”
  “操你娘!”我直视着她。她要再敢打我,我就跟她拼了,我想。
  “操你娘,操你娘,嘻嘻,真好玩。”
  西天路上有无数妖精,但从来没见过象她这么傻的。
  “你把我捉来,想干什么?”
  她绕着我走来走去,“我姐姐说吃了你的肉会长生不老,你有那么好吗,倔和尚?”
  “我从来不洗澡,我的肉又臭又硬,吃了毒死你!”我恶狠狠地说。
  她突然从背后扑过来,在我胳膊上狠狠咬了一口,鲜血直流。
  “哎哟哎哟~~”我疼得大叫,“操你娘!”
  (三)
  传说中有一种法术,叫作“回梦”,施了这种法术,你就可以沿着梦里的路,回到从前。
  “三藏,你为何来?”如来在莲座上问。
  我磕头,“我想请师尊传我回梦之术。”
  “你要回到哪里?”
  “回到取经路上。”
  他笑了,“你取过经么?”
  我的头在地上重重地顿了两下,血流了出来,殷红灿烂,象一朵盛开的桃花。
  “你入了魔道了,三藏!你何曾取过经?!”如来大喝。
  我愣住了,我看见自己的一生象一幅长轴的画卷,在眼前慢慢翻开。我看见我从一个女人的身体里钻出来,对着世界大声哭泣;看见自己慢慢站立,蹒跚地走路,咿呀学语;看见自己在五岳山苦读修行;看见自己渐渐老去,一些人围着我的身体流泪;看见我的灵魂脱离了躯壳,在白云中慢慢升腾,所有的阳光都照在我身上,我成了佛。
  “你取过经么?”
  ………
  我心里空空荡荡,摇摇欲倒。
  “来,跟我走,我带你回去。”如来身旁的童子对我说。
  “你说什么?”我蓦地睁开眼睛。
  “跟我走!我带你回去!”妖精桃儿抓着我的衣领。
  “我不去!”我拼命挣扎,“你这该死的妖精,我一定会让我徒弟杀了你!”
  桃儿叹了口气,“由得你吧,不过我告诉你,我姐姐就要来了。你~,你保重吧。”
  我成佛后,不再是当年那个固执冲动的和尚,我知道,我眼里看到的一切都是幻影,来一阵风,一切就会无影无踪。三千年,多少王国毁灭,多少城市荒芜,俗世在沧桑之后容颜更改,不留痕迹。不管你执着或者放弃,最终的结局都一样,色身化身,尽归虚空。
  妖精的洞窟里群魔乱舞,桃树精开怀大笑。
  “长生不老!”她喊道。
  “长生不老!长生不老!长生不老!”千百个小妖怪同声附和。
  我被锁在柱上,茫然地看着这群愚昧的生灵。是的,我即将死去,但他们也决不会长生,早死或者晚死,在佛的眼里,没有分别。
  桃儿远远地站着,静静地望着我。
  如果我不是和尚,我一定会由衷地赞叹她的美丽,她象一朵盛开的桃花,芬芳明艳,整座洞窟都因她而加倍明亮。她既不是脓水也不是骷髅,佛经也不总是正确,我想。
  “把唐三藏洗剥干净了,抬到蒸笼上去!”桃树精喊道。
  “嗨!嗨!嗨!”小妖们欢呼雀跃。
  桃儿的身体剧烈地抖动了一下,她满面通红。“瞧她高兴的!”我恨恨地想。
  两个妖怪架起我来就往外走,山洞里崎岖不平,我的头在石壁上撞了一下,剧烈地疼痛。
  “等一等!”桃儿突然说。
  她一把抓住我的胳膊,脸上浮现笑容,“姐姐,这些粗货肯定洗不干净,我去吧!”
  “好!”桃树妖说,“这次你立了大功,你想吃哪块肉,你自己来挑!”
  “我要吃他的心!”桃儿咬着嘴唇说。她的牙齿闪了一下,象是人世间最珍贵的玉石。
  (四)
  我少年时经常会作同一个梦:在一望无边的草原上,我骑着白马飞快地奔跑,一个害羞的少女把脸埋在我的背后,双手紧紧地抱着我的腰,我表情幸福而又悲伤,一片宁静中,少女抬起头来,在我耳边喃喃地说:如果,如果……。
  我睁开眼,门外响起第一声晨钟,这个时候我总是无比伤感。
  “你是个情种,”玄苦师父摸着我的光头说,“你不该到这里来。”
  “我该去哪里?”
  玄苦师父长久地摇头。
  直到今天我也不知道我该去哪里。成佛很难,但我成了;做梦很容易,但几千年来,我始终不能走回到那个梦里。
  桃儿没有带我去河边,她拉着我的手,象月光一样飞向桃林深处,那里雾气蒙蒙。 “你想独吞唐僧肉吗?”我冷冷地问。
  “嘘…,不要说话!”她没有回头,带着我掠过一株株桃树,粉红色的纱巾在风中飞扬,轻轻拂过我的双眼。
  夜色深深,我们在无边寂静中不停地穿行。黑暗里,有一些东西正在我心中轻轻蠕动,慢慢成长。我张开双臂,在空中轻盈漂浮,脑海里一片迷茫,我看见那匹久违的梦中白马正在长鸣,脚下青草无边……
  “和尚,我们到了!”
  我睁开眼,发现身处一个群山环抱的山谷,一条小河正从我身边潺潺流去。
  “你为什么要救我?”
  “你以为我是救你啊,和尚?我是要吃你,别做梦了!”她笑嘻嘻地说。
  妖怪就是妖怪,我双手合什,“阿弥陀佛。”
  “和尚,你说我漂亮吗?”她忽然问我。
  我的脸刷的红了,我转过身去,装作什么也没听见,低头喃喃吟诵:无上甚深微妙法,百千万劫难遭遇。
  我今见闻得受持,愿解如来真实意。
  ………
  “你在骗自己呢,和尚,”桃儿咯咯地笑,“在山洞里你就一直盯着我看–你的脸都红了。”
  玄苦师父一直夸我有定力,打坐的时候,他经常会在我耳边大吼一声。耳朵嗡嗡作响,但我的身体纹丝不动。他总是满意的摸摸我的头,起身离去。这个游戏,我们玩了三十年,直到他死。
  那一刻我忽然想起玄苦师父,羞愧难当。
  “你觉得我漂亮就好,”她幽幽地说,“我叫桃儿,是我姐姐身上的一朵桃花–不知道你会不会记得住?”
  (五)
  我功德圆满的那一天,诸天神佛云集灵山,仙乐飘飘,天花飞舞。当一切沉寂之后,如来问我:“你是谁?你从哪里来?要到何处去?”
  所有的目光都直视着我。我躬身作答:“我本无名,我本无形,我就是空,我无来处,亦无去处。”
  “那么,你都忘了吗?”
  我低头不语。
  “为什么不说话?”
  我抬起头来,对着如来大声说:“本来未曾有过,又何须忘记?!”
  如来大笑,神佛们啧啧赞叹,天花从空中纷纷撒落。
  但我记忆的闸门却在那一刻汹涌打开,生命中的每个人、每件事、每句话、每个喜怒忧乐的表情,都如此清晰和美丽,猛烈地摇动着我最深处的灵魂。
  桃林里响起一阵歌声:天路遥,人世远,凝眸处沧海桑田。
  为谁痛哭,为谁嘻笑,任光阴凋尽朱颜。
  哪个出将入相,哪个成佛登仙,到头来或为黄土,或为轻烟。
  且去世外垂钓,手有青青竹竿。
  莫问卿卿何处去,回头看见桃花仙………
  尾音袅袅,散入青云,我看见桃儿提着竹篮远远走来,她嘻嘻地笑,长发在朝霞中飘飘飞扬,身上洒满阳光……
  桃儿敲敲我的光头,笑,“吃饭了!还在看着美女发呆!”
  我红着脸低下头。
  从那一刻起,我再也没有恨过她,虽然她损折了我的修行,虽然她打过我,虽然,她几乎让我堕入魔道。佛说,爱恨痴嗔是人生痛苦的根源,但如果没有那些爱,那些恨,那些痴情,那些含泪的微笑,人生该是多么乏味啊。
  “知不知道我为什么要救你?”
  “为什么?”
  “因为只有你才敢顶撞我,而且,从来都不正眼看我。”桃儿撅起小嘴。
  “谁让你打我的。”我笑着说,心里涌上一阵悲哀。我知道,自己再也不是个和尚了,我坚守的清规戒律都已经崩溃,在那个阳光明媚的清晨,在那个妖精迷人的微笑里。
  “你吃肉吗?”
  “不吃。”
  “喝酒吗?”
  “不喝。”
  我坐在那里,不知道该生气还是该高兴,过了很久,我无声地哭了。
  (六)
  “你为什么要去取经?”
  “为了拯救众生啊。”
  “你徒弟那么厉害,让他一个人去不行吗?”
   我从没想过这个问题。
  “你徒弟去西天,只要翻一个跟斗,但你这样走过去,要走上好几百年,”桃儿夸张地比划了一下,“等你把经取回来,众生早死光了。”
  我呆住了。桃儿摸了一下我的光头,叹气,“连自己的命都差点保不住,还拯救众生,你呀,真是个傻和尚。”
  很多年以后,我终于明白,所谓西天取经,所谓普渡众生,只不过是如来跟我开的一个玩笑。那些尘封千年的往事一一浮现,一张美丽的脸在光阴深处闪烁,我看见她在千年里始终不改地向我微笑。让我浑身颤栗。
  一切事情都有它的因果,痛苦是因为执著,快乐来自于放弃。千年里我不断地想,是什么样的缘起,造就了我和那个妖精的生死悲欢?
  “我吃了你好不好?”她突然问我。
  “不好。”
  “为什么?”
  “我还没洗澡呢。”
  她愣了一下,随即咯咯的笑起来,粉红色的长裙轻轻摆动,象一朵美丽的红霞。“我才不舍得吃你,和尚,我很喜欢你呢。”
  桃儿从来不羞于表达她的情感,在她的世界里,爱和恨,生和死,都那么简单。
  “你哪里都不许去,”她站在一棵桃树下对我说,“我要和你在一起。”
  我的心剧烈地跳动,桃儿象个孩子一样无邪地看着我,这目光穿越了光阴和生死,让我在千年后泪落如雨。
  我的眼泪飘落人间,人间涌起洪水滔滔。
  “你相信有来生吗?”我在河里洗澡,桃儿远远地问。
  “相–信–!”
  她飞快地跑到岸边,脸色涨红,“来生你还见不见我?”
  我反问她:“那你还打不打我?”
  她前仰后合地笑,“不打了不打了,你这个小心眼的和尚!”
  我点头,“那我就见你,你要是还打我,我就永远都不理你了。”
  她扑通一声跳进水里,紧紧搂着我的脖子,“你要说话算话!”她说,泪水叭嗒叭嗒地落在我的头上。
  “’操你娘’的’操’是什么意思?”一片黑暗中,她贴在我的耳边问。
  “就是这样。”我拿起她的手,比了一下形状。
  “我要!”
  “什么?”
  她把脸紧紧地贴在我的脸上,“我要,”她轻轻地说,脸象火一般烫。
  …………
  那个夜里,春雨落满人间,无数朵桃花悄悄开放,光阴的枝头洒满了生命的甘露。
  “我真幸福,”
  “我也是。”
  我紧紧地拥抱她,心中无限喜悦。这就是涅磐,我在心里喃喃自语,幸福的、死亡一般的涅磐。
  (七)
  孙悟空是个诗人,我指的是他的生活态度。从本质上,他是一只浪漫多情的猴子,对世界无比温柔,但看上去却象个暴徒。他成佛的那一刻眼含热泪,浑身颤抖。如来笑着问:“你这顽皮的猴子,哭什么?”
  当时诸天神佛都在,悟空突然放声大哭,谁都劝不住。
  只有我知道,那才是真实的孙悟空,一只软弱的、自卑的猴子,一只渴望爱情的猴子。
  三百年前,他在我的墙上题了一首诗:有缘未必相逢,无情莫向翠微。人间一堕十劫,犹记桃花未归。
  我知道他说的是什么。
  幸福,是的,可望不可及的幸福。
  桃树精找到我们的时候,桃儿正在梳头。她妩媚地望着我,头发长长地垂下来,我轻轻地吻着她,小心地把一朵桃花插在她的耳边。
  “我美吗?”
  “嗯。”
  我们相视微笑,心中无限甜蜜。
  千年后,那瞬间的微笑让我无比疼痛,往事就象最锋利的宝刀,一刀刀刺穿我的胸膛。
  如果可能,我愿意用我不死的生命,用尽千千万万年光阴,来换取当年桃树下那深情的微笑,哪怕只有一刹那,一秒钟。
  两个妖精翻翻滚滚地斗了起来,风声呼啸,木叶飞扬,鲜血一滴滴溅到我身上,我双手合什,浑身颤抖着祈祷,一些从未有过的恐惧滚滚而来,反复搓弄着我的心灵。
  突然一声巨响,桃儿象断线的风筝一样跌了出去,鲜血飞溅,染红了脚下的草地。
  桃树精挥剑直劈,“小贱人,今天我要杀了你!”
  桃儿象个孩子一样无助地躺在那里,脸上满是鲜血,她痴痴地望着我,目光中含着重重的情感。我猛扑过去,紧紧抱住她,抱紧她,用尽我全身的气力。桃树精的利剑就在眼前,但我不怕,我愿意就这样死去,我想。
  当然,这是个游戏。有一双眼睛一直在俯视着这片土地。
  在最危机的关头,如来佛和孙悟空同时出现在我的面前。
  如来招了招手,桃树精就立刻倾倒、软化、分解,弹指之间,那个美丽凶狠的妖精就不见了,象风一样无影无踪。
  我如大梦初醒,生死之间,一切都那么突兀。我揉揉眼睛,看见面前金光闪闪的如来,他双手合什,慈祥地微笑,“善哉善哉,三藏,恭喜你过得此劫,西天不远,趁早上路吧。”
  桃儿紧紧地握着我的手,“不要!”她虚弱但坚定地说,“我不要你去,我不要你离开我!”
  她头发散乱,脸色苍白,两行清泪滑过她秀美的脸庞,鬓边的桃花血迹斑斑,那是一个芬芳的梦。
  我痴痴地看着她,胸中波涛汹涌,她再也不是那个法力高强的妖精,而是一个需要我去疼、去爱、去温柔拥抱的姑娘。一种从未有过的情感在我心中迅速升腾,象烈火一样灼热着我的灵魂。
  “师尊!”我突然双膝跪倒,大声地说:“我不去取经!我不想成仙成佛,也不想长生不老!我要留下来当一个平凡的人!”
  有的人一生只会勇敢一次,为了一个人或者一份真情。多年来我无数次想起我那天的宣言,我知道,在那一刻,我最接近幸福。
  如来脸色阴沉,大喝:“小小情欲,就让你意丧魂消,你还是不是我的弟子?!”他迅疾无伦的伸出手,在我头上重重敲了一下,“你还不悟?!”
  桃儿从地上艰难地爬进我的怀里,我紧紧地抱住她,不肯放手。我们目光相对,彼此都感到无限欣慰。
  如来大怒,“心魔不除,你就是妖孽!”
  我大声回答:“弟子今日愿为妖孽!哪怕堕入苦海万劫不复,也在所不惜!”
  如来怒不可遏,右手高高举起,“那你们就一起灭亡吧!”
  霹雳裂天而来,山岳摇动,江河倒流。桃儿突然勇敢地扑上前去,挡在我的身前。
  “你不要杀他,”她看着如来,“我一定会还你一个忠心耿耿的和尚!”
  她拿起宝剑,一剑刺进自己的胸膛,鲜血象桃花一样绽放在她胸前。
  “不!桃儿,不!”我大叫,一把抱住她,胸口如同千万把刀剑同时刺入,无比的疼痛。
  桃儿缓慢地转过身来,看着我泪如雨下。“和尚,你告诉我,真的有来生吗?”
  雷声在我空空的心中流响,往事如火花闪耀,我的眼泪终于决堤,在脸上滚滚流淌,“有!真的有!”我哭着说。
  “那么,来生你还要我吗?”
  我拼命地点头,在风雨里大声呼喊,“我要你!要你!!”
  桃儿用尽最后一点力气扑进我的怀里,锋利的宝剑刺穿了她的身体。她紧紧搂住我的脖子,“和尚,你要说话算话!”她说,最后一滴泪水叭嗒叭嗒地落在我的头上。
  (八)
  “师父!”一个声音在外面叫。
  “不要吵,我很累,我要睡觉。”我喃喃地说。
  我看见自己在天空飞翔,云图不停变幻,流星纷纷坠落,一颗蓝色的星球在眉间缓缓滑过。白云深处是谁的目光?让我在光阴的梦里如此悲伤。
  “师父,你醒醒啊!”悟空哭着说。
  我倏地睁开眼,看见一只痛哭流涕的猴子,他两眼通红,鼻涕和眼泪把他的脸搞得一蹋糊涂。
  我拍拍他的手,“你为什么伤心,猴子?”
  悟空扑过来,伏在我胸口号啕大哭,“师父啊,我可怜的师父啊………”
  桃儿呢?
  没有桃儿。
  你骗我!
  师父,是你自己在骗自己。根本就没有桃儿,也没有那片桃林。
  也许悟空是对的,但枝头那朵桃花为什么哭得如此伤心?
  如果没有路,你就停下来歇息。如果没有忧愁,你就真诚地微笑。但幸福,如果没有幸福,我还拿什么活着?
  悟空突然停下脚步,转过身来看着我。
  “你幸福吗?”三千年前,我幽幽地问道。
  他笑了。他哭了。一颗晶莹的眼泪凋谢在无邪的笑容里。
  “师父,你终于醒了。”
  “我已经醒了,你呢?”
  “我还在那个梦里。”
  那是多么悠长的一个梦啊。
  三千年。雪山融为江河,沧海凝固成岩石,桃花开过,人间又是春天。岁月的四壁题写着不朽的传奇,总有一些让人心潮难平。
  “我吃了你好不好?”
  “不好。”
  “为什么?”
  “我还没洗澡呢。”
  她咯咯地笑,粉红色的长裙轻轻摆动,象一朵美丽的红霞。
  “有一句话我一直想对你说。”
  “你想说什么?”如来微笑着问。
  西天的红霞轻轻拂过双眼,我看见那张美丽的脸在千千万万年光阴之外向我深情微笑,在生命的彼岸向我频频招手。
  “操你娘。”我轻轻地对如来说。

WMI 的一个实现

作者:Paul Li

翻译:Abbey



原文出处:Code Project:Windows Management Instrumentation (WMI) Implementation

源代码下载:wmi.zip(45KB)


介绍

  这是我在继上一篇文章"My Explorer"之后关于Windows Management Instrumentation(Windows管理规范)的又一新作。我将向你展示一些技巧,让你可以在远程地访问网络中其他计算机的操作系统、服务、当前运行着的进程等等信息,当然前提是你必须得拥有这些计算机的管理员权限。同时我也将向你展示如何利用WMI来启动或者停止服务、终止进程、创建进程。这是程序的主界面:

                      

开始

  在这个WMI应用程序里,我创建了一个包含了四个用户控制的库WMIControlLibrary。这四个用户控制分别是Explorer,SystemInfo,Services与Processes。每个控制都有其特定的功用。以下是对每个控制作用的一个简单描述:

  • Explorer控制     我把我那个"My Explorer"转换成了一个用户控制,它还是用来显示你系统上的驱动器、目录、文件等信息。
  • SystemInfo 控制* 这个控制用来显示操作系统与硬件数据及清单等信息。
  • Services 控制*   这个控制用来显示系统当前运行着的服务。
  • Process 控制*    这个控制用来显示系统当前运行着的进程。

(*注意:这个控制可以用来监控本地或者网络上的远程系统。)

上述的每个控制都引用了System.Management命名空间,以保证它们能访问各自特定的系统信息。

控制的状态事件

  这其中的一些控制需要点时间才能从系统获取相关的信息,因此我在每个控制中都实现了一个事件UpdateStatus(string e)。这样每个控制就可以更新主应用程序窗体的状态条,用户也能很清楚地知道控制正在干什么了。

//控制内部的代码
//声明一个Status的事件委托类型
public delegate void Status(string e);

//声明了一个更新状态的事件

public event Status UpdateStatus;

//更新状态条
UpdateStatus("Hello world.");

//主程序代码
//用参数中的字符串刷新状态条的显示文本
private void refreshStatusBar(string stringStatus)
{
    //更新状态条
    statusBarStatus.Text = stringStatus;
}

Explorer 控制

  在Explorer控制内部,我使用了WMI的Win32_LogicalDisk类来访问所有本地的及网络映射的驱动器。要访问驱动器的相关信息,我得先使用一个ManagementObjectSearcher对象来获取一个包含了我所需驱动器信息的ManagementOjbectCollection对象(译注:原文用的是class,我认为不准确,因此改译为对象)。之后,我们就可以自由支配所有这些驱动器的信息。(比如驱动器名、类型、卷标、标识等等)。你也可以只查询剩余空间低于1MByte的驱动器的信息,对此只需要改变ManagementObjectSearcher参数而已:

//译注:这句就是查询剩余空间低于1MByte的SQL语句,用在ManagementObjectSearcher的构造时。
//是不是很象一般数据库编程里用的SQL语句啊?
Select * From Win32_LogicalDisk Where FreeSpace < 1000000

//取得驱动器集
ManagementObjectSearcher query =                        new ManagementObjectSearcher ("SELECT * From Win32_LogicalDisk "); 

ManagementObjectCollection queryCollection = query.Get(); 

//遍历每个对象,以获取每个驱动器的信息
foreach ( ManagementObject mo in queryCollection)
{
    switch (int.Parse( mo["DriveType"].ToString()))
    {
        case Removable: //可移动驱动器
            imageIndex = 5;
            selectIndex = 5;
            break; 

        case LocalDisk: //本地驱动器
            imageIndex = 6;
            selectIndex = 6;
            break; 

        case CD: //CD-ROM驱动器
            imageIndex = 7;
            selectIndex = 7;
            break; 

        case Network: //网络驱动器
            imageIndex = 8;
            selectIndex = 8;
            break; 

        default: //缺省:文件夹
            imageIndex = 2;
            selectIndex = 3;
            break;
    } 

    //获取驱动器名
    Console.WriteLine("Drive: " + mo["Name"].ToString());
} 

SystemInfo 控制

  SystemInfo控制用于显示你的本地计算机或者远程计算机上一些不同类型的信息。它首先定义一个ConnectionOptions对象,并设置好该对象的UserName与Password属性,准备用此来建立一个WMI的连接。之后再以该ConnectionOptions对象为参数,使用本地或远程计算机的主机名创建一个ManagementScope对象。

//建立远程计算机连接
ConnectionOptions co = new ConnectionOptions();

co.Username = textUserID.Text;
co.Password = textPassword.Text;

//将管理范围确定为该远程计算机
System.Management.ManagementScope ms = new System.Management.ManagementScope
					("\\\\" + stringHostName + "\\root\\cimv2", co); 

  现在我们就要准备通过创建一个ObjectQuery 成员对象来访问这个系统上的信息了。我们需要利用这个ObjectQuery对象和之前的那个ManagementScope对象来创建一个ManagementObjectSearcher对象。然后再调用该ManagementObjectSearcher对象的Get()方法来执行ObjectQuery对象定义的那个查询命令,并将查询结果返回到一个ManagementObject对象集中。

//查询操作系统信息
oq = new System.Management.ObjectQuery("SELECT * FROM Win32_OperatingSystem");

query = new ManagementObjectSearcher(ms,oq);

queryCollection = query.Get();

foreach ( ManagementObject mo in queryCollection)
{
    //在树中创建一个操作系统的子结点
    createChildNode(nodeCollection, "Operating System: " + mo["Caption"]);
    createChildNode(nodeCollection, "Version: " + mo["Version"]);
    createChildNode(nodeCollection, "Manufacturer : " + mo["Manufacturer"]);
    createChildNode(nodeCollection, "Computer Name : " + mo["csname"]);
    createChildNode(nodeCollection, "Windows Directory : " + mo["WindowsDirectory"]);
}

  如果你只关心本地主机的信息,那你可以不用创建ConnectionOption,ManagementScope,与ObjectQuery这些对象。你只需要用SQL查询语句串创建一个ManagementObjectSearcher对象,然后直接调用该对象的Get()方法,就能以一个ManagementObjectCollection对象的形式返回本地主机的信息了。

ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * From Win32_OperatingSystem");
ManagementObjectCollection queryCollection = query.Get();

  SystemInfo控制也用于显示计算机相关的其他信息:系统制造商,处理器,BIOS,时区,内存、网络连接、显卡等等。用于查询这些信息的代码只是在SQL查询语句和返回属性上不同而已,所以为了减少篇幅我就不把代码写出来了。具体的代码你可以看下载包里的内容。

Service 控制

Service控制使用了这样的一个查询来返回系统中所有服务的信息:

SELECT * FROM Win32_Service

  为了能启动或者停止一个服务,我为ListView动态地创建了一个弹出式菜单(上下文菜单)。你在列表的某个项上单击鼠标右键时,一个启动或停止服务(依赖于服务的当前运行状态)的菜单就会弹出。当菜单项被点击后,我需要利用这样的查询语句获得该服务的ManagementObject对象:

SELECT * FROM Win32_Service WHERE Name = ''ServiceName''

  接着我就可以通过调用ManagementObject.InvokeMethod()方法来启动或者停止该服务了。InvokeMethod()方法的第一个参数是一个Observer。我传递一个ManagementOperationObserver对象给这个方法,来管理这些异步操作,以及相应的异步事件与信息。通过检查返回的completionHandlerObj.ReturnObject的returnValue属性,我们就可以确定操作是否成功了。

/// 
/// List view的鼠标右击事件导致动态上下文菜单的生成
/// 

private void listViewServices_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
    System.Windows.Forms.ListView listViewObject = (System.Windows.Forms.ListView) sender;
    ContextMenu mnuContextMenu = new ContextMenu();
    MenuItem menuItem = new MenuItem();
    ManagementObjectCollection queryCollection;

    //是否是鼠标右键单击
    if (e.Button == System.Windows.Forms.MouseButtons.Right)
    {
        //取得服务的名称
        ServiceName = listViewObject.GetItemAt(e.X, e.Y).Text;

        //取得列表项
        ServiceItem = listViewObject.GetItemAt(e.X,e.Y);

        //创建弹出式菜单
        listViewObject.ContextMenu = mnuContextMenu;

        try
        {
            //取得特定的服务对象
            queryCollection = getServiceCollection("SELECT * FROM Win32_Service Where Name =
		''" + ServiceName + "''");
            foreach ( ManagementObject mo in queryCollection)
            {
                //据服务的当前状态创建相应的菜单
                if (mo["Started"].Equals(true))
                {
                    menuItem.Text = "Stop";

                    //设置动作Action属性
                    ServiceAction = "StopService";
                }
                else
                {
                    menuItem.Text = "Start";

                    ServiceAction = "StartService";
                }
                mnuContextMenu.MenuItems.Add(menuItem);

                // 给菜单项挂上事件处理函数
                menuItem.Click  += new System.EventHandler(this.menuItem_Click);
            }
        }
        catch (Exception e1)
        {
            MessageBox.Show("Error: " + e1);
        }
    }
}

/// 
/// List view上下文菜单的事件响应函数
///
///
///
private void menuItem_Click(object sender, System.EventArgs e)
{
    ManagementObjectCollection queryCollection;
    ListViewItem lvItem;

    //设置一个异步回调函数的句柄
    ManagementOperationObserver observer = new ManagementOperationObserver();
    completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
    observer.ObjectReady += new ObjectReadyEventHandler(completionHandlerObj.Done);

    //获得特定的服务对象
    queryCollection = getServiceCollection("Select * from Win32_Service Where Name =''" +
                           ServiceName + "''");

    //更新状态条
    updateStatus("Starting/Stopping service..."); 

    foreach ( ManagementObject mo in queryCollection)
    {
        //启动或者停止服务
        mo.InvokeMethod(observer, ServiceAction, null);
    }
    //等待,直到invoke调用完成或者超时5秒后 

    int intCount = 0;  

    while(!completionHandlerObj.IsComplete)
    {
        if
        (intCount >  10)
        {
            MessageBox.Show("Terminate process timed out.", "Terminate Process Status");
            break;
        }

        //等待0.5秒
        System.Threading.Thread.Sleep(500); 

        //增加计数器
        intCount++;
    } 

    //检查是否成功执行了
    if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
    {
        //成功
        lvItem = ServiceItem;

        if (ServiceAction == "StartService")
            lvItem.SubItems[2].Text = "Started";
        else
            lvItem.SubItems[2].Text = "Stop";
    }
    else
    {
        //错误信息
        string stringAction;

        if (ServiceAction == "StartService")
            stringAction = "start";
        else
            stringAction = "stop";

        MessageBox.Show("Failed to " + stringAction + " service " + ServiceName + ".",
                                        "Start/Stop Service Failure");
    }

    //清除对象
    ServiceName = "";
    ServiceAction = "";
    ServiceItem = null;

    //更新状态条
    updateStatus("Ready");
    this.Update();
}

//----------------------------------
// 完整的处理器
//----------------------------------
using System;
using System.Management;

namespace completionHandler
{
    /// 
    /// MyHandler类在InvokeMethod()调用完成后处理通知
    ///
    public class MyHandler
    {
        private bool isComplete = false;
        private ManagementBaseObject returnObject;

        /// 
        /// 当InvokeMethod完成后触发Done事件
        ///
        public void Done(object sender, ObjectReadyEventArgs e)
        {
            isComplete = true;
            returnObject = e.NewObject;
        }

        /// 
        /// 取IsComplete属性
        ///
        public bool IsComplete
        {
            get
            {
                return isComplete;
            }
        }

        /// 
        /// 属性允许访问主函数里的返回结果
        ///
        public ManagementBaseObject ReturnObject
        {
            get
            {
                return returnObject;
            }
        }

    }
}

Process 控制

  Process控制显示系统中运行着的进程,启动进程的用户,CPU使用率,内存的使用情况。要获得进程的用户信息,需要调用GetOwner(User, Domain)方法,其中的User 与Domain是传出参数。我们如何才能从InvokeMethod()调用中取回这些传出型参数呢?这实际取决于我们是如何实现这个InvokeMethod()方法的。如果我们不需要管理异步操作,那么我们只需要传递一个string数组给InvokeMethod()以获取传出的参数值。否则,我们就无需给InvokeMethod()传递任何的参数了,而是从completionHandlerObj.ReturnObject属性中取回传出的参数值。

//-------------------------------------------------
//在不使用observer对象的情况下获取进程用户信息
//--------------------------------------------------
//为InvokeMethod()方法准备参数表

string[] methodArgs = {"", ""}; 

//获取进程用户信息
mo.InvokeMethod("GetOwner", methodArgs); 

//methodArgs[0] 进程用户
//methodArgs[1] 进程的域 

//-----------------------------------------------
//在使用observer对象的情况下获取进程用户信息
//-----------------------------------------------
mo.InvokeMethod(observer,"GetOwner", null);

while (!completionHandlerObj.IsComplete)
{
    System.Threading.Thread.Sleep(500);
} 

if (completionHandlerObj.ReturnObject["returnValue"].
    ToString() == "0") 

    structProcess.stringUserName = completionHandlerObj.
        ReturnObject.Properties["User"].Value.ToString();
else
    structProcess.stringUserName = "";

终止进程

  终止一个特定的进程与启动或停止一个服务类似。首先还是要取得选中进程对应的 ManagementObject 对象,然后通过调用InvokeMethod(observer, "Terminate", null) 来杀死一个进程。

//设置一个异步回调的句柄
ManagementOperationObserver observer = new ManagementOperationObserver();
completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
observer.ObjectReady  += new ObjectReadyEventHandler(completionHandlerObj.Done);

//获取进程的ManagementObject
queryCollection = getProcessCollection("Select * from Win32_Process Where ProcessID =
				''" + ProcessID + "''");

//更新状态条
updateStatus("Invoking terminate process");

foreach ( ManagementObject mo in queryCollection)
{
    //启动或者停止服务(译注:作者真懒?)
    mo.InvokeMethod(observer, "Terminate", null);
}

//等待,直到invoke调用完成或者超时5秒后 

int intCount = 0;
while (!completionHandlerObj.IsComplete)
{
    if (intCount == 10)
    {
        MessageBox.Show("Terminate process timed out.", "Terminate Process Status");
        break;
    }
    //等待0.5秒
    System.Threading.Thread.Sleep(500); 

    //增加计数器
    intCount++;
} 

if (intCount != 10)
{
    //InvokeMethod尚未超时
    if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
    {
        lvItem = ProcessItem;
        lvItem.Remove();
    }
    else
    {
        MessageBox.Show("Error terminating process.",
            "Terminate Process");
    }
}

创建进程

  要创建一个进程,我们需要调用ManagementClass 的InvokeMethod ()方法。我们可以这么创建一个ManagementClass对象:

ManagementClass processClass = New ManagementClass(ms,path,null);

  其中的ms是一个ManagementScope对象,path是一个ManagementPath对象。ManagementScope对应了一个管理操作对应的范围。ManagementPath则提供了一个对Win32_Process进行解析与创建的封装。在调用ManagementClass.InvokeMethod(observer, methodName, inParameters)之前,我们还需要做点其他的准备。我们得把四个传入参数封装到一个object数组里。

uint32 Create(string CommandLine,
				string CurrentDirectory,
				Win32_ProcessStartup ProcessStartupInformation,
			    uint32* ProcessId);

参数说明

  • CommandLine – [传入] 要执行的命令行。如果有必要,系统会自动在末尾追加一个null字符来截断该串,表示真正要执行的文件。
  • CurrentDirectory – [传入] 子进程的当前驱动器与当前目录。这个串必须保证当前目录能解析到一个已知的路径。用户可以定义一个绝对的或相对的路径作为当前的工作目录。如果该参数为null,新创建的进程就会使用父进程的同一路径。这样做是主要是为了保证操作系统外壳能确定应用程序启动的初始驱动器和工作目录。
  • ProcessStartupInformation – [传入] 这是一个Windows进程的启动配置,请参见Win32_ProcessStartup.
  • ProcessId – [传出] 一个全局的用于标识进程的标识符。这个值的生存期自进程创建时起,至进程终结时止。
//为InvokeMethod()准备参数
object[] methodArgs = {stringCommandLine, null, null, 0};

//执行这个方法
processClass.InvokeMethod (observer, "Create", methodArgs);

  下面是创建进程的实现代码。我编写了一个CreateProcess()函数接受一个传入的命令行字符串stringCommandLine作为参数。当你调用CreateProcess("Calc.exe")时,就意味着创建了一个新的计算器的进程。就这么简单。

/// 
/// 在一个本地或者远程机器上调用Create方法
///
///
private void CreateProcess(string stringCommandLine)
{
    //设置一个异步回调的句柄
    ManagementOperationObserver observer = new ManagementOperationObserver();
    completionHandler.MyHandler completionHandlerObj = new completionHandler.MyHandler();
    observer.ObjectReady  += new ObjectReadyEventHandler(completionHandlerObj.Done);

    string stringMachineName = "";

    //连接到远程计算机
    ConnectionOptions co = new ConnectionOptions();

    if (radioMachine.Checked == true)
    {
        stringMachineName = "localhost";
    }
    else
    {
        stringMachineName = textIP.Text;
    }

    if (stringMachineName.Trim().Length == 0)
    {
        MessageBox.Show("Must enter machine IP address or name.");
        return;
    }

    //获取用户名与密码
    if (textUserID.Text.Trim().Length   > 0)
    {
        co.Username = textUserID.Text;
        co.Password = textPassword.Text;
    }

    //获取指向机器的接入点
    System.Management.ManagementScope ms = new System.Management.ManagementScope("\\\\" +
	        stringMachineName + "\\root\\cimv2", co);      

  //获取进程的路径
    ManagementPath path = new ManagementPath( "Win32_Process");

    //获取将要被调用的进程的对象
    ManagementClass processClass = new ManagementClass(ms,path,null);

    //更新状态条
    updateStatus("Create process " + stringCommandLine + ".");

    //为方法准备参数
    object[] methodArgs = {stringCommandLine, null, null, 0};

    //执行方法
    processClass.InvokeMethod (observer, "Create", methodArgs);

    //等待,直到invoke调用完成或者超时5秒后
    int intCount = 0;
    while (!completionHandlerObj.IsComplete)
    {
        if (intCount > 10)
        {
            MessageBox.Show("Create process timed out.", "Terminate Process Status");
            break;
        }
         //等待0.5秒
        System.Threading.Thread.Sleep(500); 

         //增加计数器
        intCount++;
    } 

    if (intCount != 10)
    {
        //InvokeMethod尚未超时
        //检查是否出现错误
        if (completionHandlerObj.ReturnObject.Properties["returnValue"].Value.ToString() == "0")
        {
            //刷新进程列表
            this.Refresh();
        }
        else
        {
            MessageBox.Show("Error creating new process.",
                "Create New Process");
        }
    }

    //更新状态条
    updateStatus("Ready");
    this.Update();
}

总结

  编写这个演示用的WMI应用程序,增加了我不少的经验。这只展示了WMI很小一部分的功能。我想有了我给出的注释,代码还容易理解吧。

你可以使用WMI完成下列工作:

  • 控制硬件与软件
  • 监控事件
  • 就某个事件运行一个脚本
  • 就某个事件发出一封Email

MSDN 中关于WMI的内容:Windows Management Instrumentation



Paul Li 是在纽约的 Dell 专业服务的首席软件安全顾问

用户不喜欢反应慢的程序。在执行耗时较长的操作时,使用多线程是明智之举,它可以提高程序 UI 的响应速度,使得一切运行显得更为快速。在 Windows 中进行多线程编程曾经是 C++ 开发人员的专属特权,但是现在,可以使用所有兼容 Microsoft .NET 的语言来编写。

不过Windows 窗体体系结构对线程使用制定了严格的规则。如果只是编写单线程应用程序,则没必要知道这些规则,这是因为单线程的代码不可能违反这些规则。然而,一旦采用多线程,就需要理解 Windows 窗体中最重要的一条线程规则:除了极少数的例外情况,否则都不要在它的创建线程以外的线程中使用控件的任何成员。本规则的例外情况有文档说明,但这样的情况非常少。这适用于其类派生自 System.Windows.Forms.Control 的任何对象,其中几乎包括 UI 中的所有元素。所有的 UI 元素(包括表单本身)都是从 Control 类派生的对象。此外,这条规则的结果是一个被包含的控件(如,包含在一个表单中的按钮)必须与包含它控件位处于同一个线程中。也就是说,一个窗口中的所有控件属于同一个 UI 线程。实际中,大部分 Windows 窗体应用程序最终都只有一个线程,所有 UI 活动都发生在这个线程上。这个线程通常称为 UI 线程。这意味着您不能调用用户界面中任意控件上的任何方法,除非在该方法的文档说明中指出可以调用。该规则的例外情况(总有文档记录)非常少而且它们之间关系也不大。请注意,以下代码是非法的:

        private Thread myThread;

        private void Form1_Load(object sender, EventArgs e)

        {

            myThread = new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        private void RunsOnWorkerThread()

        {

            label1.Text = "myThread线程调用UI控件";

    }

如果您在 .NET Framework 1.0 版本中尝试运行这段代码,也许会侥幸运行成功,或者初看起来是如此。这就是多线程错误中的主要问题,即它们并不会立即显现出来。甚至当出现了一些错误时,在第一次演示程序之前一切看起来也都很正常。但不要搞错我刚才显示的这段代码明显违反了规则,并且可以预见,任何抱希望于“试运行时良好,应该就没有问题”的人在即将到来的调试期是会付出沉重代价的。

 

下面我们来看看有哪些方法可以解决这一问题。

 

 

一、System.Windows.Forms.MethodInvoker 类型是一个系统定义的委托,用于调用不带参数的方法。

 

        private Thread myThread;

        private void Form1_Load(object sender, EventArgs e)

        {

            myThread = new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        private void RunsOnWorkerThread()

        {

            MethodInvoker mi = new MethodInvoker(SetControlsProp);

            BeginInvoke(mi);

        }

        private void SetControlsProp()

        {

            label1.Text = "myThread线程调用UI控件";

        }

二、直接用System.EventHandle(可带参数)

        private Thread myThread;

        private void Form1_Load(object sender, EventArgs e)

        {

            myThread = new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        private void RunsOnWorkerThread()

        {

            //DoSomethingSlow();

            string pList = "myThread线程调用UI控件";

            label1.BeginInvoke(new System.EventHandler(UpdateUI), pList);

        }

        //直接用System.EventHandler,没有必要自定义委托

        private void UpdateUI(object o, System.EventArgs e)

        {

           //UI线程设置label1属性

            label1.Text = o.ToString() + "成功!";

        }

 

 

三、包装 Control.Invoke

 

虽然第二个方法中的代码解决了这个问题,但它相当繁琐。如果辅助线程希望在结束时提供更多的反馈信息,而不是简单地给出“Finished!”消息,则 BeginInvoke 过于复杂的使用方法会令人生畏。为了传达其他消息,例如“正在处理”、“一切顺利”等等,需要设法向 UpdateUI 函数传递一个参数。可能还需要添加一个进度栏以提高反馈能力。这么多次调用 BeginInvoke 可能导致辅助线程受该代码支配。这样不仅会造成不便,而且考虑到辅助线程与 UI 的协调性,这样设计也不好。对这些进行分析之后,我们认为包装函数可以解决这两个问题。

        private Thread myThread;

        private void Form1_Load(object sender, EventArgs e)

        {

            myThread = new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        private void RunsOnWorkerThread()

        {

            ////DoSomethingSlow();

            for (int i = 0; i < 100; i++)

            {

                ShowProgress( Convert.ToString(i)+"%", i);

                Thread.Sleep(100);

            }

        }

        public void ShowProgress(string msg, int percentDone)

        {

            // Wrap the parameters in some EventArgs-derived custom class:

            System.EventArgs e = new MyProgressEvents(msg, percentDone);

            object[] pList = { this, e };

 

            BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);

        }

        private delegate void MyProgressEventsHandler(object sender, MyProgressEvents e);

        private void UpdateUI(object sender, MyProgressEvents e)

        {

            lblStatus.Text = e.Msg;

            myProgressControl.Value = e.PercentDone;

        }

    public class MyProgressEvents : EventArgs

    {

        public string Msg;

        public int PercentDone;

        public MyProgressEvents(string msg, int per)

        {

            Msg = msg;

            PercentDone = per;

        }

 }

ShowProgress 方法对将调用引向正确线程的工作进行封装。这意味着辅助线程代码不再担心需要过多关注 UI 细节,而只要定期调用 ShowProgress 即可。

如果我提供一个设计为可从任何线程调用的公共方法,则完全有可能某人会从 UI 线程调用这个方法。在这种情况下,没必要调用 BeginInvoke,因为我已经处于正确的线程中。调用 Invoke 完全是浪费时间和资源,不如直接调用适当的方法。为了避免这种情况,Control 类将公开一个称为 InvokeRequired 的属性。这是“只限 UI 线程”规则的另一个例外。它可从任何线程读取,如果调用线程是 UI 线程,则返回假,其他线程则返回真。这意味着我可以按以下方式修改包装:

        public void ShowProgress(string msg, int percentDone)

        {

            if (InvokeRequired)

            {

                // As before

                //…

            }

            else

            {

                // We’re already on the UI thread just

                // call straight through.

                UpdateUI(this, new MyProgressEvents(msg,PercentDone));

            }

        }

参考:

http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/misMultithreading.mspx?mfr=true

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=715064
2007年02月01日
  • 1.mysqlimport的语法介绍:

mysqlimport位于mysql/bin目录中,是mysql的一个载入(或者说导入)数据的一个非常有效的工具。这是一个命令行工具。有两个参数以及大量的选项可供选择。这个工具把一个文本文件(text file)导入到你指定的数据库和表中。比方说我们要从文件Customers.txt中把数据导入到数据库Meet_A_Geek中的表Custermers中:

mysqlimport Meet_A_Geek Customers.txt

注意:这里Customers.txt是我们要导入数据的文本文件, 而Meet_A_Geek是我们要操作的数据库, 数据库中的表名是Customers,这里文本文件的数据格式必须与Customers表中的记录格式一致,否则mysqlimport命令将会出错。

其中表的名字是导入文件的第一个句号(.)前面文件字符串,另外一个例子:

mysqlimport Meet_A_Geek Cus.to.mers.txt

那么我们将把文件中的内容导入到数据库Meet_A_Geek 中的Cus表中。

上面的例子中,都只用到两个参数,并没有用到更多的选项,下面介绍mysqlimport的选项

2.mysqlimport的常用选项介绍:

选项 功能

-d or –delete 新数据导入数据表中之前删除数据数据表中的所有信息

-f or –force 不管是否遇到错误,mysqlimport将强制继续插入数据

-i or –ignore mysqlimport跳过或者忽略那些有相同唯一

关键字的行, 导入文件中的数据将被忽略。

-l or -lock-tables 数据被插入之前锁住表,这样就防止了, 你在更新数据库时,用户的查询和更新受到影响。

-r or -replace 这个选项与-i选项的作用相反;此选项将替代 表中有相同唯一关键字的记录。

–fields-enclosed- by= char 指定文本文件中数据的记录时以什么括起的, 很多情况下 数据以双引号括起。 默认的情况下数据是没有被字符括起的。

–fields-terminated- by=char 指定各个数据的值之间的分隔符,在句号分隔的文件中, 分隔符是句号。您可以用此选项指定数据之间的分隔符。 默认的分隔符是跳格符(Tab)

–lines-terminated- by=str 此选项指定文本文件中行与行之间数据的分隔字符串 或者字符。 默认的情况下mysqlimport以newline为行分隔符。 您可以选择用一个字符串来替代一个单个的字符: 一个新行或者一个回车。

mysqlimport命令常用的选项还有-v 显示版本(version), -p 提示输入密码(password)等。

3.例子:导入一个以逗号为分隔符的文件

文件中行的记录格式是这样的:

"1", "ORD89876", "1 Dozen Roses", "19991226"

我们的任务是要把这个文件里面的数据导入到数据库Meet_A_Geek中的表格Orders中, 我们使用这个命令:

bin/mysqlimport –prl –fields-enclosed-by=" –fields-terminated-by=, Meet_A_Geek Orders.txt

这个命令可能看起来很不爽,不过当你熟悉了之后,这是非常简单的。第一部分,bin/mysqlimport,告诉操作系统你要运行的命令是mysql/bin目录下的mysqlimport,选项p是要求输入密码,这样就要求你在改动数据库之前输入密码,操作起来会更安全。 我们用了r选项是因为我们想要把表中的唯一关键字与文件记录中有重复唯一关键字的记录替换成文件中的数据。我们表单中的数据不是最新的,需要用文件中的数据去更新,因而就用r这个选项,替代数据库中已经有的记录。l选项的作用是在我们插入数据的时候锁住表,这样就阻止了用户在我们更新表的时候对表进行查询或者更改的操作。

批处理导入文件,从sql文件导入数据到数据库中 ,批处理是一种非交互式运行mysql程序的方法,如同您在mysql中使用的命令一样,你仍然将使用这些命令。

为了实现批处理,您重定向一个文件到mysql程序中,首先我们需要一个文本文件,这个文本文件包含有与我们在mysql中输入的命令相同的文本。 //www.w3sky.com

比如我们要插入一些数据,使用包含下面文本的文件(文件名为New_Data.sql,当然我们也可以取名为New_Data.txt及任何其他的合法名字,并不一定要以后缀sql结尾):

USE Meet_A_Geek;

INSERT INTO Customers (Customer_ID, Last_Name) VALUES(NULL, "Block");

INSERT INTO Customers (Customer_ID, Last_Name) VALUES(NULL, "Newton");

INSERT INTO Customers (Customer_ID, Last_Name) VALUES(NULL, "Simmons");

注意上面的这些句子的语法都必须是正确的,并且每个句子以分号结束。 上面的USE命令选择数据库,INSERT命令插入数据。

下面我们要把上面的文件导入到数据库中,导入之前要确认数据库已经在运行,即是mysqld进程(或者说服务,Windows NT下面称为”服务“,unix下面为”进程“)已经在运行。

 

然后运行下面的命令:

bin/mysql –p < /home/mark/New_Data.sql

接着按提示输入密码,如果上面的文件中的语句没有错误,那么这些数据就被导入到了数据库中。

命令行中使用LOAD DATA INFILE 从文件中导入数据到数据库:

现在您可能会问自己,"究竟为什么我要输入所有的这些SQL语句到文件中,然后通过程序运行它们呢?” 这样看起来好像需要大量的工作。很好,你这样想很可能就对了。但是假如你有从所有这些命令中产生的log记录呢?现在这样就很棒,嗯,大多数数据库都会自动产生数据库中的事件记录的log。而大部分log都包含有用过的原始的SQL命令。因此,如果您不能从您现在的数据库中导出数据到新的mysql数据库中使用,那么您可以使用log和mysql的批处理特性,来快速且方便地导入您地数据。当然,这样就省去了打字的麻烦。

LOAD DATA INFILE

这是我们要介绍的最后一个导入数据到MySQL数据库中的方法。这个命令与mysqlimport非常相似,但这个方法可以在mysql命令行中使用。也就是说您可以在所有使用API的程序中使用这个命令。使用这种方法,您就可以在应用程序中导入您想要导入的数据。 使用这个命令之前,mysqld进程(服务)必须已经在运行。

启动mysql命令行:

bin/mysql –p

按提示输入密码,成功进入mysql命令行之后,输入下面的命令:

USE Meet_A_Geek;

LOAD DATA INFILE "/home/mark/data.sql" INTO TABLE Orders;

简单的讲,这样将会把文件data.sql中的内容导入到表Orders中,如mysqlimport工具一样,这个命令也有一些可以选择的参数。比如您需要把自己的电脑上的数据导入到远程的数据库服务器中,您可以使用下面的命令:

LOAD DATA LOCAL INFILE "C:\MyDocs\SQL.txt" INTO TABLE Orders;

上面的LOCAL参数表示文件是本地的文件,服务器是您所登陆的服务器。 这样就省去了使用ftp来上传文件到服务器,MySQL替你完成了. 您也可以设置插入语句的优先级,如果您要把它标记为低优先级(LOW_PRIORITY),那么MySQL将会等到没有其他人读这个表的时候,才把插入数据。可以使用如下的命令:

LOAD DATA LOW_PRIORITY INFILE "/home/mark/data.sql" INTO TABLE Orders;

您也可以指定是否在插入数据的时候,取代或者忽略文件与数据表中重复的键值。替代重复的键值的语法:

LOAD DATA LOW_PRIORITY INFILE "/home/mark/data.sql" REPLACE INTO TABLE Orders;

上面的句子看起来有点笨拙,但却把关键字放在了让您的剖析器可以理解的地方。 //from www.w3sky.com

下面的一对选项描述了文件的记录格式,这些选项也是在mysqlimport工具中可以用的。他们在这里看起来有点不同。首先,要用到FIELDS关键字,如果用到这个关键字,MySQL剖析器希望看到至少有下面的一个选项:

TERMINATED BY character

ENCLOSED BY character

ESCAPED BY character

这些关键字与它们的参数跟mysqlimport中的用法是一样的. The TERMINATED BY 描述字段的分隔符,默认情况下是tab字符(\t)

ENCLOSED BY描述的是字段的括起字符。比方以引号括起每一个字段。

ESCAPED BY 描述的转义字符。默认的是反些杠(backslash:\ ).

下面仍然使用前面的mysqlimport命令的例子,用LOAD DATA INFILE语句把同样的文件导入到数据库中:

LOAD DATA INFILE "/home/mark/Orders.txt" REPLACE INTO TABLE Orders FIELDS TERMINATED BY ‘,’ ENCLOSED BY ‘"’;

LOAD DATA INFILE语句中有一个mysqlimport工具中没有特点:

LOAD DATA INFILE 可以按指定的列把文件导入到数据库中。 当我们要把数据的一部分内容导入的时候,这个特点就很重要。比方说,我们要从Access数据库升级到MySQL数据库的时候,需要加入一些栏目(列/字段/field)到MySQL数据库中,以适应一些额外的需要。

这个时候,我们的Access数据库中的数据仍然是可用的,但是因为这些数据的栏目(field)与MySQL中的不再匹配,因此而无法再使用mysqlimport工具。尽管如此,我们仍然可以使用LOAD DATA INFILE,下面的例子显示了如何向指定的栏目(field)中导入数据:

LOAD DATA INFILE "/home/Order.txt" INTO TABLE Orders(Order_Number, Order_Date, Customer_ID);

如您所见,我们可以指定需要的栏目(fields)。这些指定的字段依然是以括号括起,由逗号分隔的,如果您遗漏了其中任何一个,MySQL将会提醒您

导出数据的方法:Methods of Exporting Data

您可以看到MySQL有很多可以导入数据的方法,然而这些只是数据传输中的一半。另外的一般是从MySQL数据库中导出数据。有许多的原因我们需要导出数据。一个重要的原因是用于备份数据库。数据的造价常常是昂贵的,需要谨慎处理它们。经常地备份可以帮助防止宝贵数据地丢失;另外一个原因是,也许您希望导出数据来共享。 在这个信息技术不断成长的世界中,共享数据变得越来越常见。

比方说Macmillan USA维护护着一个将要出版的书籍的大型数据库。这个数据库在许多书店之间共享,这样他们就知道哪些书将会很快出版。医院越来越走向采用无纸病历记录,这样这些病历可以随时跟着你。世界变得越来越小,信息也被共享得越来越多。有很多中导出数据得方法,它们都跟导入数据很相似。因为,毕竟,这些都只是一种透视得方式。从数据库导出的数据就是从另一端导入的数据。这里我们并不讨论其他的数据库各种各样的导出数据的方法,您将学会如何用MySQL来实现数据导出。

使用mysqldump:

 

(mysqldump命令位于mysql/bin/目录中)

mysqldump工具很多方面类似相反作用的工具mysqlimport。它们有一些同样的选项。但mysqldump能够做更多的事情。它可以把整个数据库装载到一个单独的文本文件中。这个文件包含有所有重建您的数据库所需要的SQL命令。这个命令取得所有的模式(Schema,后面有解释)并且将其转换成DDL语法(CREATE语句,即数据库定义语句),取得所有的数据,并且从这些数据中创建INSERT语句。这个工具将您的数据库中所有的设计倒转。因为所有的东西都被包含到了一个文本文件中。这个文本文件可以用一个简单的批处理和一个合适SQL语句导回到MySQL中。这个工具令人难以置信地简单而快速。决不会有半点让人头疼地地方。

因此,如果您像装载整个数据库Meet_A_Geek的内容到一个文件中,可以使用下面的命令:

bin/mysqldump –p Meet_A_Geek > MeetAGeek_Dump_File.txt

这个语句也允许您指定一个表进行dump(备份/导出/装载?)。如果您只是希望把数据库Meet_A_Geek中的表Orders中的整个内容导出到一个文件,可以使用下面的命令:

bin/mysqldump –p Meet_A_Geek Orders >MeetAGeek_Orders.txt

这个非常的灵活,您甚至可以使用WHERE从句来选择您需要的记录导出到文件中。要达到这样的目的,可以使用类似于下面的命令:

bin/mysqldump –p –where="Order_ID > 2000" Meet_A_Geek Orders > Special_Dump.txt

mysqldump工具有大量的选项,部分选项如下:

选项/Option 作用/Action Performed

–add-drop-table

这个选项将会在每一个表的前面加上DROP TABLE IF

EXISTS语句,这样可以保证导回MySQL数据库的时候不会出错,因为每次导回的时候,都会首先检查表是否存在,存在就删除

–add-locks

这个选项会在INSERT语句中捆上一个LOCK TABLE和UNLOCK

TABLE语句。这就防止在这些记录被再次导入数据库时其他用户对表进行的操作

-c or – complete_insert

这个选项使得mysqldump命令给每一个产生INSERT语句加上列(field)的名字。当把数据导出导另外一个数据库时这个选项很有用。

–delayed-insert 在INSERT命令中加入DELAY选项

-F or -flush-logs 使用这个选项,在执行导出之前将会刷新MySQL服务器的log.

-f or -force 使用这个选项,即使有错误发生,仍然继续导出

–full 这个选项把附加信息也加到CREATE TABLE的语句中

-l or -lock-tables 使用这个选项,导出表的时候服务器将会给表加锁。

-t or -no-create- info

这个选项使的mysqldump命令不创建CREATE TABLE语句,这个选项在您只需要数据而不需要DDL(数据库定义语句)时很方便。

-d or -no-data 这个选项使的mysqldump命令不创建INSERT语句。

在您只需要DDL语句时,可以使用这个选项。

–opt 此选项将打开所有会提高文件导出速度和创造一个可以更快导入的文件的选项。 //from www.w3sky.com

-q or -quick 这个选项使得MySQL不会把整个导出的内容读入内存再执行导出,而是在读到的时候就写入导文件中。

-T path or -tab = path 这个选项将会创建两个文件,一个文件包含DDL语句或者表创建语句,另一个文件包含数据。DDL文件被命名为table_name.sql,数据文件被命名为table_name.txt.路径名是存放这两个文件的目录。目录必须已经存在,并且命令的使用者有对文件的特权。

-w "WHERE Clause" or -where = "Where clause "

如前面所讲的,您可以使用这一选项来过筛选将要放到 导出文件的数据。

假定您需要为一个表单中要用到的帐号建立一个文件,经理要看今年(2004年)所有的订单(Orders),它们并不对DDL感兴趣,并且需要文件有逗号分隔,因为这样就很容易导入到Excel中。 为了完成这个人物,您可以使用下面的句子:

bin/mysqldump –p –where "Order_Date >=’2000-01-01′"

–tab = /home/mark –no-create-info –fields-terminated-by=, Meet_A_Geek Orders

这将会得到您想要的结果。

schema:模式

The set of statements, expressed in data definition language, that completely describe the structure of a data base. 一组以数据定义语言来表达的语句集,该语句集完整地描述了数据库的结构。

SELECT INTO OUTFILE :

如果您觉得mysqldump工具不够酷,就使用SELECT INTO OUTFILE吧, MySQL同样提供一个跟LOAD DATA INFILE命令有相反作用的命令,这就是SELECT INTO OUTFILE 命令,这两个命令有很多的相似之处。首先,它们有所有的选项几乎相同。现在您需要完成前面用mysqldump完成的功能,可以依照下面的步骤进行操作:

1. 确保mysqld进程(服务)已经在运行

2. cd /usr/local/mysql

3. bin/mysqladmin ping ;// 如果这个句子通不过,可以用这个:mysqladmin -u root -p ping mysqladmin ping用于检测mysqld的状态,is alive说明正在运行,出错则可能需要用户名和密码。

4. 启动MySQL 监听程序.

5. bin/mysql –p Meet_A_Geek;// 进入mysql命令行,并且打开数据库Meet_A_Geek,需要输入密码

6. 在命令行中,输入一下命令:

SELECT * INTO OUTFILE ‘/home/mark/Orders.txt’

FIELDS

TERMINATED BY = ‘,’

FROM Orders

WHERE Order_Date >= ‘2000-01-01′

在你按了Return(回车)之后,文件就创建了。这个句子就像一个规则的SELECT语句,只是把想屏幕的输出重定向到了文件中。这意味这您可以使用JOIN来实现多表的高级查询。这个特点也可以被用作一个报表产生器。

比方说,您可以组合这一章中讨论的方法来产生一个非常有趣的查询,试试这个:

在mysql目录建立一个名为Report_G.rpt 的文本文件,加入下面的行:

USE Meet_A_Geek;

INSERT INTO Customers (Customer_ID, Last_Name, First_Name)

VALUES (NULL, "Kinnard", "Vicky");

INSERT INTO Customers (Customer_ID, Last_Name, First_Name)

VALUES (NULL, "Kinnard", "Steven");

INSERT INTO Customers (Customer_ID, Last_Name, First_Name)

VALUES (NULL, "Brown", "Sam");

SELECT Last_Name INTO OUTFILE ‘/home/mark/Report.rpt’

FROM Customers WHERE Customer_ID > 1; 

然后确认 mysql进程在运行,并且您在mysql目录中, 输入下面的命令:

bin/mysql < Report_G.rpt检查您命名作为输出的文件,这个文件将会包含所有您在Customers表中输入的顾客的姓。 如您所见,您可以使用今天学到的导入/导出(import/export)的方法来帮助得到报表。

翻译声明: 本文内容来自Sam’s Teach Yourself MySQL in 21 Days一书的部分内容,by Mark Maslakowski 英文原文版权属原作者所有,中文的部分翻译有略有增删;原书讲的过于清楚的地方有删,讲的不清楚的地方有增;如果有翻译的不妥或者不正确的地方,请指正。