读书人

VB回调函数如何写?参数如何传

发布时间: 2012-01-18 00:23:26 作者: rapoo

VB回调函数怎么写?参数怎么传?
VB回调函数怎么写?参数怎么传?

百度了下,很失望..汗...参数又咋个弄呢?

[解决办法]
下面的示例创建一个带有一个列表框的窗体,该列表框包含您的系统中的字体的按字母顺序排序的列表。

要运行该示例,请创建一个带有一个列表框的窗体。窗体部分的代码如下:

VB code
Option ExplicitPrivate Sub Form_Load()    Module1.FillListWithFonts List1End Sub
[解决办法]
将函数指针传递到 DLL 和类型库


熟悉 C 语言的程序员一定会熟悉函数指针的概念。对于不熟悉 C 语言的读者,有必要对此进行一番解释。函数指针是一种约定,程序员可以用它将一个自定义的函数的地址作为参数传递到另一个函数。后面一个函数可以不是自己编写的,但是已经进行了声明,所以可以在应用程序中使用。利用函数指针,可以调用 EnumWindows 等函数列出系统中打开的窗口,利用 EnumFontFamilies 列出所有的当前字体。利用函数指针还可以访问 Win32 API 中的其它许多函数,早期的 Visual Basic 没有提供对它们的支持。

在 Visual Basic 5.0 中,使用函数指针时仍然存在若干限制。详细信息,请参阅本帮助主题后面的“函数指针的局限与风险”。

有关函数指针的知识
使用例子可以很好地说明函数指针的用法。首先,看一看 Win32 API 中的 EnumWindows 函数:

Declare Function EnumWindows lib "user32" _
(ByVal lpEnumFunc as Long, _
ByVal lParam as Long ) As Long

EnumWindows 是一个枚举函数,它能够列出系统中每一个打开的窗口的句柄。EnumWindows 的工作方式是重复地调用传递给它的第一个参数(lpEnumFunc,函数指针)。每当 EnumWindows 调用函数,EnumWindows 都传递一个打开窗口的句柄。

在代码中调用 EnumWindows 时,可以将一个自定义函数作为第一个参数传递给它,用来处理一系列的值。例如,可以编写一个函数将所有的值添加到一个列表框中,将 hWnd 值转换为窗口的名字,以及其它任何操作!

为了表明传递的参数是一个自定义函数,在函数名称的前面要加上 AddressOf 关键字。第二个参数可以是合适的任何值。例如,如果要把 MyProc 作为函数参数,可以按下面的方式调用 EnumWindows:

x = EnumWindows(AddressOf MyProc, 5)

在调用过程时指定的自定义函数被称为回调函数。回调函数(通常简称为“回调”)能够对过程提供的数据执行指定的操作。

回调函数的参数集必须具有规定的形式,这是由使用回调函数的 API 决定的。关于需要什么参数,如何调用它们,请参阅 API 文档。

使用 AddressOf 关键字
如果代码要调用 Visual Basic 5.0 的函数指针,则必须将该代码放到标准的 .BAS 模块中,不可以将其放到类模块中,也不能将其附加到窗体上。在使用 AddressOf 关键字声明函数时,必须注意下列事项:

AddressOf 只能紧接在是参数列表中的参数前;该参数可以是自定义的过程、函数或者属性的名字。


写在 AddressOf 后面的过程、函数、属性必须与有关的声明和过程在同一个工程中。


AddressOf 只能用于自定义的过程、函数和属性,不能将其用于 Declare 语句声明的外部函数,也不能将其用于类型库中的函数。


在声明的 Sub、Function 或自定义的类型定义中,可以将函数指针传递到 As Any 或 As Long 类型的参数。
注意 可创建用 Visual C++ (或类似的工具)编译的 DLL 中的回调函数原型。要使用 AddressOf 时,原型必须使用 __stdcall 调用约定。不能将缺省调用约定与 AddressOf 并用

在变量中存储函数指针
在某些情况下,在将函数指针传递到 DLL 之前需要将其存储在一个中间变量中。如果需要将函数指针从一个 Visual Basic 传递到另一个,这种做法是很有用的。例如,在调用 RegisterClass 之类的函数时就需要用结构 (WndClass) 的成员来传递函数指针。

要将一个函数指针赋予结构中的一个成员,需要编写一个包装函数(wrapper)。例如,下面创建的 FnPtrToLong 就是一个包装函数,使用它可以将函数指针放入任何结构中:

Function FnPtrToLong (ByVal lngFnPtr As Long) As Long
FnPtrToLong = lngFnPtr
End Function

要使用该函数,首先需要声明类型,然后再调用 FnPtrToLong。AddressOf 加上回调函数的名字作为函数的参数。

Dim mt as MyType
mt.MyPtr = FnPtrToLong(AddressOf MyCallBackFunction)

子类派生
利用子类派生技术,可以截取发送到控件或窗体的消息。通过截取这些消息,可以编写自己的代码来改变或者扩展对象的行为。类的派生技术比较复杂,对它的全面讨论将超出本书的范围。下例只能提供该技术的大致轮廓。

重点 当 Visual Basic 处于中断模式时,不允许调用 vtable 方法或 AddressOf 函数。为了保证安全,Visual Basic 仅仅将 0 返回到 AddressOf 函数的调用者。对于子类派生情况,这意味着 WindowProc 将 0 返回到 Windows。Windows 要求它的许多消息返回非 0 值,因此返回的常数 0 将导致 Windows 与 Visual Basic 之间的死锁,从而迫使进程终止。

在下例中,应用程序包括一个简单的窗体,其中只有两个命令按钮。代码的作用是截取发送到窗体的 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

下一步,使用两个例程“钩入”消息流。第一个过程 (Hook) 调用了 SetWindowLong 函数,它使用了 GWL_WNDPROC 索引来创建窗口类的子类,窗口类是用来创建窗口的。然后它使用 AddressOf 关键字和回调函数 (WindowProc) 来截取消息并在“立即”窗口中打印消息的值。第二个过程 (Unhook) 关闭了子类,重新使原来的 Windows 过程成为回调函数。

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

最后,窗体的代码设置了 hWnd 的初始值,按钮的代码仅仅调用了上面的两个例程:

Private Sub Form_Load()
gHW = Me.hwnd
End Sub

Private Sub Command1_Click()
Hook
End Sub

Private Sub Command2_Click()
Unhook
End Sub

使用函数指针的局限与风险
使用函数指针是有风险的。每当调用 DLL 的时候,就失去了 Visual Basic 开发环境的稳定性,使用函数指针的危险性就更大了,因为它很容易导致应用程序失败并因此而丢失已完成的工作。在工作的时候必须经常地保存和备份工作成果。下面列出了使用函数指针的一些注意事项:

调试。在中断模式下,如果应用程序引发了回调函数,那么回调函数将被正常执行,但是断点和单步设置将被忽略。如果回调函数产生了异常,则可以捕捉到它并返回当前值。在中断模式下,如果堆栈上有回调函数,那么禁止复位。


形实转换程序。形实转换是 Windows 实现代码重定位的手段。如果在中断模式下删除回调函数,它的形实转换程序将被修改返回 0。该值通常总是正确的,但是也有意外的情况。如果在中断模式下先删除了一个回调函数,然后又再次将其加入,那么有些被调用者可能对新的函数地址一无所知。在 .exe 中不使用形实转换程序,指针被直接传递到入口点。


被传递的函数的签名有误。如果函数的参数个数与调用者期望的不一致,或者在调用某个参数时错误地使用 ByRef 或 ByVal,则应用程序可能会失败。因此,在传递函数时签名一定要正确。


将函数传递给不存在的 Windows 过程。在对某个窗口进行子类派生的时候,需要将一个函数指针作为 Windows 过程 (WindowProc) 传递给 Windows。但是,在 IDE 中运行应用程序时,在调用 WindowProc 时下一层函数可能已经被破坏了。这可能导致一般性保护错误,并使 Visual Basic 开发环境遭到破坏。


不支持“Basic 到 Basic”的函数指针。在 Visual Basic 的内部不能传递指向 Visual Basic 函数的指针。目前,只支持从 Visual Basic 到 DLL 函数的指针。


回调过程中包含错误。回调过程中的任何错误都不应回传最初调用它的外部过程,这是很重要的。可以通过在回调过程的开始加上 On Error Resume Next 语句来实
[解决办法]
楼上二位回答问题真负责,提问的才写那几个字...
[解决办法]
回调是一个要求严谨的技术,如果错误的使用会导致程序崩溃的。
[解决办法]
简单的例子:http://blog.csdn.net/chenjl1031/archive/2011/01/31/6170676.aspx
如果是标准的窗口处理程序,结构是这样的:

Form1的窗体代码:

VB code
'Form1建立一个菜单项“文件”,名称为File,再建立一子菜单项“打开文件”,名称为OpenFileOption ExplicitPrivate Sub Form_Load()    Dim Rtn As Long    prevWndProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)    Rtn = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf WndProc)End SubPrivate Sub Form_Unload(Cancel As Integer)    Dim Rtn As Long    Rtn = SetWindowLong(Me.hwnd, GWL_WNDPROC, prevWndProc)    prevWndProc = 0End Sub
[解决办法]
'设置初始窗体,单位为毫米,长宽为显示器的2/3,在正中央出现
Private Sub CHUSCT()
Form1.ScaleMode = 6
Form1.Width = Screen.Width * 2 / 3
Form1.Height = Screen.Height * 2 / 3
Form1.Top = (Screen.Height - Form1.Height) / 2
Form1.Left = (Screen.Width - Form1.Width) / 2
End Sub
[解决办法]
问题不是“资料不全”,而是你基础太薄弱。

像一下子搞复杂的东西,你会经常失望的。
[解决办法]
探讨
prevent from deleting

[解决办法]
探讨
下面的示例创建一个带有一个列表框的窗体,该列表框包含您的系统中的字体的按字母顺序排序的列表。

要运行该示例,请创建一个带有一个列表框的窗体。窗体部分的代码如下:


VB code
Option Explicit

Private Sub Form_Load()
Module1.FillListWithFonts List1
End Sub




把下面的……

读书人网 >VB

热点推荐