声明 DLL 过程
尽管 Visual Basic 在 Win32api.txt 中提供了大量的预定义声明,但还是需要知道如何亲自编写声明。例如,有时希望访问用其它语言编写的 DLL 中的过程,或者改写 Visual Basic 的预定义声明,以满足特殊需要。要声明一个 DLL 过程,需要在代码窗口的“声明”部分增加一个 Declare 语句。如果该过程返回一个值,应将其声明为 Function:Declare Function publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])] As Type如果过程没有返回值,可将其声明为 Sub:Declare Sub publicname Lib "libname" [Alias "alias"] [([[ByVal] variable [As type] [,[ByVal] variable [As type]]...])]缺省情况下,在标准模块中声明的 DLL 过程是公有的,可以在应用程序的任何地方调用它。在其它类型的模块中定义的 DLL 过程是模块私有的,必须在它们前面声明 Private 关键字,以示区分。在 32 位的 Visual Basic 中过程名是区分大小写的。在以前的 16 位版本中,过程名是不区分大小写的。详细信息   请参阅《联机手册》中《语言参考》中的“声明语句”。指定库
Declare 语句中的 Lib 子句用来告诉 Visual Basic 如何找到包含过程的 .dll 文件。如果引用的过程属于 Windows 核心库(User32、Kernel32 或 GDI32),则可以不包含文件扩展名:Declare Function GetTickCount Lib "kernel32" Alias _
"GetTickCount" () As Long对于其它 DLL,Lib 子句指定文件的路径:Declare Function lzCopy Lib "c:\windows\lzexpand.dll" _
(ByVal S As Integer,    ByVal D As Integer) As Long如果未指定 libname 的路径,Visual Basic 将按照下列顺序查找该文件: .exe 文件所在的目录
当前目录
Windows 位系统目录(通常为 \Windows\System)
Windows 目录(不一定是 \Windows)
Path 环境变量中的目录 
下表中列出了通常的操作系统环境库文件。动态链接库 描述 
Advapi32.dll 高级 API 服务,支持大量的 API(其中包括许多安全与注册方面的调用) 
Comdlg32.dll 通用对话框 API 库 
Gdi32.dll 图形设备接口 API 库 
Kernel32.dll Windows 32 位核心的 API 支持 
Lz32.dll 32 位压缩例程 
Mpr.dll 多接口路由器库 
Netapi32.dll 32 位网络 API 库 
Shell32.dll 32 位 Shell API 库 
User32.dll 用户接口例程库 
Version.dll 版本库 
Winmm.dll Windows 多媒体库 
Winspool.drv 后台打印接口,包含后台打印 API 调用。 
处理使用字符串的 Windows API 过程
如果调用的 Windows API 过程要使用字符串,那么声明语句中必须增加一个 Alias 子句,以指定正确的字符集。包含字符串的 Windows API 函数实际有两种格式:ANSI 和 Unicode。因此,在 Windows 头文件中,每个包含字符串的函数都同时有 ANSI 版本和 Unicode 版本。例如,下面是 SetWindowText 函数的两种 C 语言描述。可以看到,第一个描述将函数定义为 SetWindowTextA,尾部的“A”表明它是一个 ANSI 函数:WINUSERAPI
BOOL
WINAPI
SetWindowTextA(
   HWND hWnd,
   LPCSTR lpString);第二个描述将它定义为 SetWindowTextW,尾部的“W”表明它是一个 Unicode 函数:WINUSERAPI
BOOL
WINAPI
SetWindowTextW(
   HWND hWnd,
   LPCWSTR lpString);因为两个函数实际的名称都不是“SetWindowText”,要引用正确的函数就必须增加一个 Alias 子句:Private Declare Function SetWindowText Lib "user32" _
Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _
lpString As String) As Long请注意,Alias 子句后面的字符串必须是过程的真正名称,而且必须是区分大小写的。重点   对于 Visual Basic 中使用的 API 函数,应该指定函数的 ANSI 版本,因为只有 Windows NT 才支持 Unicode 版本,而 Windows 95 不支持这个版本。仅当应用程序只运行在 Windows NT 平台上的时候才可以使用 Unicode 版本。使用值或引用传递
在缺省的情况下,Visual Basic 以引用方式传递所有参数。这意味着并没有传递实际的参数值,Visual Basic 只传递了数据的 32 位地址。在 Declare 语句中不要求包含 ByRef 关键字,但是如果包含该关键字,就能够清楚地看出数据是以何种方式传递的。许多 DLL 过程要求参数以值方式传递。这意味着它们需要实际的数据,而不是数据的内存地址。如果过程需要一个传值参数,而传递给它的参数是一个指针,那么由于得到了错误的数据,该过程将不能正确地工作。要使参数以使用值方式传递,在 Declare 语句中需要在参数声明的前面加上 ByVal 关键字。例如,InvertRect 过程要求第一个参数使用值,而第二个使用引用:Declare Function InvertRect Lib "user32" Alias _
"InvertRectA" (ByVal hdc As Long, _
lpRect As RECT) As Long也可以在调用过程时使用 ByVal 关键字。注意   在查看使用 C 语言语法的 DLL 过程文档时,请记住 C 以传值方式传递数组以外的参数。字符串参数是一个特例。如果以使用值方式传递字符串,那么传递的将是该字符串中第一个数据字节的地址;如果以使用引用方式传递字符串,那么实际传递的将是用来保存另一个地址的内存单元的地址;后面的“地址”实际是字符串的第一个数据字节的内存地址。本章后面的帮助主题“将字符串传递到 DLL 过程”将解释如何确定字符串参数传递的正确方式。不标准的名称
有时,个别的 DLL 过程的名称不是有效的标识符。例如,它可能包含了非法的字符(如连字符),或者名称是 Visual Basic 的关键字(如 GetObject)。在这种情况下,可以使用 Alias 关键字。例如,操作环境 DLL 中的某些过程名以下划线开始。尽管在 Visual Basic 标识符中允许使用下划线,但是下划线不能作为标识符的第一个字符。为了使用这种过程,必须先声明一个名称合法的过程,然后用 Alias 子句引用过程的真实名称:Declare Function lopen Lib "kernel32" Alias "_lopen" _
(ByVal lpPathName As String, ByVal iReadWrite _
As Long) As Long在上例中,lopen 是 Visual Basic 中使用的过程名称。而 _lopen 则是 DLL 中可以识别的名称。为了使用方便,也可以使用 Alias 子句改变过程的名字。如果使用自己的名称替代了过程原来的名称(例如使用 WinDir 代替 GetWindowsDirectoryA),那么必须在文档中清楚地说明这种修改,从而便于将来对文档进行维护。使用序号标识 DLL 过程
除了使用名称之外,还可以使用序号来标识所有 DLL 过程。某些 DLL 中不包含过程的名称,在声明它们包含的过程时必须使用序号。同使用名称标识 DLL 过程相比,如果使用序号,在最终的应用程序中消耗的内存将比较少,而且速度会快些。重点   一个具体的 API 的序号在不同的操作系统中可能是不同的。例如 GetWindowsDirectory 在 Win95 下的序号为 432,而在 Windows NT 4.0 下为 338。总而言之,如果希望应用程序能够在不同的操作系统下运行,那么请不要使用序号来标识 API 过程。如果过程不属于 API,或者应用程序使用的范围很有限,那么使用序号还是有好处的。要使用序号来声明 DLL 过程,Alias 子句中的字符串需要包含过程的序号,并在序号的前面加一个数字标记字符 (#)。例如,Windows kernel 中的 GetWindowsDirectory 函数的序号为 432;可以用下面的语句来声明该 DLL 过程:Declare Function GetWindowsDirectory Lib "kernel32" _
Alias "#432" (ByVal lpBuffer As String, _
ByVal nSize As Long) As Long注意,这里可以使用任意的合法名称作为过程的名称,Visual Basic 将用序号在 DLL 中寻找过程。为了得到要声明的过程的序号,可以使用 Dumpbin.exe 等实用工具。(Dumpbin.exe 是 Microsoft Visual C++ 提供的一个实用工具。)利用 Dumpbin,可以提取出 .dll 文件中的各种信息,例如 DLL 中的函数列表,它们的序号以及与代码有关的其它信息。详细信息   关于运行 Dumpbin 实用程序的有关说明,请参阅 Microsoft Visual C++ 文档。灵活的参数类型
某些 DLL 过程的同一个参数能够接受多种数据类型。如果需要传递多种类型的数据,可以将参数声明为 As Any,从而取消类型限制。例如,根据需要,下面的声明中的第三个参数 (lppt As Any) 既可以传递一个 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 LongAs Any 子句提供了一定的灵活性,但是,由于它不进行任何的类型检查,风险也随之增加。如果不进行类型检查,那么在调用过程时用错类型的可能性增加,这可能导致各种问题,包括应用程序的失败。在使用 As Any 子句时,必须仔细检查所有参数的类型。在消除了类型限制以后,Visual Basic 假定参数以传引用方式传递。在实际调用过程时,如果要使用传值方式,可以加入 ByVal。字符串将以传值方式传递,因此传递的是指向字符串的指针,而不是指向指针的指针。更深入的讨论请参阅“将字符串传递到 DLL 过程”。