文:/ 飞跃作,河许人改
AHK界一直有这么一个故事,讲的是AHK星君和WIN大神之间通过小徒弟dllcall眉来眼去的故事。
第一章
我是一个AHK星君(AHK脚本),居住在九重天上(硬盘),这九重天上由一位超级大神WIN大神(Windows系统)掌管,跟我一样的众神,每天各司其职,维持这乾坤的运转和平衡,我作为效率之神,每天幻化为亿万形态,解救人间低效之苦,深受人间的爱戴。
有一天我感应到强烈的召唤(被双击了),跳了起来说:WIN大神,我要临凡(运行),我要发威,我要表现了!WIN大神微微睁开双眼,“去吧!前途凶险(问题不好解决),受我加持(我这有好多宝贝你带上),方可顺利!”
“不用,老大你放心”
WIN大神没说话,大手一挥,云雾缭绕的九重天不见了,自由落体,好嗨呦!直奔一个蓝色的星球(内存)而去,一瞬间,我已经落地,站在一个写着“中国”的界碑前。
大神的声音响起,“这是你的活动空间(分配的内存),不准乱跑!这里我设了结界,别人也打扰不到你!”地上凭空出现一个方阵,有标准工人(栈)组成。“这些人,听你调遣,应付不了,随时打电话(dllcall)给我!”
说罢,工人扛起我,一路狂奔,直奔呼叫处。
第二章
一边走,一边想,“什么玩意,我一身法力(命令或函数)什么问题解决不了!”,说着,已经到了现场,经过一番苦战,竟然没能拿下,突然,WIN大神的最后一句话在我脑海中响起,CALL他!
于是,我拨了直通老大法宝管理处(WinAPI)的电话,刚一接通,管理处的领导说:“AHK星君啊!老大早料到你会打电话,让你小徒弟(dllcall()函数)过来拉吧!都给你准备好了(Dll文件)。”电话那头挂了。
“shit!”
“小D(dllcall()函数),去把法宝取过来!”
法宝在WIN大神准备好的Dll包裹中(Dll文件),也在某个星球中(硬盘),不一会儿,小D把它拉进地球(内存)的中国中,这是我的地盘,然后通过法宝名字(函数名)找到了法宝的位置,准备使用。
第三章
可是,WIN大神的法宝有点麻烦,要发动它需要给上面每个挂件(参数)都要一句咒语(声明),WIN大神的法宝特别多,挂件也特别多,每一个法宝挂不同的挂件效果还不一样,不过好在对应的咒语不多。
法宝只能通过标准工人(栈)来安装挂件(获取参数),我要把挂件(参数)一个个装入分配给标准工人(栈),每个挂件(参数)都是一个整数。补充一下,WIN大神分两种,一种是32位大神,他生产的标准工人每人能贡献四个单位(4个字节(1字节8位)),另一种是64位大神,他生产的标准工人每人能贡献八个单位(8个字节(1字节8位)),大神的法宝在制作时(编译),已经约定好按标准工人的能力(长度)来设计每个挂件(对齐每个参数),比如在64位大神的统治下(系统中),即使是安装一个1的挂件(传递参数1),它只需要1个单位的贡献,但是一个标准工人的8个贡献能力(8字节共有64位),根本用不完(另外63位要补0凑成64位装到集装箱中,所以输入参数的整数本来并不难,只要把参数的整数补齐到8字节装入集装箱即可。)
第四章
小D揼着法宝说,“每个挂件的都要咒语才能发动”(每个参数都要说明参数类型).
"我就纳闷了,不是约定好了一个标准工人装一个参数吗,干嘛还要念咒语呢(说明类型)?硬要我念得话,全部念最大那个咒语(使用集装箱的长度类型(Ptr))不行吗?"
小D小声说,大神让我给你传个话,大神的法宝调用有下面几种类型:
(1)第一种给它几个地址和小整数它就可以操作,这种法宝没必要叫你念咒语(特别说明类型)。
(2)第二种它需要一个特别大的整数(最大8字节),但是如果我们在32位大神那,每个标准工人只有4能力(字节),我就要把一个挂件(参数)的整数分给两个标准工人去做,这种情况你不是得用咒语(使用参数类型(int64))来喊一嗓子吗?你如果不喊,一个工人拿一半就走,法宝就没效果了(说明参数类型,我默认把这个参数装到一个集装箱中(大整数会截断一部分),把下一个参数装到下一个集装箱),如果你喊一嗓子(,而法宝声明了这个参数是64位的,)他们就知道咋回事了,两人一起搞一个挂件,这样后续的也不会乱了(它会读取连续两个集装箱拼成一个参数(下一个参数在这两个集装箱后面),后续参数不就错位了吗。)
(3)第三种它需要一个小数(浮点数)作为挂件(参数),因为标准工人里面只能装整数,所以小数是一种特殊构造的整数,系统约定好这个特殊整数的前面几位表示尾数部分,后面几位表示以2为底的指数部分,反正构造很复杂,小数1.0构造出的整数形式绝对不同于整数1的形式,如果你不说明这个参数是小数(float是4字节浮点数,double是8字节浮点数),我就不会进行特殊构造,直接取整传递整数,而法宝声明了这个挂件(参数)是小数,它会按照特殊构造来读取解析,不就搞错数字了吗。
(4)第四种它需要一个数据结构,也就是一个班级的座位结构,每个座位是一个字节,按它声明的规定,第一个数坐到第一个座位并占几个字节(霸占后续几个座位),第二个数坐到后面某个座位并占几个字节(霸占后续几个座位)等等,它需要你构造这么一个班级的座位结构,并把第一个座位的地址作为挂件(参数)分配给挂件。这种地址不用你操心挂件(参数)类型,因为地址的整数字节长度刚好等于标准工人的能力。你可以用VarSetCapacity()划分一个空闲的班级,用NumPut()把数字安放到各个座位上去,然后用 & 取班级第一个座位的地址作为参数。等法宝执行完毕后,它可能修改了某个座位上的数字,你可以用NumGet()从班级的某个座位中读取需要的数字。
(5)第五种它需要一个字符串,也就是一队字符小朋友,每个字符是一个编码的整数,它需要队伍的第一个字符的地址作为挂件(参数)装到分配给工人,本来你只要人工用 & 取队伍的第一个字符的首地址作为参数的整数即可,挂件(参数)类型不重要(Ptr即可),反正这个地址的长度刚好是一个工人的能力。但是我还是建议你使用Str类型,因为:
A、字符串有着特殊性,系统中使用的字符串有ANSI和Unicode两种编码方式,法宝到底需要哪一种,需要你自己判断及转换。声明Str参数类型而不是无所谓的类型(int、Ptr),我可以自动帮你转换(利用AStr和WStr类型转为法宝需要的编码)。
B、另外,法宝要求你输入一个地址,用于从这个地址输出一队字符串小朋友时,声明Str类型可以由我帮你自动读取字符串(更新字符串长度),否则要你自己手动读取字符串。(Str自动读取的情况AHK自身的编码与法宝名称指示的编码必须一致)
- 注意:输出字符串必须由你先用VarSetCapacity()划分一个空闲的班级,才允许法宝在这个班级中输出数据,否则法宝输出的数据可能覆盖了你的其他地盘。
(6)第六种本来法宝可以有一个返回值,但是当它需要返回不止一个值时,它需要你在参数中提供一个地址,用来把某个结果数值写入到这个地址,等法宝执行结束后,你可以从这个地址中读取这个值。本来你可以手动用 & 取一个变量的地址作为挂件(参数)让工人去取,如果法宝需要这个地址中有个初值,可以用NumPut()把数字放入这个地址中,等法宝执行结束后,再用NumGet()从这个地址中读取返回值,这种方式参数类型不重要,反正这个地址的长度刚好适合一个标准工人的能力。但是我还是建议你使用类型,因为:使用类型可以由我自动地使用一个临时地址,先把初值写入,最后又把结果读出到你的参数的变量中,完全不用你操心,绝对推荐使用(Ptr*即可)。
好了,明白了小D(DllCall)讲述的这6种情况,你就会恰当的设定参数类型了。再学会:用 & 取变量地址、VarSetCapacity()、NumPut()、NumGet()、StrPut()、StrGet(),和根据A_PtrSize地址长度来计算数据结构的偏移,你就可以炉火纯青地使用DllCall了。