ByVal iReadWrite _
As Long) As Long
在上例中,lopen是VB中使用的过程名称。而_lopen则是动态连接库中可以识别的名 称。
C.使用序号标识DLL过程
除了使用名称之外,还可以使用序号来标识DLL过程。某些动态连接库中不包含过程的名称,在声明它们包含的过程时必须使用序号。同使用名称标识的DLL过程相比,如果使用序号,在最终的应用程序中消耗的内存将比较少,而且速度会快些。但是,一个具体的API的序号 在不同的操作系统中可能是不同的。例如GetWindowsDirectory在Win95下的序号为432,而在WindowsNT4.0下为338。总而言之,如果希望应用程序能够在不同的操作系统下运行,那么最好不要使用序号来标识API过程。如果过程不属于API,或者应用
程序使用的范围很有 限,那么使用序号还是有好处的。
要使用序号来声明DLL过程,Alias子句中的字符串需要包含过程的序号,并在序号的 前面加一个数字标记字符(#)。例如,Windowskernel中的GetWindowsDirectory函数的序 号为432;可以用下面的语句来声明该DLL过程:
Declare Function GetWindowsDirectory Lib "kernel32" _
Alias "#432" (ByVal lpBuffer As String, _
ByVal nSize As Long) As Long
在这里,可以使用任意的合法名称作为过程的名称,VB将用序号在DLL中寻找过程。
为了得到要声明的过程的序号,可以使用Dumpbin.exe等实用工具(Dumpbin.exe是Microsoft VisualC++提供的一个实用工具,它的使用说明可以参见VC的文档)。利用Dumpbin,可以提取出.dll文件中的各种信息,例如DLL中的函数列表,它们的序号以及与代码有关的其它信息。
(三)、使用值或引用传递
在缺省的情况下,VB以引用方式传递所有参数(ByRef)。这意味着并没有传递实际的参 数值,VB只传递了数据的32位地址。另外有许多DLL过程要求参数以值方式传递(ByVal)。这意味着它们需要实际的数据,而不是数据的内存地址。如果过程需要一个传值参数,而传递给它的参数是一个指针,那么由于得到了错误的数据,该过程将不能正确地工作。
要使参数以使用值方式传递,在Declare语句中需要在参数声明的前面加上ByVal关键字。例如InvertRect过程要求第一个参数用传值方式传递,而第二个用引用方式传递:
Declare Function InvertRect Lib "user32" Alias _
"InvertRectA" (ByVal hdc As Long, lpRect As RECT) As Long
动态连接库的参数传递是一个复杂的问题,也是VB中调用动态连接库时最容易出现错误的地方。参数类型或传递方式的声明错误都可能导致应用
程序出现GPF(通用保护错误),甚至使操作系统崩溃,因此我们将在后面专门详细地讨论这个问题。
(四)、灵活的参数类型
某些DLL过程的同一个参数能够接受多种数据类型。如果需要传递多种类型的数据,可 以将参数声明为AsAny,从而取消类型限制。例如,下面的声明中的第三个参数(lpptAsAny) 既可以传递一个POINT结构的数组,也可以传递一个RECT结构:
Declare Function MapWindowPoints Lib "user32" Alias _
"MapWindowPoints" (ByVal hwndFrom As Long, _
ByVal hwndTo As Long, lppt As Any, _
ByVal cPoints As Long) As Long
AsAny子句提供了一定的灵活性,但是,由于它不进行任何的类型检查,风险也随之增 加。因此在使用AsAny子句时,必须仔细检查所有参数的类型。
正确的函数声明是在VB中调用动态连接库的前提,但要想在VB中用对、用好动态库中的 函数,仅仅有声明还是远远不够的。前面已经说过,由于VB不能验证应用程序传递到动态连接 库中的参数值是否正确,因此就要求程序员应对参数类型有非常详细的了解,否则很容易引 起应用程序发生通用保护错或导致潜在的