在VB应用程序中调用动态链接库 Windows的动态链接库（Dynamic Link Library，简称DLL）是Microsoft Windows操作系统最显著的特点之一，也是Windows系统成为优秀软件设计平台的基础。动态链接库提出了一个全新的概念，使得程序设计人员可以利用动态链接库的函数（或过程）实现应用程序共享代码和资源，因此极大地提高了应用程序的编写效率，在多任务环境下还可以大大提高应用程序的执行效率。 Visual Basic（以下简称VB）是Microsoft公司推出的一种简单、方便、高效率的全新Windows编程工具和环境，其本身就是通过动态链接库函数或过程来工作的，VB还允许其应用程序直接调用动态链接库的（包括用户自己开发的动态链接库）函数或过程。
下面详细说明VB应用程序调用动态链接库的方法、步骤及应注意的一系列问题。
一般说来，VB应用程序调用动态链接库需要经过两个步骤：声明和调用。
一、动态链接库函数或过程的声明
调用动态链接库之前，应该首先对动态链接库函数或过程进行声明。
1.动态链接库函数或过程的声明方法
动态链接库函数或过程必须在全局模块或表格级模块中声明才有效。当动态链接库函数或过程没有返回值时应声明成子程序Sub方式，若有返回值时则应声明成函数Function方式，具体如下：
子程序Sub格式：
Declare Sub <动态链接库过程名> Lib <动态链接库名> [Alias<别名>] [（<参数>）]
函数Function格式：
Declare Function <动态链接库函数名> Lib <动态链接库名> [Alias<别名>] [（<参数>）] As 数据类型
其中：
动态链接库函数或过程名为被调用动态链接库中的函数或过程的名字。
动态链接库名为动态链接库函数或过程所在的动态链接库的名字。
Alias<别名>为可选项，用户可为动态链接库取一个别名。
<参数>为可选项，当调用动态链接库中的函数或过程时，由VB传送给动态链接库函数或过程的参数格式和类型。
数据类型为动态链接库函数返回值的类型。
2.动态链接库函数或过程声明的注意事项
（1）Lib<动态链接库名>
若应用的是Windows操作系统本身的动态链接库，如USER.DLL、KERNEL.DLL等，只需直接说明为库名即可。如果使用的是其它来源的动态链接库，则须指明动态链接库的全路径名，如“D:\USER\EXAMPLE.DLL”。值得指出的是Declare声明不区分大小写字母。
（2）参数及其传递方式
在VB中，有按值传递（By Value）和按地址传递（By Reference）两种参数传递方式。调用动态链接库函数时要注意参数的传递方式，若按值传递，则应在参数前面加上“Byval”关键字，按地址传递则不加关键字。
（3）任意类型的参数
一些动态链接库函数或过程的同一参数可接收不同类型的参数值，这时要用As Any作为参数的类型，As Any表示参数的类型不受限制。当参数以As Any方式说明时，VB是以地址方式传递参数的。
（4）特殊名字的处理
当要调用的动态链接库函数或过程和VB标准的系统函数重名，或者有一些动态链接库函数或过程名在VB中是不合法的符号（如减字符“－”或下划线“_”）时，应用Alias为其声明一个别名。利用别名还可以对动态链接函数或过程进行改名，例如可以将某个名字很长的函数用一个简短的别名替代或表示。
（5）数据类型的对应
由于在实际工作过程中，动态链接库函数或过程主要由C（或C++）语言开发完成，其编写和设计均是按C语言规范实现的。因此要求VB应用程序中的数据类型与C语言的数据类型相对应。如表一所示。

按照表一数据类型对应关系即可将绝大部分动态链接库函数或过程声明为VB函数，进而调用执行。例如，在Windows操作系统本身提供的动态链接库GDI.DLL中，确定显示器分辨率的函数为GetDeviceCaps，其格式为：
Int GetDeviceCaps(HDC hdc，int icapabicity)
按照转换规则，在VB应用程序中声明格式应该为：
Declare Function GetDeviceCaps Lib“GDI.DLL”(Byval hdc As Integer，Byval icapabicity As Integer) As Integer
待正确声明以后，就可以像调用标准函数一样来调用执行动态链接库函数或过程了。
注：上述声明中，也可以用动态链接库声明符表示，例如用“%”表示“As Integer”。
二、动态链接库函数或过程的调用
在全局模块或表格级模块中声明函数或过程以后，VB应用程序就可以像调用标准函数一样随处调用动态链接库中的函数或过程了，但还是有些值得特别注意的地方。
调用动态链接库函数或过程时应注意以下几点：
1.数组参数的特别处理
当数组中的单个元素作为参数时，和其它类型的变量参数传递完全一样。但是，当希望传递一个数组时，就不一样了。
当参数是数值型数组时，可以通过向动态链接库过程或函数传递数组首地址的方法实现数组传递。这是因为Windows在给数值型数据分配空间时，是一段连续的地址空间，而传递数组的首地址实际上是将数组中首元素以地址方式（非Byval方式）传递参数。这样，动态链接库就可以通过首元素地址访问整个数组变量的元素。
但当参数是一个字符串数组时，动态链接库就无法通过首地址方式访问所有元素，其理由有两点：一是因为Windows和VB为字符串型数组分配空间时，并不是一段连续的地址空间；二是因为每个数组元素的串长不一致。因此，动态链接库函数或过程不能接受一个字符串型的数组作为参数。
如果一个动态链接库函数需要一个缓冲区指针的参数，可以有两种办法实现：一是通过字符串变量，二是通过一个数值型数组。不过应保证变量足够长或数组足够大以避免缓冲区溢出而造成内存冲突。
2.字符串类型参数的处理
大多数动态链接库函数或过程在以字符串作为参数时，要求用C语言的串格式，又称为ASCII串，其特点是字符串以0结束。对这种情况作声明时，要以Byval方式声明，VB将自动把VB的串格式转换成C的串格式。例如,对于RegisterClipboard
Format()函数，声明如下：
Declare Function RegisterClipboardFormat% Lib“User”(Byval FormatName$)
在VB应用程序中作以下的调用：
i%=RegisterClipboardFormat%(“RTF”)
虽然在声明中，串变量是以Byval方式声明，但是Byval仅仅起一个串格式转换的说明作用，而事实上参数传递仍按地址方式进行。这里要特别注意，有些动态链接库过程会改变串变量，通过给串变量赋值的方法，给VB应用程序传递出口参数。这样当入口参数的串长小于出口参数的串长时，就有可能因此而覆盖其它内存区域。因为动态链接库不能也无法自动为串变量增加空间，只能简单地将出口参数的串内容写入入口串变量中。因此在这种情况下，调用此类动态链接库时，要保证串变量参数足够长，以确保不造成内存冲突。
需要注意的是：一些专门为VB设计的动态链接库，对字符串变量参数不需要用Byval声明。
3.用户自定义类型参数的处理
一些动态链接库函数以记录或结构作为参数。VB允许将一个用户自定义类型的结构或结构中的某一单独元素作为动态链接库的参数传递，但用户自定义类型参数只能按地址方式传递，而不能按值方式传递，而且这种类型只能在全局模块中。
值得指出的是，在以自定义类型作为动态链接库参数传递时，类型中也不能有可变长的字符串类型，否则仍可能出现因串内容被修改而造成的内存冲突问题。
4.对象属性类型参数的处理
当需要以对象属性作为动态链接库函数或过程参数时，必须按值方式传递。在声明中，参数必须用Byval方式声明，在调用时，才可以直接调用对象的属性参数，否则就须先将属性参数赋给另一临时变量，再以临时变量为参数传递。
5.表格和控制图不能作为动态链接库函数或过程的参数
由于表格及控制图对象本身是非常复杂的数据结构，Windows的动态链接库设计没有考虑这种数据结构。此外，Windows的动态连接库也不支持系统的四个标准对象——Screen、Clipboard、Printer及Debug作为参数。
三、VB应用程序调用动态链接库示例
为了全面说明VB应用程序调用动态链接库的方法和步骤，现给出一个参考性VB应用程序示例。此程序的功能为：在一个窗体中显示机器及Windows的一些信息，包括CPU类型、协处理器是否安装、Windows运行模式、系统剩余内存、系统空闲资源等。
该程序调用了3个动态链接库函数或过程，即GetFree
Space、GetWinFlags和GetFreeSystemResources，其中：
GetFreeSpace函数扫描全局堆，确定并返回系统剩余内存空间的字节数；
GetWinFlags函数获取并返回系统内存配置信息（包括CPU类型、协处理器是否安装、运行模式）；
GetFreeSystemResources函数获取并返回系统资源分配情况。
该VB应用程序示例所涉及的窗体属性（见表二）及其相关源程序如下：

'变量定义
Declare Function GetRes Lib“User” Alias“GetFreeSystemResources”(Byval FuSysResource As Integer) As Integer
Declare Function GetWinFlags Lib“Kernel”() As Integer
Declare Function GetFreeSpace Lib“Kernel”(Byval Wflags%) As Long
Const WF_PMODE=&H1
Const WF_CPU086=&H40
Const WF_CPU186=&H80
Const WF_CPU286=&H2
Const WF_CPU386=&H4
Const WF_CPU486=&H8
Const WF_STANDARD=&H10
Const WF_Win286=&H10
Const WF_ENHANCED=&H20
Const WF_Win386=&H20
Const WF_LARGEFRAME=&H100
Const WF_SMALLFRAME=&H200
Const WF_80x87=&H400
Sub Form_load()
Winflags&=GetWinFlags()
If Winflags& And WF_CPU086 Then
Label_cpu.caption=“8086”
ElseIf Winflags& And WF_CPU186 Then
Label_cpu.caption=“80186”
ElseIf Winflags& And WF_CPU286 Then
Label_cpu.caption=“80286”
ElseIf Winflags& And WF_CPU386 Then
Label_cpu.caption=“80386”
Else
Label_cpu.caption=“80486或更高”
End if
If Winflags& And WF_ENHANCED Then
Label_run.caption=“386增强模式”
Elseif Winflags& And WF_Win286 Then
Label_run.caption=“标准模式”
End if
If Winflags& And WF_80X87 Then
Label_maths.caption=“有”
Elseif Label_maths.caption=“无”
End if
Mem&=GetFreeSpace(0)
Label_freemem.caption=Format$(mem&/1024,“##.###”)+“KB”
Res%=GetRes(0)
Label_resource.caption=Format$(res%,“##”)+“%”
End Sub
Sub CmdOk_Click()
End
End Sub
注：本程序只适应16位Windows系统。
(王向阳）