首页 > 代码库 > 获取VB类模块成员函数指针(转)

获取VB类模块成员函数指针(转)

最近在做一些VB6、VBA的项目,被如何获取类模块中的函数指针这个问题所困扰,收集整理后,有2分资料值得收藏,特将关键部分留存,以备后续查找。

参照连接1:http://www.cnblogs.com/pctgl/articles/1352916.html

参照连接2:http://blog.csdn.net/lyserver/article/details/4224676

 

以下是链接1中的部分内容:

1. 函数地址 = GetClassProcAddress ( 指定为哪个函数 [上面解释],想获取的函数的参数个数 )

2. 这个函数的实现前提,要求有个局部数组,也就是在类模块的声明部分声明一个数组,由于我这个是在写一个热键类中完成的,所以这个数组名就叫了 TimerCallBack,你可以随便修改为你想要的 100% 免责开源...

3. 函数返回值是一个 Long 型数据,并不是你指定的函数的真正地址,而是 TimerCallBack 数组的首元素地址, TimerCallBack中是一个小型的用汇编写的函数。

4. 返回值就是你可以进行子类化的函数地址,返回值可直接用于 SetWindowLong x,x,这里

下面这个GetClassProcAddress函数是作者修改后的版本,我调整了一下格式,以便阅读:

Private Function GetClassProcAddress(ByVal SinceCount As Long, ByVal ParamsCount As Long) As Long    Dim mePtr As Long    Dim i As Long    Dim jmpAddress As Long        mePtr = ObjPtr(Me)    CopyMemory i, ByVal mePtr, 4    CopyMemory i, ByVal i + (SinceCount - 1) * 4 + &H1C, 4    CopyMemory jmpAddress, ByVal i + 1, 4    jmpAddress = i + jmpAddress + 5        ReDim TimerCallback(33)    TimerCallback(0) = &H55    TimerCallback(1) = &H8B    TimerCallback(2) = &HEC    TimerCallback(3) = &H83    TimerCallback(4) = &HEC    TimerCallback(5) = &H10    TimerCallback(6) = &H8B    TimerCallback(7) = &HFC    TimerCallback(8) = &H8D    TimerCallback(9) = &H75    TimerCallback(10) = &H8    TimerCallback(11) = &H33    TimerCallback(12) = &HC9    TimerCallback(13) = &HB1    TimerCallback(15) = &HFC    TimerCallback(16) = &HF3    TimerCallback(17) = &HA5    TimerCallback(18) = &H68    TimerCallback(23) = &HB8    TimerCallback(28) = &HFF    TimerCallback(29) = &HD0    TimerCallback(30) = &HC9    TimerCallback(33) = &H0        TimerCallback(32) = ParamsCount * 4    TimerCallback(14) = ParamsCount             ‘ mov cl,4(params)   >>> for rep movs    CopyMemory TimerCallback(19), mePtr         ‘ objptr(me)    CopyMemory TimerCallback(24), jmpAddress    ‘ call eax        If ParamsCount = 0 Then        TimerCallback(31) = &HC3    Else        TimerCallback(31) = &HC2    End If        GetClassProcAddress = VarPtr(TimerCallback(0))End Function

  

以上代码,其中有一部分是作者参考的,原始函数代码如下

Private Function GetClassProcAddress(ByVal SinceCount As Long) As Long    ‘***************************************************************************************************    ‘   VB6 历史上最简单的获取类中指定函数地址的函数诞生了,can be get address of property to value ,too    ‘***************************************************************************************************    Dim i As Long, jmpAddress As Long        CopyMemory i, ByVal ObjPtr(Me), 4                                ‘ get vtable    CopyMemory i, ByVal i + (SinceCount - 1) * 4 + &H1C, 4           ‘ 查表    CopyMemory jmpAddress, ByVal i + 1, 4                            ‘ 获取的函数地址实际还是一个表,是一个跳转表    GetClassProcAddress = i + jmpAddress + 5                         ‘ 计算跳转相对偏移取实际地址End Function

调用方法:

类模块中指定的函数地址 = GetClassProcAddress( 第几个函数 )

参数 SinceCount:是从某个类模块中最顶端的函数或属性算起,他是第几个函数

1. 当被查找的函数为 公用函数时,它的值就是自顶端算起的第几个函数,比如你在类模块中最顶端写的一个公用函数 WndProc,那么就传 1 如果是第2个公用函数或属性那么就传 2 依次... 注意,计算的时候要算上公用属性,公用属性也要算上,当他是函数,算做一个

2. 当被查找的函数为 局部函数时,也就是说如果是 Private 修饰的函数,则此参数值为 所有公用函数个数 + 这是第 N 个私有函数 也是从顶端算起,同样包括属性

原理:

对象是什么? 对象实际就是一个结构,VB,甚至 C++ 都不一定能让你真正深刻的理解最底层的对象构造,如果说 VB 能让你懂得什么叫继承 则 C++ 能让你知道对象还可以变异....对象原来是那么简单实现了那么高级的技术 再向底层看,用汇编构造对象,你就可以看到,对象原来就是一个结构,结构中包括所有公用函数,属性的地址指针,和连接,销毁函数指针等 那么,在返回到 VB,ObjPtr 可以得到对象的 vTable 指针,通过查询 vTable 就可以得到我们想要的函数指针,前提是我们要知道编译器是 按照什么样的顺序放置属性函数指针的,现在经过查询资料和测试,已经知道了,那就是 基址 + &H1C 所谓的基址其实就是vTable, &H1C就 是VB给结构添加的和必要的函数指针所占用的空间, 从vTable+&H1C 开始存储我们的函数地址,存储顺序如何,可以参照上面对 GetClassProcAddress 的参数 SinceCount 的解释.VB 把所有模块都单独的建立了一个表,每个表中又有单独的表表示他所包含的函数地址. 好了,函数和原理解释已经差不多了,再说说应用 很不幸的我要说,直接应用价值基本 = 0 , 郁闷啊... 为什么呢? 因为... 对象的函数他的第1个参数是vTable指针,第2个(暂时忘了,想起来再补) 于是你构造的函数有4个参数,但编译后该函数将有6个参数,那如果直接交给别人用,比如 APi 那还不出事吗... 会出事,但又不是不能弥补,加上少量的内嵌汇编代码,从新构造一个小函数,就可以完美的运行了,o... 还是很不错的选择 说了很多, 我也累了,就先打住了,总结起来,就是成功的用最简单的代码获取了类模块中指定的函数地址,从这个角度来说此文应该还是一精华文章吧? 等我有时间了,我会将弥补的汇编函数和 GetClassProcAddress 相结合,构造一个最简单化的代码,实现真正的类模块回调函数

  

以下是链接2中的部分内容:

参考2链接的作者构建了一个类模块,可以直接使用该类模块子类化指定窗体,其窗体新的消息处理过程名是WindowProc

该作者还构建过一个自定义计时器类,其中也用到了这个获取类模块函数指针的函数,链接http://blog.csdn.net/lyserver/article/details/4230045

以下是类模块的代码:

Option ExplicitPrivate Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As LongPrivate Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As LongPrivate Declare Function IsWindow Lib "user32" (ByVal hWnd As Long) As LongPrivate 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 LongPrivate Const GWL_WNDPROC = (-4)Private Const WM_NCDESTROY = &H82Public Event WindowProc(ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long, bCallNext As Boolean, lReturn As Long)Dim m_hwnd As Long, m_NewProc As Long, m_OldProc As LongPrivate Sub Class_Initialize()    m_NewProc = GetClassProcAddr(Me, 5, 4, True)End SubPrivate Sub Class_Terminate()    Call UnbindEnd SubPublic Function Bind(ByVal hWnd As Long) As Boolean    Call Unbind    If IsWindow(hWnd) Then m_hwnd = hWnd    m_OldProc = SetWindowLong(m_hwnd, GWL_WNDPROC, m_NewProc)    Bind = CBool(m_OldProc)End FunctionPublic Function Unbind() As Boolean    If m_OldProc <> 0 Then Unbind = CBool(SetWindowLong(m_hwnd, GWL_WNDPROC, m_OldProc))    m_OldProc = 0End FunctionPrivate Function WindowProcCallBack(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long    Dim bCallNext As Boolean, lReturn As Long    bCallNext = True    RaiseEvent WindowProc(Msg, wParam, lParam, bCallNext, lReturn)    If bCallNext Then        WindowProcCallBack = CallWindowProc(m_OldProc, hWnd, Msg, wParam, lParam)    Else        WindowProcCallBack = lReturn    End If    If hWnd = m_hwnd And Msg = WM_NCDESTROY Then Call UnbindEnd FunctionPrivate Function GetClassProcAddr(obj As Object, ByVal Index As Long, _    Optional ByVal ParamCount As Long = 4, Optional ByVal HasReturnValue As Boolean) As Long    Static lReturn As Long, pReturn As Long    Static AsmCode(50) As Byte    Dim i As Long, pThis As Long, pVtbl As Long, pFunc As Long    pThis = ObjPtr(obj)    CopyMemory pVtbl, ByVal pThis, 4    CopyMemory pFunc, ByVal pVtbl + (6 + Index) * 4, 4    pReturn = VarPtr(lReturn)    For i = 0 To UBound(AsmCode)                                ‘填充nop        AsmCode(i) = &H90    Next    AsmCode(0) = &H55                                           ‘push   ebp    AsmCode(1) = &H8B: AsmCode(2) = &HEC                        ‘mov    ebp,esp    AsmCode(3) = &H53                                           ‘push   ebx    AsmCode(4) = &H56                                           ‘push   esi    AsmCode(5) = &H57                                           ‘push   edi    If HasReturnValue Then        AsmCode(6) = &HB8                                       ‘mov    offset lReturn        CopyMemory AsmCode(7), pReturn, 4        AsmCode(11) = &H50                                      ‘push   eax    End If    For i = 0 To ParamCount - 1                                 ‘push   dword ptr[ebp+xx]        AsmCode(12 + i * 3) = &HFF        AsmCode(13 + i * 3) = &H75        AsmCode(14 + i * 3) = (ParamCount - i) * 4 + 4    Next    i = i * 3 + 12    AsmCode(i) = &HB9                                           ‘mov    ecx,this    CopyMemory AsmCode(i + 1), pThis, 4    AsmCode(i + 5) = &H51                                       ‘push ecx    AsmCode(i + 6) = &HE8                                       ‘call 相对地址    CopyMemory AsmCode(i + 7), pFunc - VarPtr(AsmCode(i + 6)) - 5, 4    If HasReturnValue Then        AsmCode(i + 11) = &HB8                                  ‘mov    eax,offset lReturn        CopyMemory AsmCode(i + 12), pReturn, 4        AsmCode(i + 16) = &H8B                                  ‘mov    eax,dword ptr[eax]        AsmCode(i + 17) = &H0    End If    AsmCode(i + 18) = &H5F                                      ‘pop    edi    AsmCode(i + 19) = &H5E                                      ‘pop    esi    AsmCode(i + 20) = &H5B                                      ‘pop    ebx    AsmCode(i + 21) = &H8B: AsmCode(i + 22) = &HE5              ‘mov    esp,ebp    AsmCode(i + 23) = &H5D                                      ‘pop    ebp    AsmCode(i + 24) = &HC3                                      ‘ret    GetClassProcAddr = VarPtr(AsmCode(0))End Function

获取VB类模块成员函数指针(转)