最近在做一些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 Explicit

Private 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 Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function IsWindow Lib "user32" (ByVal hWnd As Long) As Long
Private 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
Private Const GWL_WNDPROC = (-4)
Private Const WM_NCDESTROY = &H82 Public 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 Long
Private Sub Class_Initialize()
m_NewProc = GetClassProcAddr(Me, 5, 4, True)
End Sub Private Sub Class_Terminate()
Call Unbind
End Sub Public 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 Function Public Function Unbind() As Boolean
If m_OldProc <> 0 Then Unbind = CBool(SetWindowLong(m_hwnd, GWL_WNDPROC, m_OldProc))
m_OldProc = 0
End Function Private 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 Unbind
End Function Private 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类模块成员函数指针(转)的更多相关文章

  1. VB6/VBA中跟踪鼠标移出窗体控件事件(类模块成员函数指针CHooker类应用)

    一.关于起因 前几天发了一篇博文,是关于获取VB类模块成员函数指针的内容(http://www.cnblogs.com/alexywt/p/5880993.html):今天我就发一下我的应用实例. V ...

  2. C++ 类的成员函数指针 ( function/bind )

    这个概念主要用在C++中去实现"委托"的特性. 但现在C++11 中有了 更好用的function/bind 功能.但对于类的成员函数指针的概念我们还是应该掌握的. 类函数指针 就 ...

  3. 类的成员函数指针和mem_fun适配器的用法

    先来看一个最简单的函数: void foo(int a) { cout << a << endl; } 它的函数指针类型为 void (*)(int); 我们可以这样使用: v ...

  4. 类成员函数指针 ->*语法剖析

    在cocos2d-x中,经常会出现这样的调用,如 ->*,这个是什么意思呢,如下面得这个例子: , 其实这是对类的成员函数指针的调用,在cocos2dx中,这种形式多用于回调函数的调用.如我们经 ...

  5. 从汇编看c++成员函数指针(二)

    下面先看一段c++源码: #include <cstdio> using namespace std; class X { public: virtual int get1() { ; } ...

  6. 成员函数指针与高效C++委托 (delegate)

    下载实例源代码 - 18.5 Kb 下载开发包库文件 - 18.6 Kb 概要 很遗憾, C++ 标准中没能提供面向对象的函数指针. 面向对象的函数指针也被称为闭包(closures) 或委托(del ...

  7. 成员函数指针与高性能C++委托

    1 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值. ...

  8. [转]成员函数指针与高性能的C++委托

    原文(作者:Don Clugston):Member Function Pointers and the Fastest Possible C++ Delegates 译文(作者:周翔): 成员函数指 ...

  9. C++ 指向类成员函数指针的用法(转自维基百科)

    类成员函数指针 类成员函数指针(member function pointer),是C++语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息. 目录 1 语法 ...

随机推荐

  1. Win32/MFC/COM学习推荐书籍

    以前有不少朋友问关于学习各种技术的推荐书籍的问题,这里把我觉得比较好的一些书籍列一下,希望能起到抛砖引玉的作用就好了:) Win32开发 Programming Windows by Charles ...

  2. Java编程风格学习(二)

    二.格式规范 在上一篇的Java编程风格学习(一)中我们讲述了在Java编码中的一般原则,虽然这些原则并不涉及具体的代码规范,但是这些原则却是我们在Java开发过程中所应该遵循的规范与思想.今天我们将 ...

  3. 深度神经网络(DNN)损失函数和激活函数的选择

    在深度神经网络(DNN)反向传播算法(BP)中,我们对DNN的前向反向传播算法的使用做了总结.里面使用的损失函数是均方差,而激活函数是Sigmoid.实际上DNN可以使用的损失函数和激活函数不少.这些 ...

  4. SpringMVC 集成velocity

    前言 没有美工的时代自然少不了对应的模板视图开发,jsp时代我们用起来也很爽,物极必反,项目大了,数据模型复杂了jsp则无法胜任. 开发环境 idea2016.jdk1.8.tomcat8.0.35 ...

  5. ES6-01:常量与变量的声明

    首先,我们声明一个变量: //定义一个变量num,并赋值为10: let num = 10; //进行打印 console.log(num); let与var有所不同: 语法特点1:let变量只能在当 ...

  6. Spark集群搭建_YARN

    2017年3月1日, 星期三 Spark集群搭建_YARN 前提:参考Spark集群搭建_Standalone   1.修改spark中conf中的spark-env.sh   2.Spark on ...

  7. 每天一个Linux命令 7

    常用yum命令1)查询 yum list #查询所有可用软件包列表yum search 关键字 #搜索服务器上所有和关键字相关的包2)安装 yum -y install 包名选项: install 安 ...

  8. PHP命名空间的概念与使用

    命名空间在其它编程语言中其名称不尽相同,但其核心慨念都是自定义一个存储空间.避免类名重复系统无法判断该执行哪一个类或是哪一个函数. 举例说明下.我先创建test这个文件夹在其当前目录下再创建一个ind ...

  9. WeMall微商城源码报名插件Apply的主要源码

    WeMall微信商城源码报名插件Apply,用于商城的签到系统,分享了部分比较重要的代码,供技术员学习参考 AdminController.class.php <?php namespace A ...

  10. 从数据库读取二进制图片,img标签显示图片

    引自 http://www.w3dev.cn/article/20110214/asp-net-csharp-image-base64-change.aspx      <img src=&qu ...