2009年5月13日星期三

一日双证

2009年5月12日,对我来说是个好日子,因为这一天,我获得了两个证书--驾驶证和学位证。


昨天早上我一大早起来,去驾校拿到了我早该拿的驾驶证。然后马不停蹄地赶到闵行,参加毕业典礼。时隔7年,我再一次穿上学位服(虽然颜色不同),有些自豪,有些感慨。但是最强的感受,竟然是感觉自己穿这身衣服像Jedi武士了。(我知道我不可救药了,大家随便拍砖吧。)下次一定要去弄身Jedi武士的衣服穿穿,太帅了!

有意思的是,这两个证一个花了4,000元,3个月,另外一个花了40,000元,3年,时间上10倍,花费上10倍,但他们的含金量是不是有100倍之差,那就不知道了。

不管怎么说,这事儿算是告一段落了。我以后会不会去念个博士回来,也是未知数,可能性不是零。接下去,我该一心一意的去完成人生的几件大事了:)


2009年5月7日星期四

未来5年开发人员需要的10项技能

昨天收到MSDN中文快递上编者推荐了一本书和一篇文章:

对《代码大全》我保持景仰的态度,的确是好书一本。我也对我买到现在才看了几章而感到汗颜。

不过对于后面那篇文章,我一时好奇看了一眼,我觉得这篇文章正如MS的产品宣传一样,就是用来观念殖民的。

我翻译这十项技能如下:

1: One of the “Big Three” (.NET, Java, PHP)
三大时髦语言(.net , java, php)

2: Rich Internet Applications (RIAs) - (Learn about silverlight)
富客户端应用开发(silverlight)

3: Web development
web 开发

4: Web services – (Learn about Web Services in 1 hour)
web 服务

5: Soft skills
软技能

6: One dynamic and/or functional programming language
一门动态言语或者函数变成语言

7: Agile methodologies
敏捷方法

8: Domain knowledge
领域知识

9: Development “hygiene” (Resources here:  Technical Book Club)
不知道什么意思

10: Mobile development (Resources here:  mobile)
移动开发(手持设备,嵌入式等)


如果这篇文章的标题上限定词是:微软技术应用程序开发,那我无话可说,举双手双脚赞成。但是这篇文章标题是Software for students: 10 Skills Developers will need in the next 5 years。

我认为学生就应该打好开发的基础,比如数据结构,操作系统,算法,数据库、程序开发基本概念、程序的架构等基本科目,不应该把过多的精力放在平台相关的应用技术上。

李开复在给中国学生的三封信里面也强调了基础的重要性。Joel同学在自己的blog上花了很大的篇幅告诉读者现在学生正在步入的误区。那些软件大家们对待初学者给出的第一条建议大多是基本功的训练。UCLA,MIT, Stanford这些名校从来不教什么.net。人家教的是算法,程序的构造与解析,操作系统,计算机体系架构等课程。出来的人还不是google,ms随便去。MS还抢得欢。人家可不会什么silverlight。

软件开发就像练武功,内功练好了,那些外家招式学起来是很快的。内功才是真正要花时间的地方。这点无论对于学生,还是我们这些工作了一些年头的中级开发人员来说都一样。

运用基本功解决问题,自上而下分解问题,设计解决方案,设计良好的程序结构,框架和模块,最后转化成程序,除了最后一步,其他的都与具体的程序无关。而前面几步,才是决定你是好的程序员还是差的程序员的关键。

那么如果是有开发经验的人呢?这10条是否适用?

我觉得10条还是太多了,一个人不可能做做web开发,又去做做mobile开发,这样啥都做点结果就是啥都做不好。这10条里面我唯一看中的一点就是8.领域知识。有经验的人都知道,领域知识才是自己升官发财的决定性因素,编程技术重要性要差些。如果还要加一点,敏捷开发可以勉强入选。因为Methodology绝对是个时髦货,在过去十年里面,每几年都会流行一种Methodology,从CMMI到RUP,从RUP到XP,从XP到Agile,每种methodology只能解决很小一个范围内的问题。如Ivan Jacbson所说,最终,你得有自己的Methodology。

还有一个对于任何人都非常重要的技能就是Soft skill。其实这项技能比领域知识还要有用,比如会拍马屁的人,有可能比做了十年,非常了解领域知识的人爬的快。比如察言观色,见风使舵,舌灿莲花等等。好吧,正经点说,就是交流能力,语言能力,管理能力,调配能力等。有了这些能力,我们才能从单一的开发人员变成所谓的复合型人才。

龙珠是个好游戏

最近几天迷上了NDS上最新出的《龙珠改!赛亚人来袭》



这游戏绝对Fans向,游戏的剧情完全参照动画片。我正好前几个月看完了所有的动画片,趁记忆犹新的时候重新打游戏温故一下。虽说这个RPG绝对是无聊的地雷战式的RPG,从游戏性上来说是不怎么样。但是Fans的力量是无穷的,借用现在流行的话说,我对龙珠有大爱,所以再密集的地雷也忍了。

游戏的流程还算比较长,从第二十三届天下第一武道会(和短笛决战的那场)开始一直到决战贝吉塔,是龙珠里面打弗利萨之前一大高潮。声优基本上跟动画里面一样,大招都有特写,每当听到熟悉的“卡埋哈迈哈”,我就激动,心跳加速,肾上腺素狂喷。有点夸张了,但是这种我为游戏狂的感觉真的是久违了。

本作还原动画几乎100%,很多招数一看就能想起动画和漫画里面的场景。比如悟空的メテオコンビネーション,这招就是在23届武道会打短笛时候用的招数。这种Fans向的东西就是要让人不断联想曾经看过,听过的东西。就跟机战一样,每当听到自己喜欢的人物在那里“叽里呱啦”一阵喊,就会觉得精神超级振奋。

Fans向,真是个好东西。

最后,借用某位超高位领导子孙的名言:龙珠是个好游戏。

2009年4月26日星期日

翻出些老东西

今天整理房间时候突然整理出一些小时候做的玩意儿,让人真是怀念阿。
于是拍了点照片,拿出来秀一下。

这是以前打诸葛孔明传时自己做的攻略。
这是封面。借给侄子,结果他的爸爸看到了,在上面写了个长大了当军事家,让我真是汗颜。



攻略的文字部分



自己画的地图。



道具,法术部分。之后开始学习了一些游戏日文,别的不懂,钢的,铁的,木头的之类的形容词倒是都能认得了。

2009年4月15日星期三

从公司跑回家

我最近胖了很多,体重已经快达到90KG的警戒线了。我终于感到有点怕了,于是加强了锻炼。一周至少两天去公司健身房跑步。不过我知道,在“温室”里面的花朵永远长不大的,要出去经历点风雨才会长大。于是我决定从公司跑回家。距离大概是8,9KM左右,我以前也跑过,但是从来没有完整跑下来过。

上个星期五晚上下了班,我一路跑回家,原先给自己定的计划是当中可以休息4次,到跑不动了就停下来走回去。结果让我自己也吃了一惊,除了遇到几个红灯停了3,4次以外,我竟然从头跑到了尾!用时1个小时。

事实上我一直没有感到极点的出现,而且跑的一直很轻松,最后感觉呼吸都要跟走路差不多了。当然可能因为速度也很慢,但是不管怎么样,都打破了以前的记录。

不过之后几天身体就不舒服了,喉咙发炎,身体处于一个悬崖口,我很清楚这种状况,如果不管,肯定第二天就喉咙发炎,然后感冒,咳嗽,去医院看病吃药。还好我算是很清楚自己身体的状况,提早吃药,压了下来。

由此我也感觉到自己的身体素质水平,还是比较差。我有个朋友,有次参加半程马拉松,不小心误拿了全程的报名单,结果也跑下来了。尽管照他的话说,跑完之后那双腿感觉都不是自己的了,但休息了一天就没事儿了。这种身体素质真是让人羡慕。拿我妈的话来说,我看着很胖,可是都是虚胖,没用!稍微累点就生病。我一直反驳她,但是心里知道,事实如此。

接下来,我打算继续这样锻炼,保持这样的频率一段时间。一个月起码两次跑步回家,等到稳定下来,再继续提高速度或增加强度。

2009年4月9日星期四

Google Image Search with Specified Color

Google的图像搜索增加了一个新的功能:指定颜色。

看看下面一幅图,发现搜出来的图有什么特点没?是的,这些图都是绿色的。
注意搜索框右下角的颜色选取图标没?这里就是秘密所在。



这个功能虽然小巧,结合以前就有的filter功能就可以进行例如“红色的蝴蝶 clip art”这样相对精确的搜索了。对经常做PPT的人来说无疑是个非常方便的功能。

Google Image Search with Specified olor

2009年4月1日星期三

code: 用adjacent_find判断序列升序



int main(){

int a[] = {1,2,3,4,5,6,7};
int n = sizeof(a)/sizeof(int);

int* p = adjacent_find( a, a+n, greater());

if( p != a+n )
   cout<<" the " << p-a << " element is out of order " <<endl;
else
    cout<< " the sequence is in a ascending order "<<endl;

}


这段代码很简单,这里用adjacent_find的第二个版本:使用一个predicate来判断相邻的两个数是否是升序,注意,这里允许相邻的两个数字相同。比如{1,2,3,3,4,5,6,7}也被认为是升序。如果不希望这样,可以用greater_equal()这个Predicate。

增加一个新的tag : Code_Snippets

看到肥猫拿到了今年的最具潜力无聊猥琐博客奖,我心痛啊!!


我写了一年的无聊文章,就是为了获得这个奖,最后花落别家,我气的差点吐血!!

不过我决定痛定思痛,再接再励,为了获得联合国教科文组织明年的最具潜力无聊博客奖,我决定从今天起推出一个新的专栏(tag)——Code_Snippets。用来摘录自己看过的写的精彩的代码,当然也会加上我自己写的代码,这样可以大大增加我的博客的无聊值(BoringRank,简称BK)。按我的计算,我这样写一年,明年我的博客的无聊度可以把肥猫的博客甩开2个银河系!

Visual Studio Free E-book!!!

首先声明,这不是愚人节笑话。MS Press终于大方了一记,放出了一些免费的电子书。

其中有

Visual Studio 2008: Programming Microsoft LINQ


 

Introducing Microsoft Silverlight 2, Second Edition



Programming Microsoft ASP.NET 3.5


这三本时髦的重量级参考书。

你可以免费下载部分章节试读,如果觉得喜欢,注册一下就可以获得全部章节。

http://csna01.libredigital.com/?urvs5cn3s8



2009年3月20日星期五

cmd2cb.exe v0.92 is published

In my post <pipe console program output to clipboard>, I made a tiny tool -- cmd2cb for my own convenience. After several days improvement, cmd2cb v0.92 is avaiable now.

Basic feature:
Pipe the output of a console program to clipboard.


New features:
1. Unicode support, cmd2cb can be used in CJK locale. ( Most important one)
2. Add a parameter -h or -help for help page.
3. When failed copy contents to clipboard, it will redirect the output to console.

Download:
box.net: cmd2cb.zip
namipan.net: cmd2cb.zip (for Chinese)

Screen snapshots:







cmd2cb 0.92版发布了!!

基本功能:
通过管道符把控制台程序的输出重定向到剪贴板,以方便其他程序使用。


增加的功能有:
1. 支持Unicode,因此能支持中文,日文,韩文以及其他ANSI 的CodePage
2. 增加命令行参数-h, -help,显示帮助
3. 在把内容输出到剪贴板失败时,仍旧输出到控制台

截图:
看上面英文版介绍

下载:
box.net: cmd2cb.zip
纳米盘: cmd2cb.zip

100篇blog纪念

我很惭愧,很不好意思,很小声的宣布:“本人终于写了100篇blog啦!”。


我从07年1月30日在blogger开博以来2年间,疏于耕耘,到现在两年多,算下来平均5篇每月都不到,相比引领我入门的morphine,实在让我汗颜。他一个季度的blog数就差不多和我两年的产量一样多了。

不过由于我的blog定位偏向技术,因此也不可能产量很高。有些课题需要花一定时间去研究,整理,才能写成文章。不过我以后会渐渐拓宽自己的思路,写一些别的东西。

说说之后的打算吧。

第一件事就是关于blog空间的。

由于马勒隔壁的鸡恶妇打不溜,我的blog已经无法直接访问了。但是域名mogling.com是在国外托管的,不会有问题。我现在只能暂时用FTP发布到我的域名服务商godaddy.com的免费服务器上,这样又可以通过http://blog.mogling.com访问了。这样做的缺点就是页面上头有广告。输出的FEED也会有影响,而且还只能用超窄的经典模板(屁,这么烂的也算经典?)。

我正在犹豫,是否像morphine一样去弄个没有广告的免费空间,用WP算了。但是现在工作比较忙,对这些网络上的东西也不像以前那样热衷,而且以前做ffsky.com的时候吃尽了颠沛流离的苦,所以对这种方式还是有点吃不准。

第二件事算是预告。
接下来可能会写一个系列的文章,关于STL库的设计和运用的。具体时间还没有定,不过在6月份之前应该会搞定这件事情。这系列文章我一定会精心打造:)


2009年3月16日星期一

Lnk2001: unresolved external symbol _com_util::ConvertBSTRToString

Today my colleague encountered a strange Lnk2001 error while using VS2003.net to build a project.

Here is the error snippet:

error LNK2019: unresolved external symbol "unsigned short * __stdcall _com_util::ConvertStringToBSTR(char const *)" (?ConvertStringToBSTR@_com_util@@YGPA_WPBD@Z) referenced in function "public: __thiscall _bstr_t::Data_t::Data_t(char const *)" (??0Data_t@_bstr_t@@QAE@PBD@Z)

error LNK2019: unresolved external symbol "char * __stdcall _com_util::ConvertBSTRToString(unsigned short *)" (?ConvertBSTRToString@_com_util@@YGPADPA_W@Z) referenced in function "public: char const * __thiscall _bstr_t::Data_t::GetString(void)const " (?GetString@Data_t@_bstr_t@@QBEPBDXZ)

I checked if the correct lib (comsuppw.lib) is included. But he already did.

The same code can pass build in VC2005. But in VC2003, it can’t. That’s interesting.

One possibility is the environment of VC2003 differs from VC2005. By making a rough search, I found they are almost same. After I googled this bug, a post inspires me. I forget to check the method’s return type and argument type.

Let take a look at the same errors in VC2005:
Error    5     error LNK2001: unresolved external symbol "char * __stdcall _com_util::ConvertBSTRToString(wchar_t *)" (?ConvertBSTRToString@_com_util@@YGPADPA_W@Z) XLRCreatorDll.obj
Error    6     error LNK2001: unresolved external symbol "wchar_t * __stdcall _com_util::ConvertStringToBSTR(char const *)" (?ConvertStringToBSTR@_com_util@@YGPA_WPBD@Z) XLRCreatorDll.obj


Compare the error msg in VC2005 and VC2003, we can see the return type and argument type is different:

char * __stdcall _com_util::ConvertBSTRToString(unsigned short *)    ----VC2003

char * __stdcall _com_util::ConvertBSTRToString(wchar_t *) ----VC2005


What does this mean?

Yes, the wchar_t in VC2003 project is treated as unsigned short instead of wchar_t itself. We know in some old OS implementation, wchar_t is only typedef of unsigned short. But in new release, wchat_t is definitely a native data type to support Unicode.

Then we know, in VC2003, the compiler still treats wchar_t as unsigned short rather than a native data type. But the prototype in comsuppw.lib, it requires wchar_t.

Thus comes the very simple solution: add /Zc:wchar_t to command line of compiler.

After adding this option, the annoying Lnk2001 disappears.

2009年3月7日星期六

养目荠菜粥

荠菜,又名护生草、鸡心菜、净肠草,在路边或野地里随处可见。

荠菜不仅是营养丰富的美味食品,而且还能治疗多种疾病,民间不仅有“阳春三月三,荠菜当灵丹”的谚语,还流传着“春食荠菜赛仙丹”的说法。可见荠菜不仅是佳肴一碟,更是灵药一方。在中药里,荠菜的药用价值非常广泛,被誉为“菜中甘草”。祖国医学认为:荠菜味甘、性凉,归肝、脾、肾经,有和脾、利水、止血、明目等效用。

我这里推荐一道简单易学又清淡好吃的荠菜粥:


做法:荠菜(量自己控制),梗米50克,武火烧开,文火煮熟。

吃法:空腹食用,连续一个星期效果比较好。

推荐对象:每天对着显示器工作,感到自己眼睛不舒服的上班族。荠菜粥对目赤目暗,视网膜出血等症有比较好的治疗效果。其实我眼睛不舒服已经几个月了,最近连续吃荠菜粥,荠菜饭,炒荠菜,大量荠菜投下去,终于有点效果了。

另外喜欢精致的朋友可以用珍珠米。煮粥的米要预先腌:约半碗米淘洗干净后,要用2汤匙的油、1个半茶匙的盐和少许水(2茶匙)拌匀,腌至少半小时。这样煮出来的粥非常软,入口即化。煮的时候放一个轻的小调羹在锅底与粥同煮,水沸腾过程中,小调羹也被带动,可以防止粥煮粘底。

2009年3月1日星期日

Share a very interesting physics game

the game "Crayon Physics" reveals the fun of physics to you and bring you back to your childhood drawing with crayons.



you may get it from http://www.crayonphysics.com/

2009年2月28日星期六

Unicode, A programmer should know

首先说说这个标题,因为我最近非常迷星战,所以顺带也喜欢上了Yoda大师说话的腔调,以后可能会用的更多,情别见怪(反正也没人看)。

“曹操”为什么会变成“变巨”一文中,我提到了Unicode是多语言共存的解决方案,本文将详细介绍Unicode,以及为什么解决了各种令人头疼的编码问题。

那unicode解决了哪些问题呢?其实最主要的是这几个问题:

  1. 人们希望有一个单一的字符集,能包含这个星球上所有的字符
  2. 每个字符都有一个独立的编号,不会出现一个编号对应不同字符的事情
  3. 可以根据需要变换不同的编码方式,但是不会影响字符编号
那么看看unicode是怎么解决这些问题的呢?

Unicode 是一个国际标准,它主要包含了:
  • 超大的字符集,可以有1,000,000个字符,code space为[0x0-0x10FFFF]
  • 每个字符的code point编码方法
  • 一个标准的字符集
  • 一组字符属性的枚举值
  • 提供字符集的多种编码
有很多人把unicode 与utf8,utf16这些编码混为一谈,看完本文后,应该不会再存在这样的混淆了。

Unicode有其标准的书写格式。要表示一个字符的Unicode码,可以这样写:
U+0041 
这就是大写字母A的Unicode码,U+表示这是Unicode码,后面的十六进制数字是这个字符的unicode code point。比如hello的code point就是U+0048 U+0065 U+006C U+006C U+006F。可以看到,这些码值去掉前面的00,就跟ASCII码一样了,是的,unicode最大限度跟ASCII保持兼容。

Unicode的字符集可以说包括了这个星球上所有的字符。包括一些我们可能永远也不会遇到的字符。不过Unicode标准把一些最常用的字符放在0x0000-0xFFFF这个区间中,这样有些系统实现的时候就不需要用那么多空间来存放不常用的字符了。这个区间被称为BMP(Basic Multilingual Plane)。Unicode一共有17个Plane,有6个已经被占用,剩下的都是空的。如下表所示:

Plane RangeDescriptionAbbreviation
0 0000-FFFFBasic Multilingual PlaneBMP
1 10000-1FFFFSupplementary Multilingual PlaneSMP
2 20000-2FFFFSupplementary Ideographic PlaneSIP
3-13 30000-DFFFFcurrently unassigned
14 E0000-EFFFFSupplementary Special-purpose PlaneSSP
15 E0000-EFFFFSupplementary Private Use Area-A 
16 E0000-EFFFFSupplementary Private Use Area-B 

在BMP中的任何一个code point都用4位16进制数来表示。其他的plane中的code point用5-6位。

Unicode的编码问题是我们最关心的。Unicode允许使用不同的编码方式,也就是你平时看到的utf8, utf16之类的名字。他们的特点在于编码出来的值各不相同,但是解码之后,都对应到一个唯一的unicode code point。这样保证了unicode编码之间可以无损(在BMP中)转换以及映射(mapping)的唯一性。Unicode中把一个值的code point及其使用某种编码得到的码值称为一个映射(mapping)。

在unicode发展过程中,出现过多种编码。我这里就介绍几种主要的编码,在此过程中出现过两个组织,各自发展出一套映射方法,称为Unicode Transformation Format(UTF)和 Universal Character Set(UCS)。 后来两个组织合并在一起,UTF和UCS也随之合并了。简单的说它们之间的对应关系是UTF后面跟的是一个编码的位数,UCS后面跟的数字是编码的字节数(以八位作为一个字节),比如UTF-16和UCS2几乎是同一个东西。但实际上UCS2只实现了BMP内的字符编码,而UTF16则实现了所有unicode编码。但为了简单期间,我们在这里认为它们是一个东西。

UTF-16/UCS2
最直观的编码方式就是一一映射。一个unicode码对应一个UTF-16编码。比如上面所说的hello,其编码为00 48 00 65 00 6C 00 6C 00 6F。但是问题马上来了,在大端法(big endian)和小端法(little-endian)的机器上编码得到的串的顺序不一样。在大端法的机器上,同样hello经过编码得到的是48 00 65 00 6C 00 6C 00 6F 00。而48 00也是一个合法的unicode code point,因此把在大端法机器上保存的文件拿到小端法机器上读就会解析成完全不同的东西。人们于是想到用最前面的两个字节来标明字节顺序。这两个字同样是unicode码,U+FEFF,一般被称为BOM(Byte Order Mark)。FE FF表明大端法,FF FE表明小端法。

UTF-16使用2个字节来进行编码,因此只能编码到BMP的code point。对于其他的plane,UTF-16采用一种额外的编码方式,达到3,4字节的编码。但是一般情况下,UTF16指的就是2字节的编码。这样的编码非常方便,所有的字符都占用一个unsigned short,对于传统的c字符串函数就非常方便。

在windows上,为了方便utf16被直接称为unicode编码,与ANSI对应。你在使用notepade时可以保存成unicode,指的便是utf16。

下图显示了在linux上使用utf16编码



比较下在windows下使用utf16(unicode)编码,注意到BOM了吗?




UTF-8
UTF-8可以说是用得最广泛的编码方式了,很多Linux的发布版都使用UTF-8作为其系统编码。UTF-8的产生原因可能是由于有人觉得UTF-16太占用空间了。在美国英语系统下,每一个字符都有一个00,字符串大小凭空增加了一倍。于是人们研究出UTF-8这个可以兼容ASCII的变长编码方式。UTF8编码出来的字符占1-4字节。

具体占位如下图所示:


对于在U+0000到U+007F区间(是不是很熟,对了,就是ASCII码的区间)内字符,使用1个字节来编码,因此完全兼容ASCII码,而且省空间。

[U+0080, U+07FF] 区间内的字符,基本上是欧洲各国字符集,使用2个字节来编码。
图中的yyy是code point中的高位字节,由于最多到7,因此只要3个bit就可以编码。
图中的xxxxxxxx是code point中的低位字节,从00到FF,需要用的所有8个bit,在编码时,把低位的6个bit与10结合作为一个字节,剩下的2个bit与yyy,110结合作为一个字节。

[U+0800, U+FFFF] 区间内的字符,包括中文,日文,韩文等都使用3个byte来编码。详细的编码规则在图中已经解释的很清楚了,我这里不再重复。

下图显示了在linux使用utf8编码,于utf16的截图相比,utf8对曹操这两个中文各用了3个byte进行编码。



比较下在windows上使用utf8编码,首先是字节顺序不同。然后你还可以看到utf8的BOM,0xEF 0xBB 0xBF。这BOM不仅和系统,还和使用的软件有关。notepad无论什么编码都会加上BOM。



UTF32/UCS4
这是最浪费空间的一种编码方式,它也是一一映射,但是UTF32映射了所有的unicode code point。对于每一个code point,UTF32如其名字所示,使用32bit也就是4个字节来进行编码。比如A=00 00 00 41。

很多程序使用utf32作为内部编码,在保存的时候它可以无损的转换成其他编码。而且因为现在的硬件总线的关系,这种编码的寻址和读写更快。比如Apache的Xerce2 就是这么做得。



还有其他很多编码,比如国标的GB18030。只要符合unicode标准,你也可以创造自己的编码。


讲了这么多,最初的问题也算回答得差不多了。Jeffrey Richter[1], Joey Spolsky[2], Charles Petzold[3]等人都在各自的著作中强调unicode对现代编程的重要性。虽然我人微言轻,但是我还是想附和一句:unicode以及编码的知识应该是每一个专业的程序员需要了解的知识。现在,停止ANSI程序,开始用unicode编写你的正式程序吧。




注1:《Programming Applications for Microsoft Windows》, chapter 2, Jeffrey Richter
注2:《Joel on software》, Charpter 4, Joel Spolsky
注3:《Programming windows》,chapter 2, Charles Petzold

2009年2月27日星期五

一张14.74亿像素的照片

美国总统奥巴马就职大典downloading
你可以看清大部分人的脸
http://gigapan.org/viewGigapanFullscreen.php?auth=033ef14483ee899496648c2b4b06233c

下面是缩略图

2009年2月21日星期六

“曹操”为什么会变成“变巨”?


以前在PC上玩过三国志的人大多数都会记得一个奇怪的词——“变巨”。但大家都知道,那是曹操,因为头像放在那,一看就知道是曹操,一张枭雄的脸。可是为什么会显示成“变巨”呢?有些人会说,编码问题呗。是的,那到底是怎么回事儿呢?

这个问题的根本原因可以说是编码和解码方式不匹配

要解释这个问题,首先要解释下Code Page的概念。在Windows下,打开控制面板->区域和语言选项->高级,里面就有代码页转换表。我们在里面会看到类似936  (ANSI/OEM - 简体中文 GBK) 这样的选项。这就是一个Code Page(代码页)。

Code Page最早始于IBM,当时个人PC上只有ASCII码,用0x00-0x7F这128个code point(代码点)来编码控制字符,数字,大小写字母,特殊字符和打印符号等。但是很快ASCII码就无法适应需求了,更多的西欧字符要求被加入码表。于是出现了EBCDIC codepages。这些Code Pages扩充了ASCII码,成为256位的一个码表。前面0x00-0x7F与ASCII码保持一致,后面的128根据不同的语言定义不同的code page。微软一开始借用了这些CODE PAGE,之后用自己的ANSI(或者说ISO 8859-1)的code page作为补充,成为现在我们在Windows操作系统上的CodePage。

在Windows操作系统中,对CJK(Chinese,Japanese,Korean) 也用Code Page进行处理。但是中文等语言的字符实在太多,不可能用256位的code page来表示,那怎么办呢?一个直观的想法是用双字节来编码一个字符,理论上就可以安置65536个字符。也可以用多字节来表示,单字节部分可以兼容ASCII码,双字节部分编码CJK字符。在code page设计时是怎么考虑的我不知道,但最后还是采用了后一种方式。

看下图:


这是一张Windows Codepage 936(以下简称CP936)的示意图。前面0x00-0x7F和ASCII码一致,和Windows上其他的codepage保持一致。后面128个字节被称为lead byte,用来和另一个byte组合成一个CJK字符的编码。每个lead byte又对应一张子表,比如0x81这个lead byte对应的子表如下图所示:


于是汉字“丢”的GBK码就是0x8147。但是子表并不一定都是256个字符。理论上GBK码按照这种方式可以编码128*256个=32768个字符。但实际上中文字符要多的多。因此出现了其他更好的编码,这里先按下不说。Windows的设计的这个字符集被称为MBCS(Multi Byte Character Set)。

知道了codepage的概念,我们就可以解释“曹操”-“变巨”的问题了。为什么我开头说“编码和解码方式不匹配”呢?想想看,曹操是存放在什么地方的?用什么编码存放的?

对了,曹操两个字是存放在资源文件里面的。而我们玩的三国志是第三波汉化的繁体中文版,也就是说,曹操两个字是以BIG5码(CP950)进行编码的。从以下的CP950_B1可以看到,“曹”字的编码是:B1E4。而我们是在简体中文的Windows上玩这个游戏的,对于磁盘上存放的B1E4,我们用CP936 GBK码来解码的。从下面的CP936_B1可以看到B1E4正好就是“变”字。



同理可以知道操对应的是巨,繁体字赵云对应的简体字是化冻。

事实上,这小小的一个问题,牵涉到Locale,字符集,codepage,编码等各种问题。
而CodePage在使用过程中也暴露出很多问题;最主要的就是一个代码点(code point)在不同的codepage中代表不同的字符,变成了一对多的关系,导致在一个程序中没法使用多语言。不同的厂商在设计code page时缺少文档,不利于交流等。

解决这些问题的,就是Unicode。我会在最近的blog中详细介绍Unicode。因为Unicode是每个程序员需要了解的概念。

最后,我总结下Code Page的概念:
  • Code Page只在Windows和IBM的mainframe上有,Linux/Unix等操作系统并没有这个概念。
  • Code Page是为了解决多语言及本地化的问题而产生的。
  • 使用Code Page,无法在一个程序中支持多语言。
  • 在Windows中,一旦换了Code Page,我们需要重启。
  • Code Page提供了字符集和编码/解码方式。

更新于2009.2.28: 关于unicode的介绍已经写好了--Unicode, a programmer should know

雷到我了

From Reference Photos

不知道三分钟后,他们是不是全部下场了

2009年2月8日星期日

log to console when programming windows application

我们在开发Windows界面程序的时候往往需要打印一些运行过程中的调试信息。很多人都使用OutputDebugString来把信息打印到WINDOWS内部的调试器上。然后通过SysInternal的DebugView来查看。

今天我来介绍另外一个非常有用的方法--打开一个控制台窗口

Impossible! 你是在开发窗口程序,怎么可能打开控制台呢?或许有人会这样说。如果不可能,我还写这么多干什么?事实上只要2行代码就可以搞定:

AllocConsole();
freopen( "CONOUT$" , "a" , stdout );
这两行代码打开了一个和当前应用程序共享同一个进程空间的控制台。因此你可以简单的用cout来输出任何你想输出的信息。如下图:

不过一般在这两句话只会用在debug版本下,因此最好在外面加上对DEBUG版本的判断:
#ifdef _DEBUG
AllocConsole();
freopen( "CONOUT$" , "a" , stdout );
#endif
最后提一下,这两个窗口无论关掉哪一个,都会同时关闭另外一个,相当于同时退出进程。

该技巧在编写WinForm的时候也是适用的。所要做的只是把项目设置(Project Setting)中的Output Type改为"Console Application", 如下图:

有了这个Console,你就可以在程序中随意的使用System.Console.WriteLine输出调试信息了。

写windows程序的朋友不妨试一下。

WM_PAINT no end

今天在写程序的时候,发现一个自定义控件的WM_PAINT被不断的调用,以至于CPU占用率上升到40%以上。

我觉得很奇怪,我并没有移动窗口或者做什么引起重绘的操作,WM_PAINT为什么会被不断调用了。之后我发现一开始该控件没有不断重绘,只有第一次重绘之后才开始不断调用。
我的重绘代码是这样写的:

HDC hdc = GetDC(m_hWnd);
....do some drawing
Release(m_hWnd, hdc);
我很老实的遵守了调用DC的对称性规则,有创建也有释放。看来问题不在这里。

再一次仔细看了WM_PAINT的说明其中有几句引起了我的注意:
a WM_PAINT message may have been caused by both a non-NULL update region
仔细体会一下,就得出了这个问题的背后黑手--update region。

我曾在Windows绘图概述一文中写道:
事实上WM_PAINT产生的条件就是有部分窗口失效了。如果不出现这种情况,就不会产生WM_PAINT
当窗口的Client Area发生改变的时候,系统给应用程序发送WM_PAINT消息。系统仅在消息队列中没有其他消息的时候发送该消息给应用程序。一般的用法是在WM_PAINT中调用BeginPaint获得DC,然后进行任何GDI绘图,最后通过EndPaint释放DC
BeginPaint会把需要更新的区域设为NULL,以防连续导致发送WM_PAINT消息。
这里的文字已经很清楚的告诉了我原因,很可惜,这些文字是翻译自MSDN,没有经过自己亲身经历,因此没有深刻的体会。而这次,我正是犯了这个错误。

我接收到的WM_PAINT的消息是由InvalidateRect(NULL)导致的。该函数把整个Client区域都变成了update region.而我在WM_PAINT中又恰恰用了GetDC而不是BeginPaint函数来获取DC,因此导致处理完WM_PAINT后,失效区域没有更新,让Window以为我一直没有重绘,因此DefWindowProc中不断的重复发送WM_PAINT到message queue中,导致了无限循环。

解决的方法有两个,一个是在WM_PAINT中用BeginPaint和EndPaint来处理。另外一种是在WM_PAINT中使用ValidateRect函数。

Bjarne Stroustrup说过,一知半解的程序员是危险的,这个问题没有花去我很多时间,但是它告诉我,不要小看任何一个小小的函数。它或许牵涉到很大的问题。

2009年2月5日星期四

我的第一次就这么没了

好吧,我承认,我有点标题党了。

今天去体检,遇到了一个以前从来没有过的体检项目--查痔疮。结果第一次被人爆了菊花,这感觉真是恶心,不过还算值得,因为查出了轻微的外痔。犯我菊花者说让我不要久坐,常起来走动,少吃辛辣食物。看来我也要注意了,做我这个工作的坏处就是一直坐着,容易引起腰椎问题不说,也容易得痔疮,还容易引起前列腺问题。建议和我一样干程序员的朋友常起来走动走动。

另一个第一次就是第一次洗牙。洗牙这东西,我从来没有好好了解过,一直自以为是的认为是一种奢侈的行为,或者是那些牙齿泛黄的人为了美观才去做的事情。但是今天却是好好得上了一堂课。牙科医生检查了一下我的牙齿,然后让我拿着一面镜子看我的牙齿内侧,真是不看不知道,一看吓一跳。我牙齿内侧就像糊着一层泥一样恶心。医生说这就是牙结石,而且我的牙结石已经很厉害了。当牙齿上的牙结石增多,牙龈(我们常说的牙肉)就会被挤下去,慢慢露出牙根,牙龈对牙齿的保护作用就随之退化和消失。这样就会产生牙周炎,牙龈肿痛之类的问题,牙齿也会慢慢损坏。她建议我尽快做洗牙,把这些牙结石全部洗掉。同时告诉我,洗掉之后,牙齿间就会露出缝隙,因为原来牙龈的部分都已经被牙结石代替了,结石没了之后,牙龈不会立刻恢复。而且随着年龄的增加,牙龈的恢复能力会越来越差,所以这个事情越早越好。

她告诉我,在发达国家,一般人都有这个意识,每半年去检查一次牙齿,如果有牙结石就洗掉它,这是一种常识性的东西。很可笑的是,我活了快30年了,这是生平第一次听到医生跟我解释这个事情。有人可能说她想赚我钱,但是她至少说服了我,虽然洗牙还挺贵(300块全套,因为我比较严重),但是我还是心甘情愿的掏了钱。而且我了解了这个常识,以后我每半年会去检查一次看看。我好吃,所以我不想我的牙齿有什么问题,否则美食在眼前,却无福享用,那可比杀了我还难受啊。

洗牙的过程不多说了,稍微有点小痛。结束之后医生说我的牙结石钙化非常严重,很多地方要洗好几次才能洗掉。现在回想起来,我以前自己用舌头舔牙齿内侧的时候,总归感觉牙缝之间好像被什么东西填住了。还有两个牙齿根部好像有硬硬的东西,当时还傻乎乎的以为自己又长了小牙齿出来。后来在洗牙过程中,医生用力的在牙根中间冲刷,我才知道那是一块钙化物,是牙结石最顽固也是危害最大的地方。如果你和我有同样的经历,建议你去医院检查一下牙齿。

最后由于我门牙附近几颗牙齿钙化特别严重,导致洗牙之后牙龈大量出血,医生给我用了进口药(感觉有点骗钱,不过大刀也宰了,就不在乎这点小刀了)又加了150元。第一次洗牙总共花了450块。最后医生说牙龈恢复很慢,而且像我这种情况也不大可能完全恢复,这是我唯一遗憾的地方了。

医生最后建议我用软一点的牙刷,因为我不抽烟喝酒,因此色素很少,用硬的牙刷还容易损坏牙齿,刷牙的正确方法我想也不用我说了,大家都知道,要上下刷,不要左右刷,里外都要刷,不能只刷外面。

最后,建议从来没有洗过牙的朋友也去医院检查一下,有备无患么,说不定还可以体验你的“第一次”。

2009年1月19日星期一

恭喜万猫喜得千金

这一个月来相当忙,家里的事,公司的事,学校的事,忙的我都没有时间写blog。不过还好,毕竟我的blog也没有什么人订。

这里先恭喜一下好友万猫,今天早上9点26分,我收到他短信,他的女儿出世了。具体时间有兴趣的朋友看他的blog: http://blog.morphinewan.com  不过我不知道他啥时候有空去发布这个消息。从大学第一天起认识到现在,也有十多年了,或许我还没有意识到时间流逝的速度。似乎前阵子还像在大学时聊天,没想到一眨眼,他竟然当爸爸了。我也由衷的为他高兴,真好,又有小囡可以给我捏捏了,哈哈。