« June 2006 | Main | August 2006 »

July 30, 2006

手机收不到短信了

最近一年用的索爱 P910c ,短信收的越多,速度越慢。隔一段时间我就删除上千条短信,并把一些归档,让手机处理的可以更流畅一些。

今天发现一件事,我的手机从 7 月 14 日开始,就没有再收到任何一条短信,包括广告信息。怪不得最近老有人说为啥不回短信。

暂时还不能确认是移动的问题,还是我手机的问题。猜想更大的可能是手机问题吧。这手机软件写的还真不是一般的滥呢。

不过我的梦想终于快实现了——总有一天我可以扔掉手机的,最近真是清净啊 :)

July 27, 2006

用 lua 调用 Windows 的 API

昨天同事谈起能否给一个从 lua 中调用 Windows API 的简单方案。一开始觉得,如果是一个通用方案,那么至少需要先给出一个类似 windows.h 的原型声明,然后从 lua 来解析这些原型。大约写了几十行程序就实现了。后来又想了一下,似乎可以用一个更简单的方式,绕过原型,更简洁(但不保证安全)的方法来做到。

其间的问题就只有一个,每个 api 的参数都不一样,如何自动生成 C 中匹配的函数指针。似乎 C++ 的 template 是一个正统的解决方案。不过思考过几分钟以后,就被我否决了。实际用到的解决方案比较诡异:

先用 alloca 分配出正确的参数空间,再立刻填充这些参数,接下来以无参数的形式调用 api 。这样做,对于 __stdcall 的函数是没有问题的。好在 api 大多也是这样。

我写了这样一段程序来验证我的想法:

#include #include #include typedef void (__stdcall *func_call)(); void __stdcall foo(int a,int b) { printf("%d,%d\n",a,b); } void check(void *arg) { assert((void**)arg-&arg==1); } void test() { int *arg=(int*)_alloca(2*sizeof(int)); arg[0]=1; arg[1]=2; check(arg); ((func_call)foo)(); } void main() { test(); }

这个方法唯一的漏洞,可能存在于 _alloca 并不能正确的分配出需要的空间。因为由于某些(对齐?)因素,我们不能保证分配出来的空间正好符合后面的函数调用需要的位置。个人感觉,这个问题在大多数编译器上不会出现。不过安全起见,我写了个 check 函数运行时检查。

用这个程序验证无误后,就写了个简单的 lua 扩展。使用起来大约是这样的:

opendll = require("api.opendll")

getprocaddress =require("api.procaddress")

user32=opendll("user32.dll")

MessageBox=getprocaddress(user32,"MessageBoxA")

MessageBox(nil,"hello","test",0)

有点意思 :) 另外我还测试了 FindWindow , ShowWindow 等,都工作的很正常。

这个方案初步解决了 dll 中 api 的调用问题,但还并不实用。比如我们需要写一套 dll 管理模块(直接用 lua 完成即可)。更重要的是需要解决 api 调用中无处不在的 C struct 的传递问题。这个问题又分两类,一类是作为输入参数的 struct ,一类是作为输出参数的 struct 如 (GetWindowRect) 。我们可以用 lua 的 table 去模拟 struct 。作为输入参数做 lua table 到 c struct 的转换;而作为输出参数则做 c struct 到 lua table 的回转。或者干脆用 userdata 直接映射 struct ,再用 metable 去读写之。

另一个需要解决的问题是,有些 api 为了返回多个参数,以传入指针的形式接收返回值。lua 里是没有指针的概念的。简单的解决方法是统一用 struct 的方式解决,把单一指针看成是一个只有一个成员的 struct 指针。

因为做这个东西纯属娱乐,目前项目中并不会用到,所以我也就没有继续深入下去了。

July 22, 2006

糟糕的 DELL 鼠标

用 DELL 的产品好几年了,不是我喜欢,只是公司统一采购的而已。以前一些小配件还可以自己买,最近一年全改 DELL 的了。

几天前,我的 dell 鼠标就不好使了。拖拽东西老是中途掉下来,让我苦恼不已。隔壁的同事说他一直就受不了 dell 鼠标,自己另买了一个,所以他的机器配的那个还是新的,也就给了我用。想想也是,dell 的东西口碑一直很差,再说我这鼠标也用了快 1年了,出点问题就换个新的算了。

没想到新鼠标没用几天,opera 里的鼠标手势就老是失败。用 spy 查了一下,原来每次我按住鼠标右键的同时,窗口都会伴随着接受到一个 WM_RBUTTONUP 消息。感情是,坏掉的前任我还扔在旁边,那毛病传染到新鼠标的右键上了。

另外,这批鼠标还有一个共同的毛病,用着好好的,鼠标光标会突然飞到屏幕一角。问过 IT 部门的同事,似乎不是我这一批特有的问题,只是打过 dell 的电话,那边死不承认质量问题,光是推脱说是鼠标垫不好罢了。我这还是 dell 原配的鼠标垫呢 (._.!)

July 21, 2006

一部值得看的电影

昨天跟同事一起看完了《疯狂的石头》,笑声不断。上次这么开心的看电影是《无极》,不过笑的有些不同罢了。

没看过的推荐看看,很有点意思。几次都让我联想到一个笑话——恐怖分子在广州的不幸遭遇。 最后那个摩托车劫匪撞上车门不幸的镜头,似乎我的一个同事亲眼在大街上见过。不过那一次,劫匪爬起来拍拍屁股跑掉了,连车都没兴趣去拣,身体还真的是强悍啊。

有人说是抄袭的作品,但是那又怎么样呢?我不想分辨所谓“抄袭”的真伪,那些问题让那些喜欢研究电影的人去从中找到快感吧,我只需要从观看电影的过程中得到愉悦。

网易泡泡的一个问题

一直以来,我都很头痛 popo 的一个设定。就是明明我的默认浏览器是 opera , 在 popo 的编辑框内点击 url 还是会启动 IE 。

popo 组的人似乎埋头做他们的 popo 2006 ,对于我们这些 popo2004 用户不太理睬。我提了几次意见,他们都不想真的去解决。除了 IE ,popo 组的人只关心 firefox 能不能用,这让我这个 opera fans 很伤心,只好自己动手解决了。

先反编译 popo.exe ,找了一下。发现了一段冒似处理 URL 点击的代码,但是打了 patch 后不解决问题。后来才醒悟过来那段代码是处理 popo 界面上的 url 的。又 grep 了一下那一堆 dll ,感觉编辑界面是由 ExtraEditor.dll 负责的。然后在这个 dll 里找到了一段和 popo.exe 中 2 进制几乎相同的代码。

这个比较寒,我个人是很反对 copy - paste 代码的,那反应的是某种设计失误。不同的是 ExtraEditor.dll 这个东东里面还有 pdb 信息,似乎开发者不太放心,放了个 debug 版出来 (._.!) 。这让我得到了关键函数的名字:

CExtraEdWnd::IsUsingIEAsHttpBrowser

看名字,这个函数好象用来检测系统默认浏览器是否是 IE 。分析了下实现,是通过读去注册表项和其它一些手段实现的。其方法不太专业,也没细看。反正我的机器明明设置的 opera 做默认浏览器,但是这个函数依然认为我偏好 IE 。

一旦认为系统设置的是 IE 做默认浏览器,popo 会用 ShellExecute 强制调用 iexplore.exe 打开 url 。而如果不是 IE 则不填写浏览器的名字,这样就不会强制用 IE 了。

就这个奇怪的设定,我问了 popo 组的同事。他们的说法是,如果 ShellExecute 直接打开 url 的话,当用户机器默认浏览器用的是 IE ,那么新的页面会在用户已经打开的 IE 窗口中打开,把用户已经在浏览的页面覆盖掉。而指定 iexplore.exe 就不会。

我强烈 BS 这种头痛医头的的解决方案。不过话虽如此,我也没想用啥更好的方法来做,反正我也不用 IE ,所以就把这个奇怪的设定跳过去,禁止 popo 强迫我使用 IE 打开网页。

方法很简单,把 ExtraEditor.dll 中偏移量为 0x00048FBE 地方的 0xEB 改成 0x74 就可以了 :)

ps. 我查了一下 IE 的设置,发现是否重复使用当前打开的窗口其实是由用户自己决定的。在 Internet 选项—高级—(倒数第3项)重新使用启动快截方式的窗口 。奇怪的是,似乎大多数人都希望可以启动一个新的窗口打开 ShellExecute 引导的 url ,但是这个选项却是默认勾选上淹没在茫茫的 option 菜单海洋中。

July 13, 2006

读了一篇文章

偶然的机会,读到这样一篇文章:清华梦的粉碎—写给清华大学的退学申请

这让我想起当年自己做毕业设计的日子。当然,我那个是本科论文。不过,学校里许多老师的做学问的态度 ......

ps. 最近在读周培德教授的《计算几何——算法设计与分析》,是本好书,值得推荐。

July 08, 2006

A* 算法之误区

估计是因为我在上大学时在网上留过一篇文章,里面有一个简单的使用 A* 算法寻路的源程序,导致了这近七年来,不段的收到 email 和我讨论这个算法。

对,毫不夸张。那个简陋的代码。在七年前我随手写出来扔在网上后,已经遍布中文网络世界的犄角旮旯,让我在痛恨那段代码的时候,想收回都不可能。

我最大的困惑在于,为什么课堂上最为基础的算法知识,却有如此多的人行入误区。难道写游戏的程序员都不去读读基本的课本?

A* ,读作 A-Star 。它仅仅是一个启发式搜索算法。就是说在一个可以被穷举的有限解空间集中,用比较有效的方法(主要是利用估价函数)求出最优解的算法。A* 跟寻路算法没有太多关系,我们只是借助了这个方法来解决寻路这个问题,正如四则运算对于数学题一样,它只是一个基本工具。寻路算法本身有很多种,我们找到算法后,借助 A* 这个工具实现这个算法而已。

我的那篇文章中提到的,也是传统意义上的地图寻路,其实依据的是这样一种算法:把地图分成若干个格子,把起始点的格子上标作 0 。然后根据将周围一圈可以通畅的格子上标为1。然后再把所有标上 1 的格子周围可以通达的格子标为 2 ,当然,如果那些格子上已经有过数字了(一定比 2 小)就不用标了。

如此反复迭代下去,我们地图上的终点只要可以通达,就一定会被标上数字。而这个数字就是理论最短的距离,而标记过的每个格子都有一个前导的入口(即它由附近一个比它小 1 的格子引导过来)整个标记的过程逆推,也就找到了最短路径。

这种一步步尝试的过程,就是一种搜索过程。如果加上启发函数,不让它盲目的寻找,就衍生出很多启发式搜索算法。A* 是其中的一种。之所以加一个 * 号,是因为它的启发式函数是有限制的,这个限制确保它能找到绝对最优解,去掉这个限制,就是 A 算法了。

我们可以看到,把地图分格子,给格子间的路径一个权值(前面的例子中,格子间的距离都是相等的,我们也可以根据地形划分成不等的距离,即权值,或者定义单向道路,这都是可以的),这是解决寻路问题的关键,但都不是 A* 算法的范畴。

如果你想出某种算法,比如把地图划分成不规则的区域,或者把地图矢量化。依然可以再此基础上用 A* 算法这个工具来从中求到最优解。如果想别的方法来寻路,比如拟人的判断,预设路点等等,甚至按走迷宫的方法一样,分叉右转,碰壁回头,那些可以算是对分格寻路方法的一些改进,却都不关 A* 什么事情。

A* 算法理论上 是时间最优的 可以得到最优解,不过我们可以通过选择一个更好的估价函数,或是减少解空间来提高性能。 A* 算法最大的缺点就是,空间需求太大。我们可以用一些时间换空间的方法改进。但是如果不存在解(比如在寻路问题中,根本不存在一条通达的路),采用 A* 算法求解,势必会穷举所有的可能。所以一般在游戏里,我们一般会采用额外的手法避免这个问题。

ps. 如果用 waypoint 的方法来解决寻路问题,实际上将地图化成了一个图,经典的最短路径算法是 dijkstra 算法。若是把地图的阻挡物用凸多边形描述,有一道 10 年前的 ACM 赛题可以参考:Cutting Corners