当一个过程的名称出现在一个参数列表中时,通常该过程已经被计算,并且该过程返回值的地址也会被传递。AddressOf 允许该过程的地址被传递给动态链接库 (DLL) 中的一个 Windows API 函数,而不是传递该过程的返回值。API 函数然后就可以使用这个地址来调用相应的 Basic 过程,这个过程就是我们所知道的回调。AddressOf 运算符只出现在对 API 过程的调用中。
熟悉 C 语言的程序员一定会熟悉函数指针的概念。对于不熟悉 C 语言的读者,有必要对此进行一番解释。函数指针是一种约定,程序员可以用它将一个自定义的函数的地址作为参数传递到另一个函数。后面一个函数可以不是自己编写的,但是已经进行了声明,所以可以在应用程序中使用。利用函数指针,可以调用 EnumWindows 等函数列出系统中打开的窗口,利用 EnumFontFamilies 列出所有的当前字体。利用函数指针还可以访问 Win32 API 中的其它许多函数,早期的 Visual Basic 没有提供对它们的支持。
在声明的 Sub、Function 或自定义的类型定义中,可以将函数指针传递到 As Any 或 As Long 类型的参数。
注意 可创建用 Visual C++ (或类似的工具)编译的 DLL 中的回调函数原型。要使用 AddressOf 时,原型必须使用 __stdcall 调用约定。不能将缺省调用约定与 AddressOf 并用
在下例中,应用程序包括一个简单的窗体,其中只有两个命令按钮。代码的作用是截取发送到窗体的 Windows 消息,并在“立即”窗口中打印出这些消息的值。
代码的第一部分是声明部分,包括 API 函数声明,常数声明和变量声明:
Declare Function CallWindowProc Lib "user32" Alias _
"CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias _
"SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Const GWL_WNDPROC = -4
Global lpPrevWndProc As Long
Global gHW As Long
Public Sub Hook()
lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, _
AddressOf WindowProc)
End Sub
Public Sub Unhook()
Dim temp As Long
temp = SetWindowLong(gHW, GWL_WNDPROC, _
lpPrevWndProc)
End Sub
Function WindowProc(ByVal hw As Long, ByVal uMsg As _
Long, ByVal wParam As Long, ByVal lParam As Long) As _
Long
Debug.Print "Message: "; hw, uMsg, wParam, lParam
WindowProc = CallWindowProc(lpPrevWndProc, hw, _
uMsg, wParam, lParam)
End Function
将函数传递给不存在的 Windows 过程。在对某个窗口进行子类派生的时候,需要将一个函数指针作为 Windows 过程 (WindowProc) 传递给 Windows。但是,在 IDE 中运行应用程序时,在调用 WindowProc 时下一层函数可能已经被破坏了。这可能导致一般性保护错误,并使 Visual Basic 开发环境遭到破坏。