|
<font face="Verdana">
<p><font face="Verdana">'VB动态调用外部函数的方法<br/>'VB可以用Declare声明来调用标准DLL的外部函数,但是其局限性也很明显:利用Declare我们只能载入在设计时通过Lib和Alias字句指定的函数指针!而不能在运行时指定由我们自己动态载入的函数指针),不能用Declare语句来调用任意的函数指针。当我们想动态调用外部函数的时候,就必须考虑采用其他的辅助方法,来完成这个任务了。<br/>'<br/>'在文章《VB真是想不到系列之三:VB指针葵花宝典之函数指针 》、《Matthew Curland的VB函数指针调用》、《利用动态创建自动化接口实现VB的函数指针调用》等文献中对此问题都进行了一定程度上的讨论,但是头绪都很繁琐,对我这样的菜鸟还有点深奥,在资料搜索过程中,找到通过在VB中调入汇编程序,比较简便的实现了这个功能,下面就是实现原理:<br/>'<br/>'1)使用LoadLibrary加载DLL;<br/>'2)GetProcAddress获得函数指针;<br/>'<br/>'以上两步得到了预加载函数的指针,但是VB中没有提供使用这个指针的方法。我们可以通过一段汇编语言,来完成函数指针的调用!<br/>'<br/>'3)通过汇编语言,把函数的所有参数压入堆栈,然后用Call待用函数指针就可以了。</font></p>
<p><font face="Verdana">'实现以上功能的主要程序:<br/>'加载Dll<br/>Private Sub Form_Load()<br/> ' LibAddr = LoadLibrary(ByVal "user32")<br/> ' '获得函数指针<br/> ' ProcAddr = GetProcAddress(LibAddr, ByVal "MessageBoxA")<br/> ' '原型为MessageBox(hWnd, lpText, lpCaption, uType)<br/> '<br/> ' '---以下为Assembly部分---<br/> ' 'push uType<br/> ' 'push lpCaption<br/> ' 'push lpText<br/> ' 'push hWnd<br/> ' 'Call ProcAddr<br/> ' '--------------------<br/> ' FreeLibrary LibAddr '释放空间</font></p>
<p><font face="Verdana"> '嘿,够简单吧!下面是动态调用MessageBoxA的源代码,上面的步骤被封装到RunDll32函数中,可放到模块(CallAPIbyName.bas)中:<br/>Dim s1() As Byte, s2() As Byte<br/>Dim ret As Long<br/> s1 = StrConv("Hello~World", vbFromUnicode)<br/> s2 = StrConv("VBNote", vbFromUnicode)<br/> ret = RunDll32("user32", "MessageBoxA", hWnd, VarPtr(s1(0)), VarPtr(s2(0)), 0&)<br/>End Sub</font></p>
<p><font face="Verdana">'CallAPIbyName.bas中的源代码:</font></p>
<p><font face="Verdana">Option Explicit</font></p>
<p><font face="Verdana">Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long<br/>Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long<br/>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<br/>Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long<br/>Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)</font></p>
<p><font face="Verdana">Public m_opIndex As Long '写入位置<br/>Private m_OpCode() As Byte 'Assembly 的OPCODE</font></p>
<p><font face="Verdana">Public Function RunDll32(LibFileName As String, ProcName As String, ParamArray Params()) As Long<br/>Dim hProc As Long<br/>Dim hModule As Long</font></p>
<p><font face="Verdana"> ReDim m_OpCode(400 + 6 * UBound(Params)) '保留用来写m_OpCode<br/> '读取API库<br/> hModule = LoadLibrary(ByVal LibFileName)<br/> If hModule = 0 Then<br/> MsgBox "Library读取失败!"<br/> Exit Function<br/> End If</font></p>
<p><font face="Verdana"> '取得函数地址<br/> hProc = GetProcAddress(hModule, ByVal ProcName)<br/> If hProc = 0 Then<br/> MsgBox "函数读取失败!", vbCritical<br/> FreeLibrary hModule<br/> Exit Function<br/> End If</font></p>
<p><font face="Verdana"> '执行Assembly Code部分<br/> RunDll32 = CallWindowProc(GetCodeStart(hProc, Params), 0, 1, 2, 3)<br/> FreeLibrary hModule '释放空间<br/>End Function</font></p>
<p><font face="Verdana">Private Function GetCodeStart(ByVal lngProc As Long, ByVal arrParams As Variant) As Long<br/> '---以下为Assembly部分--<br/> '作用:将函数的参数压入堆栈</font></p>
<p><font face="Verdana">Dim lngIndex As Long, lngCodeStart As Long</font></p>
<p><font face="Verdana"> '程序起始位址必须是16的倍数<br/> 'VarPtr函数是用来取得变量的地址<br/> lngCodeStart = (VarPtr(m_OpCode(0)) Or &HF) + 1</font></p>
<p><font face="Verdana"> m_opIndex = lngCodeStart - VarPtr(m_OpCode(0)) '程序开始的元素的位置</font></p>
<p><font face="Verdana"> '前面部分以中断点添满<br/> For lngIndex = 0 To m_opIndex - 1<br/> m_OpCode(lngIndex) = &HCC 'int 3<br/> Next lngIndex</font></p>
<p><font face="Verdana"> '--------以下开始放入所需的程序----------</font></p>
<p><font face="Verdana"> '将参数push到堆栈<br/> '由于是STDCall CALL 参数由最后一个开始放到堆栈<br/> For lngIndex = UBound(arrParams) To 0 Step -1<br/> AddByteToCode &H68 'push的机器码为H68<br/> AddLongToCode CLng(arrParams(lngIndex)) '参数地址<br/> Next lngIndex</font></p>
<p><font face="Verdana"> 'call hProc<br/> AddByteToCode &HE8 'call的机器码为HE8<br/> AddLongToCode lngProc - VarPtr(m_OpCode(m_opIndex)) - 4 '函数地址 用call的定址</font></p>
<p><font face="Verdana"> '-----------结束所需的程序--------------</font></p>
<p><font face="Verdana"> '返回呼叫函數<br/> AddByteToCode &HC2 'ret 10h<br/> AddByteToCode &H10<br/> AddByteToCode &H0</font></p>
<p><font face="Verdana"> GetCodeStart = lngCodeStart<br/>End Function</font></p>
<p><font face="Verdana">Private Sub AddLongToCode(lData As Long)<br/> '将Long类型的参数写到m_OpCode中<br/> CopyMemory m_OpCode(m_opIndex), lData, 4<br/> m_opIndex = m_opIndex + 4<br/>End Sub</font></p>
<p><font face="Verdana">Private Sub AddIntToCode(iData As Byte)<br/> '将Integer类型的参数写道m_OpCode中<br/> CopyMemory m_OpCode(m_opIndex), iData, 2<br/> m_opIndex = m_opIndex + 2<br/>End Sub</font></p>
<p><font face="Verdana">Private Sub AddByteToCode(bData As Byte)<br/> '将Byte类型的参数写道m_OpCode中<br/> m_OpCode(m_opIndex) = bData<br/> m_opIndex = m_opIndex + 1<br/>End Sub</font></p>
<p><font face="Verdana"><br/></font> </p></font>
[此贴子已经被作者于2009-8-22 22:24:19编辑过] |
|