| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2021 人关注过本帖
标题:VB函数指针的问题
取消只看楼主 加入收藏
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
结帖率:87.93%
收藏
已结贴  问题点数:20 回复次数:5 
VB函数指针的问题
在网上搜了一篇文章:http://blog.
代码如下
'FunctionPtr.cls        '函数指针类的定义
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "FunctionPtr"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
Private Const DISPATCH_METHOD = &H1
Private Const LOCALE_SYSTEM_DEFAULT = &H800
Private Const DISPID_VALUE = 0
Private Enum CALLCONV
    CC_FASTCALL = 0
    CC_CDECL = 1
    CC_MSCPASCAL = CC_CDECL + 1
    CC_PASCAL = CC_MSCPASCAL
    CC_MACPASCAL = CC_PASCAL + 1
    CC_STDCALL = CC_MACPASCAL + 1
    CC_FPFASTCALL = CC_STDCALL + 1
    CC_SYSCALL = CC_FPFASTCALL + 1
    CC_MPWCDECL = CC_SYSCALL + 1
    CC_MPWPASCAL = CC_MPWCDECL + 1
    CC_MAX = CC_MPWPASCAL + 1
End Enum
Private Type PARAMDATA
    szName As String
    vt As VariantTypeConstants
End Type
Private Type METHODDATA
    szName As String
    ppdata As Long '/* pointer to an array of PARAMDATAs */
    dispid As Long      '/* method ID */
    iMeth As Long        '/* method index */
    cc As CALLCONV        '/* calling convention */
    cArgs As Long       '/* count of arguments */
    wFlags As Integer       '/* same wFlags as on IDispatch::Invoke() */
    vtReturn As Integer
End Type
Private Type INTERFACEDATA
    pmethdata As Long  '/* pointer to an array of METHODDATAs */
    cMembers As Long
End Type
Private Declare Function CreateDispTypeInfo Lib "oleaut32" (ByRef pidata As INTERFACEDATA, ByVal lcid As Long, ByRef pptinfo As IUnknown) As Long
Private Declare Function CreateStdDispatch Lib "oleaut32" (ByVal punkOuter As IUnknown, ByRef pvThis As Delegator, ByVal ptinfo As IUnknown, ByRef ppunkStdDisp As IUnknown) As Long
Private Type VTable
    pThunk As Long
End Type
Private Type Delegator
    pVtbl As Long
    pFunc As Long
End Type
Private m_Thunk(5) As Long
Private m_VTable As VTable
Private m_Delegator As Delegator
Private m_InterfaceData As INTERFACEDATA
Private m_MethodData As METHODDATA
Private m_ParamData() As PARAMDATA
Private m_FunctionPtr As Object
Public Function Create(ByVal pFunc As Long, ByVal RetType As VariantTypeConstants, ParamArray ParamTypes() As Variant) As Object
   
    If TypeName(m_FunctionPtr) <> "Nothing" Then
        Set Create = m_FunctionPtr
        Exit Function
    End If
   
    Dim i As Long
    Dim p As Long
    Dim cParam As Long
    cParam = UBound(ParamTypes) + 1
   
    ReDim m_ParamData(cParam)
   
    If cParam Then
        For i = 0 To cParam - 1
            m_ParamData(i).vt = ParamTypes(i)
            m_ParamData(i).szName = ""
        Next
    End If
    m_MethodData.szName = "Invoke"
    m_MethodData.ppdata = VarPtr(m_ParamData(0))
    m_MethodData.dispid = DISPID_VALUE
    m_MethodData.iMeth = 0
    m_MethodData.cc = CC_STDCALL
    m_MethodData.cArgs = cParam
    m_MethodData.wFlags = DISPATCH_METHOD
    m_MethodData.vtReturn = RetType
   
    m_InterfaceData.pmethdata = VarPtr(m_MethodData)
    m_InterfaceData.cMembers = 1
    Dim ti As IUnknown
    Dim Result As IUnknown
    Set Result = Nothing
    i = CreateDispTypeInfo(m_InterfaceData, LOCALE_SYSTEM_DEFAULT, ti)
    If i = 0 Then
        m_VTable.pThunk = VarPtr(m_Thunk(0))
        
        m_Delegator.pVtbl = VarPtr(m_VTable)
        m_Delegator.pFunc = pFunc
        p = VarPtr(m_InterfaceData)
        p = VarPtr(m_Delegator)
        i = CreateStdDispatch(Nothing, m_Delegator, ti, Result)
        If i = 0 Then
            Set m_FunctionPtr = Result
            Set Create = m_FunctionPtr
        End If
    End If
End Function
Private Sub Class_Initialize()
    'thunk的机器码,加nop是为了清晰
    m_Thunk(0) = &H4244C8B      'mov ecx, [esp+4]           获得this pointer
    m_Thunk(1) = &H9004418B     'mov eax, [ecx+4]   nop     获得m_pFunc
    m_Thunk(2) = &H90240C8B     'mov ecx, [esp]     nop     得到返回地址
    m_Thunk(3) = &H4244C89      'mov [esp+4], ecx           保存返回地址
    m_Thunk(4) = &H9004C483     'add esp, 4         nop     重新调整堆栈
    m_Thunk(5) = &H9090E0FF     'jmp eax                    跳转到m_pFunc
End Sub
'Helper.cls     '其实不是Helper,只是原来的名字而已,包含供测试的函数
Attribute VB_Name = "Helper"
Option Explicit
Sub Test1(ByRef this As Long)
    MsgBox "Test1", vbOKOnly, "hehe"
End Sub
Sub Test(ByVal s As String)
    MsgBox s, vbOKOnly, "hehe"
End Sub
  
  
'测试程序
Option Explicit
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Sub Form_Load()
    Dim p As FunctionPtr
    Set p = New FunctionPtr
   
    Dim d As Object
    Set d = p.Create(AddressOf Test, vbEmpty, vbString)
   
    d.Invoke ("hehe")
   
    Dim hModUser32
    Dim pMessageBoxW As Long
   
    hModUser32 = GetModuleHandle("User32")
    pMessageBoxW = GetProcAddress(hModUser32, "MessageBoxW")
    Dim mbw As New FunctionPtr
    Dim MessageBoxW As Object
    Set MessageBoxW = mbw.Create(pMessageBoxW, vbLong, vbLong, vbString, vbString, vbLong)
    'MessageBoxA 0, "hehe,form MessageBoxA", "", 0
    MessageBoxW.Invoke 0, "hehe,form MessageBoxW", "", 0
End Sub
  
  
'Project文件
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\WINDOWS\SYSTEM\
STDOLE2.TLB#OLE Automation
Form=Form1.frm
Module=Helper; Helper.bas
Class=FunctionPtr; FunctionPtr.cls
IconForm="Form1"
Startup="Form1"
HelpFile=""
Title="工程1"
ExeName32="工程1.exe"
Command32=""
Name="工程1"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
CompilationType=0
OptimizationType=2
FavorPentiumPro(tm)=0
CodeViewDebugInfo=-1
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1


本文来自CSDN博客,转载请标明出处:http://blog.
-------------------------------------------------

查了下 VB中的声明
Private Declare Function MessageBoxW Lib "user32" (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal wType As Long) As Long
对于这一句的调用不是很明白
Set MessageBoxW = mbw.Create(pMessageBoxW, vbLong, vbLong, vbString, vbString, vbLong)
不知道谁能帮忙解释下 谢谢了
搜索更多相关主题的帖子: 函数指针 
2009-08-13 17:01
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
收藏
得分:0 
自己整理后的代码:
'窗体:
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
'获取一个应用程序或动态链接库的模块句柄
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
'GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址
Private Sub Form_Load()
    Dim p As FunctionPtr
    Set p = New FunctionPtr
    Dim d As Object
    Set d = p.Create(AddressOf Test, vbEmpty, vbString)
    'Test是一个标准模块函数
    d.Invoke "hehe"
    d "hehe"           ' 可以省略Invoke
 
    '调用Win32 API MessageBoxW
    Dim hModUser32
    Dim pMessageBoxW As Long
    hModUser32 = GetModuleHandle("User32")
    '获取一个应用程序或动态链接库的模块句柄
    pMessageBoxW = GetProcAddress(hModUser32, "MessageBoxW")
    'GetProcAddress函数检索指定的动态链接库(DLL)中的输出库函数地址
    'Private Declare Function MessageBoxW Lib "user32" (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal wType As Long) As Long
     
    Dim mbw As New FunctionPtr
    Dim MessageBoxW As Object
    Set MessageBoxW = mbw.Create(pMessageBoxW, vbLong, vbLong, vbString, vbString, vbLong)
 
    MessageBoxW.Invoke 0, "hehe,form MessageBoxW", "", 0
    MessageBoxW 0, "hehe,form MessageBoxW", "", 0       '可以省略Invoke
     
End Sub
2009-08-14 08:39
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
收藏
得分:0 
'类模块FunctionPtr
Option Explicit
 
Private Const DISPATCH_METHOD = &H1
Private Const LOCALE_SYSTEM_DEFAULT = &H800
Private Const DISPID_VALUE = 0                  '默认函数
 
Private Enum CALLCONV                           '函数调用方式
    CC_FASTCALL = 0
    CC_CDECL = 1
    CC_MSCPASCAL = CC_CDECL + 1
    CC_PASCAL = CC_MSCPASCAL
    CC_MACPASCAL = CC_PASCAL + 1
    CC_STDCALL = CC_MACPASCAL + 1
    CC_FPFASTCALL = CC_STDCALL + 1
    CC_SYSCALL = CC_FPFASTCALL + 1
    CC_MPWCDECL = CC_SYSCALL + 1
    CC_MPWPASCAL = CC_MPWCDECL + 1
    CC_MAX = CC_MPWPASCAL + 1
End Enum
 
Private Type PARAMDATA                          '参数信息
    szName As String                            '参数名
    vt As VariantTypeConstants                  '参数数据类型
End Type
 
Private Type METHODDATA                         '函数信息
    szName As String                            '函数名
    ppdata As Long '/* pointer to an array of PARAMDATAs */          '参数数组的地址
    dispid As Long      '/* method ID */        '函数的序号(不是很明白,估计是指函数的被调用级别,此程序中设置为默认级)
    iMeth As Long        '/* method index */    '??????
    cc As CALLCONV        '/* calling convention */   '调用方式
    cArgs As Long       '/* count of arguments */     '参数数目
    wFlags As Integer       '/* same wFlags as on IDispatch::Invoke() */    '?不是很明白,估计是标明方法是作为接口的方法吧
    vtReturn As Integer                         '函数调用返回的数据类型
End Type
 
Private Type INTERFACEDATA                      '接口信息?(不是很明白,按字面意思理解的,估计是用于接口)
    pmethdata As Long  '/* pointer to an array of METHODDATAs */
    cMembers As Long
End Type
 
Private Declare Function CreateDispTypeInfo Lib "oleaut32" (ByRef pidata As INTERFACEDATA, ByVal lcid As Long, ByRef pptinfo As IUnknown) As Long
'用于创建接口?
'pidata:接口的相关信息
'lcid:
'pptinfo
Private Declare Function CreateStdDispatch Lib "oleaut32" (ByVal punkOuter As IUnknown, ByRef pvThis As Delegator, ByVal ptinfo As IUnknown, ByRef ppunkStdDisp As IUnknown) As Long
'动态建立一个IDispatch接口
'punkOuter
'pvThis:代理对象 见下
'ptinfo
'ppunkStdDisp
 
'代理对象
Private Type Delegator
    pVtbl As Long       '虚函数表指针
    pFunc As Long       '一个数据成员,在此为需要调用的函数的指针
End Type
   
'虚函数表
Private Type VTable
    pThunk As Long      '指向一个x86机器语言编写的thunk函数,当然,我是先用VC
End Type
 
Private m_Thunk(5) As Long                      '汇编代码
 
Private m_VTable As VTable                      '虚函数表
Private m_Delegator As Delegator                '代理对象
Private m_InterfaceData As INTERFACEDATA        '接口信息
Private m_MethodData As METHODDATA              '函数信息
Private m_ParamData() As PARAMDATA              '参数数组
Private m_FunctionPtr As Object                 '函数对象?
 
Public Function Create(ByVal pFunc As Long, ByVal RetType As VariantTypeConstants, ParamArray ParamTypes() As Variant) As Object
     
    If TypeName(m_FunctionPtr) <> "Nothing" Then
        Set Create = m_FunctionPtr
        Exit Function
    End If
     
    Dim i As Long
    Dim p As Long
    Dim cParam As Long
    cParam = UBound(ParamTypes) + 1
     
    ReDim m_ParamData(cParam)
     
    If cParam Then
        For i = 0 To cParam - 1
            m_ParamData(i).vt = ParamTypes(i)
            m_ParamData(i).szName = ""
        Next
    End If
    m_MethodData.szName = "Invoke"                           '函数名
    m_MethodData.ppdata = VarPtr(m_ParamData(0))             '参数地址
    m_MethodData.dispid = DISPID_VALUE                       '函数被调用级别
    m_MethodData.iMeth = 0
    m_MethodData.cc = CC_STDCALL                             '函数执行方式
    m_MethodData.cArgs = cParam                              '参数个数
    m_MethodData.wFlags = DISPATCH_METHOD                    '函数为接口函数
    m_MethodData.vtReturn = RetType                          '函数返回数据类型
     
    m_InterfaceData.pmethdata = VarPtr(m_MethodData)         '函数地址
    m_InterfaceData.cMembers = 1                             '函数个数
 
    Dim ti As IUnknown
    Dim Result As IUnknown
    Set Result = Nothing
    i = CreateDispTypeInfo(m_InterfaceData, LOCALE_SYSTEM_DEFAULT, ti)
    If i = 0 Then                                             '接口创建成功?
        m_VTable.pThunk = VarPtr(m_Thunk(0))                  '汇编代码
         
        m_Delegator.pVtbl = VarPtr(m_VTable)                  '虚函数表指针----这里用到了Thunk技术,利用这个可以实现替换虚函数表来直接定位目标函数
        m_Delegator.pFunc = pFunc                             '真实的函数地址(个人认为也只是相对地址,不是绝对地址)
        p = VarPtr(m_InterfaceData)                           '接口函数的地址?
        p = VarPtr(m_Delegator)                               '代理地址?
        i = CreateStdDispatch(Nothing, m_Delegator, ti, Result)    '用真实的函数实现接口函数?
        If i = 0 Then                                         '创建成功?
            Set m_FunctionPtr = Result
            Set Create = m_FunctionPtr                        '返回接口对象?
        End If
    End If
End Function
 
Private Sub Class_Initialize()
    'thunk的机器码,加nop是为了清晰
    m_Thunk(0) = &H4244C8B      'mov ecx, [esp+4]           获得this pointernc
    m_Thunk(2) = &H90240C8B     'mov ecx, [esp]     nop     得到返回地
    m_Thunk(1) = &H9004418B     'mov eax, [ecx+4]   nop     获得m_pFu址
    m_Thunk(3) = &H4244C89      'mov [esp+4], ecx           保存返回地址
    m_Thunk(4) = &H9004C483     'add esp, 4         nop     重新调整堆栈
    m_Thunk(5) = &H9090E0FF     'jmp eax                    跳转到m_pFunc
End Sub
2009-08-14 08:40
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
收藏
得分:0 
现在不明白的主要有下面几点:
1.
  函数声明为
Public Function Create(ByVal pFunc As Long, ByVal RetType As VariantTypeConstants, ParamArray ParamTypes() As Variant) As Object
但是调用的时候的参数个数和原始的声明函数个数都不一致 怎么能调用
Set MessageBoxW = mbw.Create(pMessageBoxW, vbLong, vbLong, vbString, vbString, vbLong)
2.
VB中函数MessageBoxW的声明应该如下:
Private Declare Function MessageBoxW Lib "user32" (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal wType As Long) As Long
 
但是上面调用的时候 参数和原始声明的参数个数以及类型方面是怎样匹配的
 
3.
  如果我想实现对一个多参数的函数的调用 应该怎样写?
例如:
  Public Sub TestMessage(strValue as string,strCaption as string )
      msgbox strValue,Vbokonly,strCaption
  End Sub
2009-08-14 08:40
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
收藏
得分:0 
以下是引用风吹过b在2009-8-14 13:44的发言:Public Function Create(ByVal pFunc As Long, ByVal RetType As VariantTypeConstants, ParamArray ParamTypes() As Variant) As Object  ParamArray ParamTypes() As Variant这个声明 是 提供任意个参数给函数 ...

谢谢你的解答 有些是我看代码太不仔细了
别人给的解答:
1.ParamArray 表达不定长参数
2.你给的声明是直接在 VB 中调用的方式,正真原型为 C/C++
codeint MessageBox(  HWND hWnd,          // handle of owner window  
                     LPCTSTR lpText,     // address of text in message box  
                     LPCTSTR lpCaption,  // address of title of message box  
                     UINT uType          // style of message box);
所以与下面调用中的 (函数指针、返回值类型、参数1类型、参数2类型、参数3类型、参数4类型) 是一致的
VB codembw.Create(pMessageBoxW, vbLong, vbLong, vbString, vbString, vbLong)
3.这个是 Standard dll 的调用方式。VB 编写的 ActiveX Dll 遵循 COM 标准,应该直接提供公共对象的方法来调用。

[ 本帖最后由 happynight 于 2009-8-14 14:52 编辑 ]
2009-08-14 14:51
happynight
Rank: 8Rank: 8
等 级:贵宾
威 望:15
帖 子:807
专家分:760
注 册:2008-4-26
收藏
得分:0 
第一个是我看程序太马虎了 没注意到参数中还有 ParamArray 查下资料就知道了
第二个还是看程序马虎 没注意到参数中还有一个是指明了函数返回的数据类型
第三个我又修改了下程序 能正常执行了
Public Sub TestMessage(Byval strValue as string,Byval strCaption as string )  
      msgbox strValue,Vbokonly,strCaption  
End Sub

...............
Set d = p.Create(AddressOf MyText, vbEmpty, vbString, vbString)
...............
搞定 很Happy
2009-08-14 14:58
快速回复:VB函数指针的问题
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019316 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved