xlWarKey v2.0 手记
经过一周多的断断续续的奋战,终于赶在除夕夜把 xlWarKey v2.0 发布了。现在把一些心得体会记一下。
从去年 5 月 21 发布 1.2 版以来,到现在已经半年多了,由于上学期忙于找工作等一堆乱七八糟的事,一直没去更新它。但期间得到的反馈还是蛮多的。主要有两方面的,一是 Vista + 浩方 下改键无效的问题,而是游戏里聊天打字受影响以及切出游戏切回来显血失效的问题。第二个显血失效问题好解决,只是有感于人心的不足——从一开始没有工具,到有简陋的工具,到对工具的不满,到工具的进化,然后继续不满……呵呵,这也是人类社会发展的动力吧,呵呵,扯远了。
等到期末考考完了,考研也考完了,才开始想这件事了。这才发现,有些工具(比如 JiaJia 的 WarHelper)已经有一键改三键的功能了,U9 也推出了自己的魔兽辅助工具。不过它们走的都是集成路线,用户群的影响应该不算非常大。可是我不得不考虑要不要加入一对多的功能。问了几个室友的意见,其中包括当初提出多方案的那个室友,都说要。这样的话,配置文件格式又要重新设计过——我很不乐意重新设计文件格式,因为这显得我之前眼光不够长远,留的余地不多。不过现实的需求摆在我面前,只好干吧^_^
感谢之前的几个网上的朋友,QQ 的和 Email 的,他们告诉了我一些 MapHack 界的常用方法,比如 Cheat Engine 这个工具。我这才知道,原来 MH 并不是想象中的遥不可及,我原来也能做到的,呵呵。用 CE 跟踪了一下,1.22 版的有关聊天状态变量的地址是 6FABDFE0h,1.20e 以及 1.21 的都是 0045CB8Ch,1.20 的 a、b、c、d 就没去看了,既然估计是不变的。
准备到这一步,就着手开始搞了。首先是数据结构上的设计。原来的版本,一个方案就对应一张表 keyMap[256],按下一个键,得到 vkCode,再去找 keyMap[vkCode],如果是 0,就是没改过,return CallNextHookEx(),非零的话,keyMap[vkCode] 存的就是需要模拟的键的 vkCode,模拟之。多个方案以动态数组的形式存于内存中。现在要一对多,也就是 keyMap[vkCode] 也要对应一个表,一个个模拟下去。于是这数据结构就有点复杂了:
一个配置文件包含多个方案,每个方案包含多个改键项,一个改键项中原始键还要对应多个键。
我讨厌这么多级的分类,有点不想弄这个一对多了,心里琢磨着不如把别的问题解决了,叫 1.3 好了。
然后是用什么写的问题。前面 1.0 用的是 VS2005,Win32 SDK;1.1、1.2 用的是 VS2008,SDK。到 1.2 的时候,纯 API 就已经有点头大了,界面上有 6 个框都是要按下键显示键名的,可是我不得不写 6 个很相似的回调函数。现在,几个选择是:1、继续 SDK 路线;2、MFC;3、.Net;4、VB;5、试试看 Delphi 啥的。5 首先枪毙了,我对 pascal 一点都不熟,现学现用来不及了,何况...也不想学 pascal,谁叫它老跟什么学科竞赛扯在一起呢,呵呵。1 么有点不想了。4 也不合适,要用这么多 API,单单 VB 声明堆在一起就够恐怖了。剩下的就是 2 和 3 了。其实挺想 3 的,界面设计太舒服了,可是不能放弃广大 XP 用户是不,玩游戏的,谁有这心思去装个 .Net Framework 呢!还有就是,一边 .Net,一边玩键盘钩子,这局面似乎有点不和谐的——高端到这么高端,底层到这么底层。剩下的只有 MFC 了。看了下操作系统自带 MFC 库的情况,XP 带了 mfc42.dll,Vista 居然也只带到 mfc42,这意味着 VS2002、2003、2005、2008 的 MFC 都不能用了,要用的话,静态编译进去,要 1.5M,一个 100K 的软件变成 1.5M,总大小不大但是比例不调。于是最后选择了被许多人鄙视,也被我自己不喜欢的 VC6。这么选择来选择去的结论居然是——MFC 还是王道啊,VC6 还是王道啊!
问题又来了,用了 MFC,再用个 std::wstring 或者 std::string,居然报错,而且很莫名其妙。查来查去查不出原因,网络上也找了,也没有定论,不过找到了句“建议不要 MFC 和 STL 混用”。那么好吧,CString 吧,std::list 也不要了,CList 之。
嗯,既然支持一键对多键了,那么把附带的问题都解决了吧——诸葛的下雨下雪下冰雹。C、E 之后要等一会儿才能按 X。我要加个延时功能。C++ 中面向对象的实践我搞得并不多,原先只在 .Net 中玩玩那些。由于实现多态的时候,只能手动用指针去搞,然后把这个指针存到链表里,单独使用这个链表的时候,内存管理很麻烦,以至于有一天花了两个小时才找到了一处内存泄漏。幸好这种单独使用的情形不多,在上一层数据结构中马上把它包装了。原来玩那些教学上的例子的时候,只是觉得程序里遍布着 new、delete 并且不出错心里会感到很舒服,但实际上用起来马上感到麻烦了。Java 和 .Net 那么做确实很人性化。另外一个就是对象复制的问题,起先用了 dynamic_cast,后来发现 dynamic_cast 貌似会出错(这个再去研究研究,以后细谈),只好继续想办法,最后看到有篇文章里介绍的虚克隆函数挺不错的,问题得到圆满解决。
下一个问题是延时和低级键盘钩子的冲突。低级键盘钩子允许的处理时间有限,测试了下,约在 300ms 左右,超时了系统会自动把消息传给下一个钩子。而诸葛的 CEX 中,E、X 间延时 250ms 就够了,但是 CRX、CWX 不够。再,既然放了个延时功能在,仅仅能延两三百毫秒,意义似乎不大,一两秒总要能支持的吧。换种思路,每次执行按键的时候,创建新线程,在新线程里面去延时。一开始我傻了,每个按键一个线程,延时也一个线程,这样哪能起到延时作用呀?不过让我发现了,系统创建新线程速度是相当有限的,稍微按快点就跟不上了。睡了一觉起来以后我醒了,开一个新线程,设缓冲队列,让新线程不断地去扫描缓冲队列。效果似乎还可以。因为这个新线程没有涉及到界面,所以在这里我还没体会到多线程编程的难点,到后来在写一个小彩蛋的时候我终于体会到了,而且解决不了,最终只好用 Timer 代替了,当然那里就暂时抛开了 MFC 了,谁叫 this 指针没法传到回调函数里面去呢。怪不得笔试啊面试啊,经常会来一些多线程的东西。
有人反映切换方案的时候应该给出提示显示方案名称。我想是不是可以像对战平台那样在魔兽窗口显示呢?后来了解到这要拿一个 dll 植入 war3.exe 进程去 hook DirectX 的相关东西。而我刚发现低级键盘钩子可以不用在 DLL 里面,乐呵呵地做成了单文件了,这回又要变成多文件,我不干。所以只好利用聊天框来提示了(还可以附带宣传下,呵呵)。魔兽的字符串都是 UTF8 的,趁写 Unicode2UTF8 的机会了解了 UTF8 格式的定义。细细想来,这次学到的东东还蛮多的哈。
后来的一些过程都没有碰到什么大的问题了。最后装了个 Vista 来测试。原先一些网友反映的 Vista + 浩方 下不能用的问题,我一直不知道原因,因为我自己从来可以用。直到那一天我才有一点点明白。原来我自己测试的时候都是用管理员用户,并且关闭 UAC 的——这种情况下一点问题也没有。但是在标准用户下,会有些问题。一个是没法提升权限,因为这个版本要读其他进程的内存,所以设计到这个。再一个,注意到浩方运行时会触发 UAC 的,也就是说浩方运行的时候得到了管理员权限,我那原来的版本来测试,果然不能用了,可能低级键盘钩子因为什么原因被限制了。VS 是不会触发 UAC 的,在 VS 下老版本可以用。于是我让新的 xlWarKey 也去触发 UAC 了,这样就可以在浩方下用了。因为 XP 下以及管理员模式的 Vista 下,浩方平台和 xlWayKey 都不冲突,所以应该不是浩方有意去屏蔽相关 API 的吧。其中没有研究透的问题,有待继续研究。
这两天就再看下载量,第一个 24 小时,第二个 24 小时……到现在已经 41 小时了,下载量 9400 多了,平均每分钟 3.8 次,不错了呵呵。又去下载看了下 JiaJia 的 WarHelper 新版,个人觉得商业色彩稍微有点多了。看了下,它好像是用 VB6 写的,继承这么多功能在一起很不容易啊,如果是我怕会搞混掉。很佩服作者的推广能力,网站也搞得听炫目的(不过看到“所获奖项”这些字,我也有点想笑,呵呵),去作者的博客留了一句言“xlWarKey 发来贺电,很佩服作者的宣传推广能力”,今天去看,“xlWarKey”这几个字变成“********”了,心胸问题,呵呵。原来还以为可以相互问个好,交个朋友呢。现在呢,就默默地祝它走好吧。当然我自己的也要走好。在可预见的将来我都没有在这个软件里加入商业元素的想法,要加只加在网站上。
码完了,这么多字不容易啊~~~~
(原发表于 CSDN:https://blog.csdn.net/cnStreamlet/article/details/3854430)