a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 115|回复: 1

[Visual Basic] 2011年计算机二级VB辅导知识总结(7)

[复制链接]
发表于 2012-7-31 22:10:12 | 显示全部楼层 |阅读模式
  VB动态调用外部函数的方法
  {5 t, k( {9 U: F  导读:VB可以用Declare声明来调用标准DLL的外部函数,但是其局限性也很明显:利用Declare我们只能载入在设计时通过Lib和Alias字句指定的函数指针!而不能在运行时指定由我们自己动态载入的函数指针),不能用Declare语句来调用任意的函数指针。当我们想动态调用外部函数的时候,就必须考虑采用其他的辅助方法,来完成这个任务了。
; d+ |! u6 X2 M% q% [; U  在文章《VB真是想不到系列之三:VB指针葵花宝典之函数指针 》、《Matthew Curland的VB函数指针调用》、《利用动态创建自动化接口实现VB的函数指针调用》等文献中对此问题都进行了一定程度上的讨论,但是头绪都很繁琐,对我这样的菜鸟还有点深奥,在资料搜索过程中,找到通过在VB中调入汇编程序,比较简便的实现了这个功能,下面就是实现原理:
+ g" O0 d( ]7 d1 f! {  1)使用LoadLibrary加载DLL;
, n3 k) K3 w0 ]3 G: d: w) H8 g  2)GetProcAddress获得函数指针;
5 K9 I, [6 @- u( Y- E  以上两步得到了预加载函数的指针,但是VB中没有提供使用这个指针的方法。我们可以通过一段汇编语言,来完成函数指针的调用!1 u8 B  n5 m0 a4 }
  3)通过汇编语言,把函数的所有参数压入堆栈,然后用Call待用函数指针就可以了。; U' g: K3 ]3 U2 k" ^9 ]
  实现以上功能的主要程序:: K0 t* w- F. v; [, I* [* A6 n
  ′加载Dll
& f0 K5 G9 @' b& t, F8 f# X9 u  LibAddr = LoadLibrary(ByVal "user32")$ ?% _# [  a$ P( w8 L
  ′获得函数指针
$ W4 ?, H8 w( i1 P  ProcAddr = GetProcAddress(LibAddr, ByVal "MessageBoxA")1 s( o* i' R) ?0 Z
  ′原型为MessageBox(hWnd, lpText, lpCaption, uType)9 t9 n0 R- z4 R
  ′---以下为Assembly部分---0 u. U, \+ g$ e* _( P
  push uType
) R$ ~' x5 j5 c  push lpCaption
$ v% j$ J0 F- t+ T  push lpText
, B) I! L, C$ B' B+ H  push hWnd, M$ l# x3 D" G! {! b
  call ProcAddr
# J0 J& f  a4 b  ′--------------------
' V7 ^4 n4 _: Y0 V& l  FreeLibrary LibAddr′释放空间4 j) x8 U3 O! m! o
  嘿,够简单吧!下面是动态调用MessageBoxA的源代码,上面的步骤被封装到RunDll32函数中,可放到模块(CallAPIbyName.bas)中:
8 K+ P* Y2 b, M6 @& P$ r$ f/ t  Dim s1() As Byte, s2() As Byte
( }5 G, {4 i& o5 T6 C1 n/ k( P% I  Dim ret As Long
. i9 Y4 n- }6 j& L4 |  s1 = StrConv("Hello~World", vbFromUnicode)9 ^7 H5 I# N- R1 Q: Q  _
  s2 = StrConv("VBNote", vbFromUnicode)
4 t; m7 I' G$ b  ret = RunDll32("user32", "MessageBoxA", hwnd, VarPtr(s1(0)), VarPtr(s2(0)), 0&)& _; D! I# T- M& K6 N
  CallAPIbyName.bas中的源代码:
3 ]( E  x5 z" l0 J% |# E! E2 q' O  Option Explicit
/ M3 Y: _; k2 X0 u% w* K+ F  Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long9 _! K/ h3 D. B6 c, i
  Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long0 Z/ b1 u9 N  c
  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
* N' S7 O  U/ b4 n8 a: B& I  Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long# D3 X2 P: W* `2 k
  Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
9 I" A, }* e( ]7 {2 ~  Public m_opIndex As Long ′写入位置- F3 d* ^" n$ n7 r
  Private m_OpCode() As Byte ′Assembly 的OPCODE
) T1 _5 Z2 _" M" L& n3 n; J  Public Function RunDll32(LibFileName As String, ProcName As String, ParamArray Params()) As Long3 G& u2 F% y$ u' A$ _7 P
  Dim hProc As Long
8 j( W7 Y1 b) G/ H0 _  Dim hModule As Long2 x' {! r- o0 H' q* a4 y" f
  ReDim m_OpCode(400 + 6 * UBound(Params)) ′保留用来写m_OpCode
+ W! O( y- s/ c, _% Q$ ^  ′读取API库, S1 u: U$ B. s# t; b" s
  hModule = LoadLibrary(ByVal LibFileName)* z2 I( b- s* g! y0 _* V
  If hModule = 0 Then' f* s0 m' Q0 f; v
  MsgBox "Library读取失败!"
9 d; d! t/ i9 c2 R8 Q- P, r# X" R  Exit Function
% A7 Q% w! |. s; V5 N  End If
! w  k0 d$ ~; k- ]! ^" W  q  ′取得函数地址/ X, a; O3 f- w7 M
  hProc = GetProcAddress(hModule, ByVal ProcName)
7 C2 f# y+ L( J; l, ^: V  If hProc = 0 Then6 ^: Y$ c# W! ^5 y( ~4 o7 v
  MsgBox "函数读取失败!", vbCritical
9 s: d( R, N1 [8 j/ a! F  FreeLibrary hModule
. ]8 }% U; |+ m* y0 \$ y  Exit Function( G: R7 U; n2 s0 x9 ]; g
  End If
( b" q& X4 Y9 j8 X; b& `  ′执行Assembly Code部分
: ?( t8 w# l( u- O  RunDll32 = CallWindowProc(GetCodeStart(hProc, Params), 0, 1, 2, 3), J6 L  d, d' p0 `; X
  FreeLibrary hModule ′释放空间# w/ d3 G# b3 c7 g. P
  End Function
$ P4 E1 j- _& F& A  Private Function GetCodeStart(ByVal lngProc As Long, ByVal arrParams As Variant) As Long
9 q1 K9 N5 |  L' F1 b& x, u; h2 B; r( f9 Y/ R( ?  }
  ′---以下为Assembly部分--
# }* H7 u! l' K8 G  w. g  ′作用:将函数的参数压入堆栈
: e% \& y8 u: I2 j. P! c! i  Dim lngIndex As Long, lngCodeStart As Long) M9 Q# ]) A7 \
  ′程序起始位址必须是16的倍数! F1 a% g: O  q: n8 [) u1 C
  ′VarPtr函数是用来取得变量的地址% C' N. b( N  Q7 P1 \" P
  lngCodeStart = (VarPtr(m_OpCode(0)) Or &HF) + 1
+ Z/ _' N3 I; H* g* t  m_opIndex = lngCodeStart - VarPtr(m_OpCode(0)) ′程序开始的元素的位置
! Y8 @2 e8 I! h$ A8 Y8 ]7 y9 w  ′前面部分以中断点添满' D% K* U9 ?9 ~6 l8 D; K* E- S
  For lngIndex = 0 To m_opIndex - 1
$ c! Y/ b! d. O  m_OpCode(lngIndex) = &HCC ′int 3' t$ }6 h7 t0 q: U9 g% ?& A1 a
  Next lngIndex) g( g7 Y$ _% a2 g
  ′--------以下开始放入所需的程序----------
% Q" t: c+ U* q  ′将参数push到堆栈
* y6 c9 [: n9 d" i  `  ′由于是STDCall CALL 参数由最后一个开始放到堆栈
7 u: L& ?5 G, J  W. u6 r  For lngIndex = UBound(arrParams) To 0 Step -1
, G1 s& c  Y% \  AddByteToCode &H68 ′push的机器码为H68
' E& h3 S& Z+ r( y1 e  AddLongToCode CLng(arrParams(lngIndex)) ′参数地址
$ G2 X. K" `: {  M/ _# z  Next lngIndex
0 d4 f- x& k1 j; `& w  ′call hProc
& W" {: s' z% p* x) O  AddByteToCode &HE8 ′call的机器码为HE8
* Q% g, F1 n' n& w; m# F$ q7 A% r" y3 n  AddLongToCode lngProc - VarPtr(m_OpCode(m_opIndex)) - 4 ′函数地址 用call的定址
回复

使用道具 举报

 楼主| 发表于 2012-7-31 22:10:13 | 显示全部楼层

2011年计算机二级VB辅导知识总结(7)

  ′-----------结束所需的程序--------------
) n- u) ^. q/ E$ k2 G" x- R7 s  ′返回呼叫函數) M: h; ]7 M; W7 V1 C
  AddByteToCode &HC2 ′ret 10h
) N' P5 B2 g/ P  AddByteToCode &H10
$ R9 c0 ?4 e3 u; T) {  AddByteToCode &H0
8 J7 T, J4 t+ M+ {  GetCodeStart = lngCodeStart
" G% K1 X; Q& L( V9 u) T% b3 V2 r  End Function
3 m" I5 {. N0 A# M3 B$ k  M  Private Sub AddLongToCode(lData As Long)
3 ~$ O- i8 V8 p1 P  ′将Long类型的参数写到m_OpCode中
* ?& Q4 `6 w- S. w' H2 |) Y; n  CopyMemory m_OpCode(m_opIndex), lData, 4
. m6 ]! {0 e! i9 w+ u  m_opIndex = m_opIndex + 4$ K+ l9 Z: G# q% G$ }- f
  End Sub
. R& _) v( S" ^  Z2 x  Private Sub AddIntToCode(iData As Byte)$ z! \6 W* w( v7 M) G$ {( y( M
  ′将Integer类型的参数写道m_OpCode中! s4 a; R# ]# @$ f% k/ T6 Z5 F
  CopyMemory m_OpCode(m_opIndex), iData, 2
7 u5 R9 C7 t* ?# X# [* _& ^5 D3 N" I  m_opIndex = m_opIndex + 2
9 i; M# h* E5 c" J  i: V& x  End Sub7 f4 W6 b: A/ M1 A/ I& ^  g; d
  Private Sub AddByteToCode(bData As Byte)
& S; x. L8 n2 n% W, @  ′将Byte类型的参数写道m_OpCode中" J9 `8 @0 |( P) k6 X, V
  m_OpCode(m_opIndex) = bData! e5 u% q$ D. K0 C8 d
  m_opIndex = m_opIndex + 1& Q' ?4 R8 O) x
  End Sub</p>
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-5-18 08:22 , Processed in 0.758410 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表