梦想 · 责任

梦想

昨天晚上又从半中间看《鲁豫有约》,这次的嘉宾是《天下无贼》中的“傻根”王宝强。他坐在那里憨憨的,操着方言味很浓的普通话与鲁豫聊他的经历,笑得很灿烂也很天真。真的很难想象他是一个拿过金马奖的明星。

王宝强说,他小时候因为看了李连杰和成龙的电影,就梦想有一天能成为他们那样的明星,因此很小就到少林寺学艺。16岁的时候来北京当了一个北漂。干过搬运工,后来跟着一个老乡开始当群众演员,拍了好几部没有台词,甚至在镜头里看不清楚的戏。很辛苦也似乎没有任何希望,他的老乡劝他说,你年龄还这么小,别干这行了,回家去吧。但是他没有轻易放弃他的梦想,一个偶然的机会演了《盲井》,却意外地拿了金马奖最佳新人。从此,他的命运被改变了。

人人都有梦想,关键在于我们坚持了吗?

迪恩说:“人类因梦想而永生;失去梦想,生与死无异。”

责任

看到柴静的blog是因为Keso的一篇文章,实际上Keso的这篇文章完全可以作为柴静blog的导读。

静静地读柴静的blog,让我分明地感受到了这个山西姑娘对新闻事业的热爱,对人的关怀,和她所具有的一份对社会的责任感,读她的文字,有时会让我感动。我得承认,我一直非常敬佩有责任感的人。

崔永元说:“今天你对社会不负责,将来,社会就会对你不负责。”

918

用下面两个链接作为今日的纪念:

http://fools.iblog.cn/post/13369/79314

http://zqb.cyol.com/gb/bd/2006-01/11/node_53.htm

台湾人已经习惯生活在一个民主体制里。民主体制落实在茶米油盐的生活中,是这个意思:

  他的政府大楼,是开放的,门口没有卫兵检查他的证件。他进出政府大楼,犹如进出一个购物商场。他去办一个手续,申请一个文件,盖几个章,一路上通行无阻。拿了号码就等,不会有人插队。轮到他时,公务员不会给他脸色看或刁难他。办好了事情,他还可以在政府大楼里逛一下书店,喝一杯咖啡。咖啡和点心由智障的青年端来,政府规定每一个机关要聘足某一个比例的身心残障者。坐在中庭喝咖啡时,可能刚好看见市长走过,他可以奔过去,当场要一个签名。

  如果他在市政府办事等得太久,或者公务员态度不好,四年后,他可能会把选票投给另一个市长候选人。

  他要出国游玩或进修,是一件极其简单的事,不需要经过政府或机关单位的层层批准,他要出版一本书,没有人要做事先的审查,写作完成后直接进印刷厂,一个月就可以上市。他要找某些信息,网络和书店,图书馆和各级档案室,随他去找。图书馆里的书籍和资料,不需要经过任何特殊关系,都可以借用。政府的每一个单位的年度预算,公开在网上,让他查询。预算中,大至百亿元的工程,小至计算机的台数,都一览无余。如果他坚持,他可以找到民意代表,请民意代表调查某一个机关某一笔钱每一毛钱的流动去向。如果发现钱的使用和预算所列不符合,官员会被处分。

  他习惯看到官员在离职后三个月内搬离官邸或宿舍,撤去所有的秘书和汽车,取消所有的福利和特支。他习惯看到官员为政策错误而被弹劾或鞠躬下台。他习惯读到报纸言论版对政府的抨击、对领导人的诘问,对违法事件的揭露和追踪。他习惯表达对政治人物的取笑和鄙视。

  如果他是个大学教师,他习惯于校长和系主任都是教授们选举产生,而不是和“上级长官”有什么特别关系;有特别关系的反而可能落选。他习惯于开会,所有的决策都透过教授会议讨论和辩论而做出。有时候,他甚至厌烦这民主的实践,因为参与公共事务占据太多的时间。

  他不怕警察,因为有法律保障了他的权利。他敢买房子,因为私有财产受宪法规范。他需要病床,可以不经过贿赂。他发言批评,可以不担心被报复。他的儿女参加考试,落榜了他不怨天尤人,因为他不必怀疑考试的舞弊或不公。捐血或捐钱,他可以捐或不捐,没有人给他配额规定。

  他按时缴税,税金被拿去救济贫童或孤苦老人,他不反对。他习惯生活在一个财富分配相对平均的社会里;走在街上看不见赤贫的乞丐,也很少看见顶级奢华的轿车。他习惯有很多很多的民间慈善组织,在灾难发生的时候,大批义工出动,大批物资聚集,在政府到来之前,已经在苦痛的现场工作。

—- 摘自 龙应台 《你可能不知道的台湾》

贴图: WMP11

昨晚试了试WMP11,好像很花哨的样子……

我的无线路由器

几个同事在讨论买无线路由器的事,将我以前写的一个帖子贴在这里供大家参考:
 

NETGEAR WGR614 54M无线路由器

 
前两天在淘宝上买了一个NetGear WGR614 54M无线路由器,终于把家里的无线网络架了起来。我的T43配上无线网络,真正实现了“随需移动”。

Click to see the original picture.

在挑选无线路由器的时候,我主要看了D-Link DI-624+ANETGEAR WGR614 V6两个型号。从网上的评测文章来看,这两款的性能指标差不多,D-Link的稍微便宜一点,但是NetGear的外形更好看。我希望将路由器放在客厅里,这样覆盖更均衡,因此选择了外形更好看的NetGear。

Click to see the original picture.

设置还是颇费了一番功夫的。我家里的ADSL用的是中兴的Modem,我将它设置为自动拨号、一直在线,并且开启了DHCP功能,并于一个Switch相连,这样家里所有房间的网络接口就都能获得网络信号了。而使用无线路由器时,必须使用无线路由器自身的DHCP。于是我先将Modem的所有设置恢复到出厂设置,然后将Router和Modem相连,在将Router连接到我的电脑。但是我发现,电脑可以获得ip地址,却无法ping通Router。查了半天才发现,原来中兴的Modem出厂设置DHCP已经启用了,而且Modem和Router默认都使用192.168.1.1,造成了地址冲突。在禁用了Modem的DHCP,并将它的ip地质改为其他的地址之后,终于可以通过Router的无线方式上网了。现在我就在通过无线网络写blog呢。:)

在买这个路由器时还有一件尴尬事,在测试好Router之后,我本打算通过支付宝确认付钱的,结果发现支付宝的付款密码和登陆密码是不同的。由于不常在淘宝买东西,我忘记了自己的支付宝密码,取回密码要回答一个问题,我甚至不记得自己当初在设置这个密码问题时,用的是中文还是英文了。再查淘宝的Q&A,要清空密码问题,必须提供自己的身份证明等等文件。我怕麻烦,于是只好作罢,向卖价打招呼说,只有等过了确认期限,等支付宝自动付钱过去了。应该说支付宝这样的设置确实安全,但是我想,在用户忘记密码的情况下,还有没有更好的方式来使取回密码更方便一些呢?比如说不要使用密码问题这种很容易忘记的设置,而是用一些用户注册时的信息,比如生日,身份证号码,电话号码等等信息的组合。

既非常的User Friendly,又非常安全的设计,一直是软件设计方面非常值得研究的课题。

推荐《读库》

在下班回家的路上看了会《读库0603》。这本书是上周五从Joyo上买的,买它倒不是因为看了老白的书评,只是当时买《再说长江》,搭上这本书省的单独买还要送货费。路上看了其中两篇比较感人的文章,《一步三回头》和《大师之哭》,一时觉得非常感动,当时就决定回家写篇blog推荐一下。如果你想我一样时常觉得无聊,或者很久没被什么感动过了,不妨来读读这本书,顺便支持一下编者。如果从Joyoz之类的网站上订购,也就19块多点的价钱。越说越想广告了……

写这篇blog的时候,发现在线编辑器加链接的功能不能用。前几次我都是用HTML直接code进去的,今天觉得总这样比较麻烦,就将我的Space的URL加入到了Trust Site里。谁知不加还好,一加之后竟然遇到了"Missing Framework"的错误,看了半天新闻组也没什么收获,只能将它从Trust Site里面移掉了。这篇blog是用Windows Live Writer写的,这个Writer倒是挺方便,就是似乎对中文输入法的支持有问题。

在我的Spaces上加了一个小游戏,Hangman,挺有趣的 😀

以八卦的名义

晚上坐在那里翻台,无意中翻到了《绝代双骄》那个频道,是众选手亮相的环节,Sophy看到了吉雪萍和谢晖搭档唱歌,就想听听。于是我们就耐着性子听完了前面的几对的演唱,吉雪萍和谢晖唱了一首《只爱一点点》。
 
于是,在他们唱完之后,八卦开始了。主持人程雷说,你们唱的真不错,不如下一回给大家唱一首《当爱已成往事》。显然程雷是在拿吉、谢二人过去的那点事说事。他开了个头,于是各路评委和其他几对选手就纷纷接上,有说程雷酸溜溜的,因为他也曾是吉的绯闻男友;有说谢和吉可以比作中国的贝克汉姆和维多利亚的,吓得吉雪萍直摆手;所有人都纷纷的往那个方向上靠。我当时想,不知道谢的老婆和吉的老公听着这些话会是什么感觉。不过,这些八卦观众肯定是爱听的,这从他们二人的短信得票上就能看得出来。
 
我承认我也喜欢八卦,刚才还专门搜了一把吉雪萍程雷吉雪萍谢晖,了解了一下整个的background。

在.NET中读写INI文件 ——兼谈正则表达式的应用

INI文件是Windows平台上的一种较常用的软件配置文件格式,Windows应用程序常常使用它来保存一些配置信息。它一般是由数个包含key-value对的Section组成,每个key-value对保存着一些软件配置信息。例如最典型的NT系列的启动配置文件boot.ini


 

[boot loader]

timeout=30

default=multi(0)disk(0)rdisk(0)partition(2)/WINDOWS

[operating systems]

multi(0)disk(0)rdisk(0)partition(2)/WINDOWS=”Microsoft Windows XP Professional” /fastdetect

multi(0)disk(0)rdisk(0)partition(1)/WINDOWS=”Microsoft Windows XP Professional” /fastdetect


 

在这个文件中,方括号中的字符串是Section的名字,两个方括号之间的内容为一个SectionSection的内容是一些key-value对,每个key-value对占据一行,例如timeout=30就是一对key-value对,timeoutkey,对应的value30Windows平台专门提供了一组API可以方便地操作INI文件,例如GetPrivateProfileSection()GetPrivateProfileInt()等。

 

随着Windows系列操作系统的不断发展,INI文件的作用逐渐被注册表、XML格式的config文件等所取代,很少再用于系统配置,但我们仍可以在应用程序中使用它。在.NET平台上推荐使用的软件配置文件格式是基于XMLconfig文件,因此在.NET Framework中并没有提供对INI文件读写的特殊支持,使得我们有时在需要读写INI文件时不是很方便。本文将探讨如何使INI文件的读写在.NET平台上变得更加容易。当然,我们可以直接引入上述的API,但本文将不使用API,而是完全基于.NET Framework

 

创建INI文件读写类

 

要在.NET平台上处理INI文件,很自然的想法就是创建一个专门的class来负责INI文件的读写工作,这个class暴露适当的接口供外部调用。一般的INI文件的尺寸很小,因此最简单的做法就是以文本的方式将整个文件读入一个string变量中。类定义如下:


 

 

    public class FileIni

    {

        private string fileContents = null;

 

        public FileIni(string fileName)

        {

            if(File.Exists(fileName))

            {

                StreamReader r = File.OpenText(fileName);

                fileContents = r.ReadToEnd();

                r.Close();

            }

        }

    }


 

 

接下来我们要提供一些方法来操作这个字符串,比如从中返回所有的Section Name、取得特定的key所对应的value等。我们可以使用字符串查找之类的方法来完成这些工作,但是.NET Framework为我们提供了更好的方法,那就是正则表达式。

 

正则表达式

 

所谓正则表达式是一种被设计用来优化字符串操作的语言。它使用一组元字符(Metacharacters)来实现强劲的字符串操作能力。这组元字符最早来自于对DOS文件系统中?*的扩展。在DOS文件系统中,?*分别被用来代替单个字符和字符群组,它们可以被认为是最早的元字符。正则表达式在它们的基础上不断扩充,形成了一套元字符集,能够表达非常复杂的字符串。

 

举例来说,网上注册时常常需要用户输入一个有效的Email地址。当用户输入一个字符串后,我们如何验证这个Email地址是否合法呢?使用下面这个正则表达式可以轻易地实现目的:


 

 

@”^([/w-/.]+)@((/[[0-9]{1,3}/.[0-9]{1,3}/.[0-9]{1,3}/.)|(([/w-]+/.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(/]?)$”


 

关于这个正则比表达式的含义,在此不做过多解释,有兴趣的朋友可以参考相关的正则表达式资料。这个正则表达式虽不能保证用户输入的Email地址100%的真实有效,但至少可以保证用户输入的Email地址看上去是合法有效的。

 

.NET Framework中提供了一些使用正则表达式的类,这些类位于System.Text.RegularExpressions名字空间下。

 

使用正则表达式实现FileIni类的功能

 

现在我们可以使用正则表达式来实现FileIni类的相应功能了。为了返回INI文件中所有Section的名字,我们可以使用一个只读属性SectionNames来返回一个Section Name的字符串数组。


 

        public string[] SectionNames

        {

            get

            {

                // Using regular expression to get all section names.

                string regexPattern = @”/[(?<SectionName>/w*)/]”;

                Regex r = new Regex(regexPattern);  // Match “[anywords]”

                MatchCollection matches = r.Matches(fileContents);

                // Writing all section names to a string array.

                string[] results = new string[matches.Count];

                for(int i = 0; i < matches.Count; i++)

                {

                    results[i] = matches[i].Result(“${SectionName}”);

                }

 

                return results;

            }

        }


 

在上面的代码中,我们使用一个正则表达式:@”/[(?<SectionName>/w*)/]”,对源字符串进行一次匹配就取出了所有的Section Name

 

为了取得特定Section下的特定的keyvalue,我们先要取得此Section下的所有内容,然后再从中取出特定keyvalue


 

        public string GetSectionString(string sectionName)

        {

            string regexPattern = @”(/[” + sectionName + @”/]”

                + @”(?<SectionString>.*)/[)”;

            Regex r = new Regex(regexPattern, RegexOptions.Singleline);

            if(r.IsMatch(fileContents))

            {

                return r.Match(fileContents).Result(“${SectionString}”);

            }

 

            return string.Empty;

        }


 

GetSectionString()根据特定的sectionName取得此Section的全部内容。假设sectionName为字符串boot loader,此时的正则表达式为@”(/[boot loader/](?<SetionString>.*)/[]”。得到Section下的所有内容后,我们再从其中得到我们想要的value值。


 

 

        public string GetKeyString(string sectionName, string keyName)

        {

            string sectionString = this.GetSectionString(sectionName);

            string regexPattern = @”(” + keyName + @”=(?<value>.*)/r/n)”;

            Regex r = new Regex(regexPattern);

            if(r.IsMatch(fileContents))

            {

                return r.Match(fileContents).Result(“${value}”);

            }

           

            return string.Empty;

        }


 

在此基础上,可以得到更多的诸如GetKeyInt()之类的方法。至于写方法,利用RegexReplace()方法也是很容易实现的,在此就不做过多的叙述了。

 

总结

 

本文着重演示了正则表达式在读写INI文件时的应用。所实现的INI文件读写类FileIni扩展性稍显不足,例如,这个类只能处理通用格式的INI文件,对于格式稍有变化的INI文件,此类中的正则表达式就需要修改了。总之,正则表达式是处理字符串的强大工具,掌握了它对我们更高效地处理字符串是绝对有好处的。

 

ATL中的Thunk机制学习

ATL利用一系列的类来管理窗口。为了使代码尽量紧凑而高效,ATL使用了一种有趣的技术来实现与窗口消息相关联的HWND和负责处理消息的对象的this指针之间的映射。具体过程如下:

 

在窗口注册时声明的窗口过程为此窗口对应的窗口类的静态成员函数StartWindowProc,当第一条消息到达此函数时,其处理如下:

 

template <class TBase, class TWinTraits>

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

         CWindowImplBaseT< TBase, TWinTraits >* pThis =

(CWindowImplBaseT< TBase, TWinTraits >*)_Module.ExtractCreateWndData();

         ATLASSERT(pThis != NULL);

         pThis->m_hWnd = hWnd;

         pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);

         WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);

         WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);

#ifdef _DEBUG

         // check if somebody has subclassed us already since we discard it

         if(pOldProc != StartWindowProc)

                   ATLTRACE2(atlTraceWindowing, 0, _T(“Subclassing through a hook discarded./n”));

#else

         pOldProc;  // avoid unused warning

#endif

         return pProc(hWnd, uMsg, wParam, lParam);

}

 

它先由全局变量_Module中取得此窗口对应的窗口类的this指针,然后通过m_thunk运用汇编指令改造此窗口类的窗口过程成员函数。m_thunkCWndProcThunk的实例,每个窗口类各有一个。它的定义如下:

 

class CWndProcThunk

{

public:

         union

         {

                   _AtlCreateWndData cd;

                   _WndProcThunk thunk;

         };

         void Init(WNDPROC proc, void* pThis)

         {

#if defined (_M_IX86)

                   thunk.m_mov = 0x042444C7;  //C7 44 24 0C

                   thunk.m_this = (DWORD)pThis;

                   thunk.m_jmp = 0xe9;

                   thunk.m_relproc = (int)proc – ((int)this+sizeof(_WndProcThunk));

#elif defined (_M_ALPHA)

                   thunk.ldah_at = (0x279f0000 | HIWORD(proc)) + (LOWORD(proc)>>15);

                   thunk.ldah_a0 = (0x261f0000 | HIWORD(pThis)) + (LOWORD(pThis)>>15);

                   thunk.lda_at = 0x239c0000 | LOWORD(proc);

                   thunk.lda_a0 = 0x22100000 | LOWORD(pThis);

                   thunk.jmp = 0x6bfc0000;

#endif

                   // write block from data cache and

                   //  flush from instruction cache

                   FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));

         }

};

 

Init()函数完成对WndProcThunk结构的初始化工作。WndProcThunk结构针对X86体系的定义如下:

 

struct _WndProcThunk

{

         DWORD   m_mov;          // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)

         DWORD   m_this;         //

         BYTE    m_jmp;          // jmp WndProc

         DWORD   m_relproc;      // relative jmp

};

 

结构成员中保存的是一组汇编指令。在X86体系下,在Init()函数中这组汇编指令被初始化为下面的指令:

 

mov dword ptr [esp+0x4], pThis

jmp (int)proc – ((int)this+sizeof(_WndProcThunk))

 

它完成的功能是,用窗口类的指针pThis代替窗口句柄hWndesp+0x4中放的就是hWnd),然后跳转到传入的proc函数处((int)proc – ((int)this+sizeof(_WndProcThunk))procthunk之间的距离)。

 

在调用完m_thunk.Init()函数之后,实际上就得到了经过改造的窗口过程(m_thunk.thunk),此窗口过程是窗口类的一个成员函数,它的第一个参数定义虽然是HWND,但实际上是它的类的this指针。最后使用SetWindowLong()用这个新的窗口过程替换掉原有的窗口过程(也就是StartWindowProc),以后的所有消息都会被路由给新的窗口过程。

深入.NET托管堆(managed heap)(下)

在这里,对象可以通过两种方式被清除。第一种方式是通过IDisposable接口的Dispose方法。此方法在对象显式地结束时被客户代码调用,它调用InternalDispose(true)。在这种情况下所有的对象都被清除了。如果析构函数被调用,那么InternalDispose(false)被调用,此时只有外部资源会被释放。如果我们已经执行了终止操作,那么我们自己的对象有可能已经被释放了,此后对它们的引用有可能引起异常。

 

GC.SuppressFinalize的调用会阻止垃圾收集器将对象放入终止队列中。这样做可以降低在一次GC过程中由于整理对象而引起的内存消耗,并且由于终止操作不会被调用,从而使性能得到提高。

 

C#的优化

 

因此使用IDisposable.Dispose()来释放资源是个很好的方式,它不但可以减少一些在托管堆上进行操作的内存需求,而且能够减少必须执行终止操作的对象的数量。但是它使用起来比较麻烦,尤其是有多个临时对象被创建的时候更是如此。为了能够从IDisposable接口受益,C#客户程序应该书写象下面这样的代码:

 

OverdueBookLocator bookLocator = null;

try

{

    bookLocator = new OverdueBookLocator();

    // Use bookLocator here

    Book book = bookLocator.Find(“Eiffel, the Language”);

    .

    .

    .

}

finally

{

    if(bookLocator != null)

    {

        IDisposable disp = bookLocator as IDisposable;

        disp.Dispose();

    }

}

 

finally中的代码被用来在有异常发生时作适当的清理工作。为了C#客户程序能够简单有效地使用Dispose模式,Beta2引入了using表达式。Using表达式允许你简化你的代码,因此上面的代码可以写成:

 

 

using(bookLocator = new OverdueBookLocator())

{

   // Use bookLocator here

   Book book = bookLocator.Find(“Eiffel, the Language”);

}

 

无论何时分配具有明确定义的生存期的类型时,你都应该使用using表达式。它能保证对IDisposable接口的适当调用,即使是在有异常发生的时候。

 

使用System.GC

 

System.GC类用来访问被.NET framework暴露出来的垃圾回收机制。这个类包含以下一些有用的方法:

 

     GC.SuppressFinalize 这个方法在前面已经描述过了,它能够抑制终止操作。如果你已经将属于一个对象的外部资源释放了,调用这个方法来抑制此对象的终止操作的执行。

     GC.Collect 具有两个版本。不带参数的版本在托管堆的所有generation上执行回收动作。另一个版本带有一个整型参数,此参数指明所要进行回收操作的generation。你将很少调用这个方法,因为垃圾收集器在需要的时候会自动调用它。

     GC.GetGeneration 返回作为参数传入的对象所在的generation。这个方法在由于性能的原因而进行的调试和跟踪中很有作用,但是在大部分应用中作用有限。

     GC.GetTotalMemory 返回堆中已经被分配的内存总量。由于托管堆的工作方式,这个数字并不精确,但是如果你以true作为参数的话,还是会得到一个比较接近的近似值。这个方法在计算之前会先执行一遍回收操作。

 

下面是使用这些方法的一个例子:

 

/// <summary>

/// Displays current GC information

/// </summary>

/// <param name=”generation”>The generation to collect</param>

/// <param name=”waitForGC”>Run GC before calculating usage?</param>

public void CollectAndAudit(int generation, bool waitForGC)

{

  int myGeneration = GC.GetGeneration(this);

  long totalMemory = GC.GetTotalMemory(waitForGC);

  Console.WriteLine(“I am in generation {0}.”, myGeneration);

  Console.WriteLine(“Memory before collection {0}.”, totalMemory);

  GC.Collect(generation);

  Console.WriteLine(“Memory after collection {0}.”, totalMemory);

}

 

关于本文作者

 

Mickey WilliamsCodev Technologies的创始人之一。Codev Technologies是一家从事位Windows程序开发者提供咨询和工具的机构。他同时也是.NET Experts (http://www.codeguru.com/columns/DotNet/www.dotnetexperts.com)的主要成员,他在此讲授.NET Framework的课程。他时常在美国和欧洲的一些研讨会上发表演讲,并且已经写了八本有关Windows程序设计方面的著作。他目前正被微软出版社邀请写作“Microsoft Visual C#”。你可以在mw@codevtech.com找到他。