虫子樱桃 发表于 2010-10-6 16:19:06

你DllCall了吗?

本帖最后由 虫子樱桃 于 2010-10-6 19:15 编辑

首先,申明下,关于这个DllCall的内容不是我写的,我是参考Andreas Karlsson 2009 |《Dealing with Dlls in AutoIt 》来半翻译半写的,英文不好,所以会很生涩,请大家莫怪!


我的第一个Dllcall()
我们首先来实现sleep()这个函数,这个功能驻留在Kernel32.dll这个文件中。您可以使用autoit中的sleep()来实现类似的功能。实质上,autoit仅仅是您和这个函数之间的一个中介而已。该函数的MSDN文档http://msdn.microsoft.com/en-us/library/ms686298(VS.85).aspx 查看该函数的文档的时候,您会发现这个函数仅有一个参数,并且没有返回值,接下来,让我们使用我们今天的主角函数--dllcall来实现同autoit中sleep()相同的功能!
DllCall ( "dll", "return type", "function" [, "type1", param1 [, "type n", param n]] )
让我们仔细来看它的参数,首先是“dll”这个参数,这是我们所要运用的函数功能就存储在这个dll文件中。之前我们说起,sleep这个功能函数存储在Kernel32.dll这个文件中,所以这里,我们将Kernel32.dll放在“dll”这个参数的位置。   
其次,是返回值,该函数功能在MSDN文档中,没有返回值,所以,在“return type”这里,我们赋值为“none”。接下来的就不是太难了,我们知道我们要实现的函数功能的名字为sleep,所以,这里将其赋值为sleep。
   紧接着,让我们来看看参数,正如您所见,现在亟需做的事情,是指定参数的类型。在MSDN中,这被称之为“DWORD”,而在Autoit中,则简单的叫做“dword”。所以,在type1这个参数这里,我们设置其为“dword”。参数的类型就这样被指定了,然后通过“param1”这个参数,您可以指定您sleep时间长短的值。现在在DllCall()里面已经没有其他的参数了,但是如果这个sleep函数有更多的参数,您就应当重复使用。DllCall提供可选量的参数,这样对于用户来说,确为一件很酷的事。   如果函数有返回值,那么返回值将储存于一个数组内,在这个数组中以0为下标(例如arry)的数据就是从dll中返回的数据。
希望您现在已经有自己得意的代码了,即使您现在还没有也不必泄气!; Sleeps for 1 second
   DllCall("Kernel32.dll","none","Sleep","dword",1000) 希望你现在明白了这个函数用法!在结束这一小节内容之前,请您查阅相关资料,实现以下函数功能。LockWorkStation ------提示:BOOL是一个整型(int),当没有参数的时候,您可以完全跳过DllCall的参数选择。
GetCurrentProcessId ----提示:返回值是作为数组返回的,数组中下标为0的,即是返回值。



DllCall中的字符串
正如您所知,在Dll文件中的字符串实质上是阵列字符,幸运的是Autoit帮助我们解决了这个问题,您所要做的,只是选择"str"还是"Wstr",然后在某个参数位置输入您的字符串。另外有一件很重要的事情要提醒您的是在win2000系统出现之前windows系统并不完全支持Unicode,几乎所有的函数在处理字符串的时候都是以两种编码来处理的。这两种编码通常在它们的名字末尾有一个"A"或者"W",譬如这个“MyStringFunctionA”。一般的,"A"代表 ANSI编码 ,"W"代表宽字符,即Unicode编码。现在让我们来看一下这节要用的dll中的函数 MessageBox,,当然在autoit中也有收录。正如您所看见的那样,这个函数有很多不同的可以供您设定的标志值,但是很遗憾的是MSDN只指定了它的符号名城,这就意味着您所看到的不同的名称实际上只是一个数字而已。如果您想找到对应值,建议您在谷歌搜索下这个名称或者查询一个winAPi语言的扩展版本,如针对于C/C++的windows.h。   
这里我们使用0来作为“uType”的值。下面的是这节要用到的源码,请认真研读,然后作适当改写。例如,试着将uType的值改为16.; 我们写成MessageBoxW是因为我们要用unicode编码,这样意味着我们只能使用wstr
; hwnd的值设置为0是因为没有和messagebox句柄关联的窗口。
DllCall("user32.dll","int","MessageBoxW","hwnd",0,"wstr","我爱AU3!","wstr","Info","uint",0) 这就是这节的内容,认真揣摩以上代码,并完成以下内容!
将以上代码改写成ASCII版本
执行FindWindow



参数传递
如果您曾经在一个函数声明中见到过Autoit中的“ByRef”关键字,您就会明白这是怎么一回事。简单来说,就是您发送一个值将值传递给Dll文件,Dll文件为您对值进行一些处理。这对于Dll通过返回值来表明它执行成功但仍要返回值给其调用者的这种情况来说,是很实用的。在这节中,我们要用到的是windows中的一个示例函数GetDiskFreeSpace. 如果要传递一个参数作为参考值,只需要在参数类型那里加上一个“*”即可。
以下是函数实现的过程:; 变量作为参数传递
Local $SectorsPerCluster
Local $BytesPerSector
Local $NumberOfFreeClusters
Local $TotalNumberOfClusters
$calldata=DllCall("Kernel32.dll","int","GetDiskFreeSpaceW","wstr","C:\","dword*",$SectorsPerCluster,"dword*",$BytesPerSector,"dword*",$NumberOfFreeClusters,"dword*",$TotalNumberOfClusters)
; 即使变量的值被改变,返回值仍然以数组形式被返回
$SectorsPerCluster=$calldata
$BytesPerSector=$calldata
$NumberOfFreeClusters=$calldata
$TotalNumberOfClusters=$calldata
MsgBox(0,"","Total number of clusters: "&$TotalNumberOfClusters) 正如您所见到的那样,所有的数据都存在于数组中,并且变量没有完全被改变。如果您想要完全改变数组中变量的值,您就必须重赋新值用以覆盖掉原值。以上就是这一章节的所有内容,我找不到任何适当的可以用来供练习的题目,所以请务必确保您非常明白这一章节的内容!



与调用规则相关的一个词
说到Dllcall就不得不提一下“调用规则”。调用规则是用来规定函数的调用如何被执行。在windows中,所有Dll文件都遵循stdcall调用规则,在Autoit中它同样也是作为一个标准模式来使用的。然而一个叫“cdecl”的调用规则的使用也十分普遍.。而且在Autoit中使用它十分简单,只需将“cdecl”放在返回类型之后即可。所以; With stdcall
DllCall(”SomeDll.dll”,”int”,”Func”) 可以变为; With cdecl
DllCall(”SomeDll.dll”,”int:cdecl”,”Func”) DllStructs——极为难的一个东东
下面我们开始学习真正需要很好了解的,特别实用的东西——DllStructs,,那么什么是DllStructs,呢?请把它们想象成内存中一个个用来装数据的包,在这些包里面装着紧紧挨着彼此变量数据。   那么我们为什么需要这些“包”呢?难道我们不能把所有的数据以参数形式传递给函数?在某些情况下,我们是可以那样做的,但是在大多数情况下,这是一种费力不讨好的做法。因为在某些时候变量的数量可能会发生变化,这些变化会将您的代码变得冗长而复杂。同样地,如果一个函数需要返回很多值,使用“包”也是很有必要的。因为使用“包”之后,可以返回指针(内存地址)到一个(dll)包含很多变量(或者元素,因为从现在开始我将把他们看作是表格一样的东西)的结构中去。另外一种方法就是将指针作为一个参数传递到(DLL)结构中,并让其处理其指向的(DLL)结构。这样做的另一个好处就是能够使Autoit进行直接的内存存取。也许这不是特别有用,但是当您使用advanced low-level(相对的LOV级)代码的时候,您会发现没有它,Autoit会被削弱很多。作为第一个例子,请参看 GetSystemTime 这个函数,这个函数是一个比较简单的函数,它只有一个参数。并且这个参数是一个指向SYSTEMTIME结构的指针。如果您想调用这个结构,您就必须将将其用一个Autoit的DllStruct表示出来。请点击链接查看SYSTEMTIME,的相关文档,其结构如下: typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;您最先注意到的,可能是WORD这种类型,这种类型在Autoit中是没有被指定的,所以您必须得弄明白微软用WORD所表达的实质意思。通过谷歌搜索“WORD typedef”发现,原来WORD在Autoit中实际上是一个无符号的又名“ushort”的16位整数(请查阅Dllcall函数帮助文件的相关项)。因此,该结构使用Autoit的完整表达(和简单的调用)如下:$SYSTEMTIME=DllStructCreate("ushort wYear;ushort wMonth;ushort wDayOfWeek;ushort
wDay;ushort wHour;ushort wMinute;ushort wSecond;ushort wMilliseconds")
DllCall("Kernel32.dll","none","GetSystemTime","ptr",DllStructGetPtr($SYSTEMTIME))
MsgBox(0,"Current
time:",DllStructGetData($SYSTEMTIME,"wHour")&":"&DllStructGetData($SYSTEMTIME,"wMinut
e")) 当您觉得您已经理解上面代码的含义的时候,请多加练习,并发觉其中新的。试着做下面的练习: 使用 SetLocalTime 函数将您当前系统的时间改到明天下午的六点,并修改回来。

虫子樱桃 发表于 2010-10-6 19:28:09

没人顶,自己顶!

258958681 发表于 2010-10-6 19:38:41

{:face (382):}看不懂......

zps26 发表于 2010-10-6 19:46:34

本帖最后由 zps26 于 2010-10-6 21:33 编辑

多谢楼主的用心付出,DLLCALL的使用相当重要,正在认真学习这部分内容呢
试验GetCurrentProcessId函数:

$result=DllCall("Kernel32.dll","DWORD","GetCurrentProcessId")
MsgBox(0,"CurrentProcessId",$result)

试验FindWindow函数:

Run("notepad.exe")
Sleep(1000)
$result=DllCall("user32.dll","HWND","FindWindow","str","Notepad","str","无标题 - 记事本")
DllCall("user32.dll","int","MessageBox","HWND",0,"str",$result,"str","用dllcall显示窗口句柄","uint","0x40")
MsgBox(0,"窗口句柄",$result)
MsgBox(0,"窗口类名",$result)
MsgBox(0,"窗口标题",$result)

itljl 发表于 2010-10-6 23:35:04

多谢楼主,非常好的贴.

风行者 发表于 2010-10-7 09:10:21

翻译不错,支持

yeqing880 发表于 2010-11-21 22:34:16

记号。。。。到时来取

shanjunpeng 发表于 2010-11-23 12:46:06

收藏了,,,晚上回来看,,,

liuhaogang 发表于 2010-11-23 14:59:37

初学,还看不懂...

gzh888666 发表于 2010-11-25 00:05:01

的确是比较难懂呀!菜鸟飘过!

131738 发表于 2010-11-25 13:10:58

有意义。。。。支持。。。。

lsqyx528 发表于 2010-11-30 18:21:39

顶一个。。。

tryhi 发表于 2010-12-1 23:11:16

本帖最后由 tryhi 于 2010-12-1 23:13 编辑

这方面完全不懂啊,先收藏

都市浪子666 发表于 2010-12-2 17:32:45

初学,还看不懂...

yarsye 发表于 2010-12-3 09:05:07

不是很懂 帮顶
页: [1] 2 3 4 5 6
查看完整版本: 你DllCall了吗?