利用動態建立自動化介面實現VB的函式指標呼叫 (轉)
發信人: RoachCock (chen3feng), 信區: TRD
標 題: 我的 VB的指標
發信站: BBS 水木清華站 (Fri Jan 3 14:54:25 ), 轉信
本文首發於水木清華BBS MicrosoftTRD版,轉載請保留有關資訊
作者chen3feng(to:RoachCock@smth.org">RoachCock@smth.org)
: , .com">chen3fengx@hotmail.com
前幾天在CSDN文件中心見了一篇 Matthew Curland的VB函式指標呼叫,它是用的動態建立自定義介面指標
然後回掉其某個方法,不過這種方法雖然高,但是每一種函式需要建立一個自定義介面
型別,還得使用IDL語言,實在算不上方便,昨天我嘗試出來一種方案,那就是動態建立自
動化介面指標。雖然效率低,但是其靈活性足以彌補這個弱點.
我只動用兩個
為此我用了兩個OLE API:
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
前一個函式透過指定的描述資料建立一個型別資訊,後者則透過給定的介面和型別資訊創
建一個IDispatch指標 // VB的型別對應於VC的IDispatch智慧指標
為了建立型別資訊,需要填寫一個資料結構,因此需要從oleaut.h引入常數,型別,函式
宣告,就不再一一細述了。關於這兩個API的詳細資料請參考MSDN
實現方法
首先我們需要模擬C++中的類的結構,我們需要一個自定義結構來表示,
'物件
Private Type Delegator
pVtbl As Long '虛擬函式表指標
pFunc As Long '一個資料成員,在此為需要呼叫的函式的指標
End Type
'虛擬函式表
Private Type VTable
pThunk As Long '指向一個x86機器語言編寫的thunk函式,當然,我是先用
End Type '寫,在把機器碼抄下來的
thunk的程式碼如下:
'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
建立的這個方法的名字叫Invoke, dispid為0,也就是說,可以不透過成員直接呼叫
示例程式碼
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
'呼叫 API MessageBoxW
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, VT_I4, VT_I4, VT_BSTR, _
VT_BSTR, VT_I4)
MessageBoxW 0, "hehe,foMessageBoxW", "", 0 '可以省略Invoke
End Sub
'編譯以上程式碼需要引入型別庫操作庫
需要說明的是,由於Oleaut32只支援對自動化相容型別進行轉換,因此只能使用自動化相容型別
另外,由於VB的類不支援聚合,因此CreateStdDispatch的第一個引數外部IUnknown指標
引數不能使用,這也就意味著FunctionPtr物件必須保證在透過Create方法獲取的自動化
介面指標生存期內有效,這一點算是個遺憾吧
雖然期間廣泛使用了VC,但是作完了就不需要了,也不需要額外的動態連線庫
只需要把FunctionPtr類模組加入工程,建立一個FunctionPtr型別的物件,呼叫Create
就可以得到能用來回掉的自動化物件
Create的第一個引數為函式指標,第二個為函式返回值得型別,後面的不定個數的引數
是函式的引數的型別.用起來很簡單
,包括完整的測試Project
'FunctionPtr.cls '函式指標類的定義
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataBehavior = 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 PARATA
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 = Vtr(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: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
CodeViewDeInfo=-1
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-996735/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- VB動態呼叫外部函式的方法 (轉)函式
- 利用指標實現strncmp函式功能指標函式
- 函式指標呼叫函式指標
- VB真是想不到系列之三:VB指標葵花寶典之函式指標 (轉)指標函式
- C#動態執行函式:利用反射實現C#函式反射
- 利用VB實現對IE的呼叫與控制 (轉)
- 在VB中實現窗體的動態效果 (轉)
- 宣告函式指標並實現回撥 (轉)函式指標
- VB中實現窗體自動隱藏 (轉)
- 在PowerBuilder中動態呼叫函式UI函式
- 函式計算自動化運維實戰3 -- 事件觸發自動建立快照函式運維事件
- 利用Github Actions實現自動化部署Github
- VB呼叫C程式的方法—動態連結庫法 (轉)C程式
- 基於RestAssured實現介面自動化REST
- BCB中實現動態建立元件 (轉)元件
- 利用github提供的Webhooks實現自動化部署GithubWebHook
- python介面自動化(三十四)-封裝與呼叫--函式和引數化(詳解)Python封裝函式
- C++動態建立二維陣列,二維陣列指標,以及動態二維陣列函式傳遞C++陣列指標函式
- 動態呼叫python類和函式Python函式
- 如何使用函式指標呼叫類中的函式和普通函式函式指標
- iOS--利用Fastlane實現自動化打包iOSAST
- 指標函式 和 函式指標指標函式
- 動態庫的建立和呼叫
- 利用C++Builder 中OLE自動化功能實現呼叫Word進行報表製作 (轉)C++UI
- 利用模板實現動態的繼承體系 (轉)繼承
- python+requests 實現介面自動化Python
- postman實現介面的自動化測試Postman
- 函式計算自動化運維實戰2 -- 事件觸發eip自動轉移函式運維事件
- C#動態建立介面的實現例項物件C#物件
- SpringBoot 動態代理實現三方介面呼叫Spring Boot
- 利用 GitHub Actions 實現自動部署靜態部落格Github
- 利用Python實現微信半自動化操作!Python
- 如何利用 RPA 實現自動化獲客?
- 函式指標淺談 (轉)函式指標
- JMeter 介面自動化測試(手工轉自動化指令碼)JMeter指令碼
- 利用VC++程式設計實現程式自動啟動 (轉)C++程式設計
- 在VB6.0中實現動態統計報表 (轉)
- Laravel 如何實現既能靜態呼叫,又能動態呼叫Laravel