VB動態呼叫外部函式的方法

2021-04-02 21:14:12 字數 4384 閱讀 5919

vb可以用declare宣告來呼叫標準dll的外部函式,但是其侷限性也很明顯:利用declare我們只能載入在設計時通過lib和alias字句指定的函式指標!而不能在執行時指定由我們自己動態載入的函式指標),不能用declare語句來呼叫任意的函式指標。當我們想動態呼叫外部函式的時候,就必須考慮採用其他的輔助方法,來完成這個任務了。

在文章《vb真是想不到系列之三:vb指標葵花寶典之函式指標 》、《matthew curland的vb函式指標呼叫》、《利用動態建立自動化介面實現vb的函式指標呼叫》等文獻中對此問題都進行了一定程度上的討論,但是頭緒都很繁瑣,對我這樣的菜鳥還有點深奧,在資料搜尋過程中,找到通過在vb中調入匯程式設計序,比較簡便的實現了這個功能,下面就是實現原理:

1)使用loadlibrary載入dll;

2)getprocaddress獲得函式指標;

以上兩步得到了預載入函式的指標,但是vb中沒有提供使用這個指標的方法。我們可以通過一段組合語言,來完成函式指標的呼叫!

3)通過組合語言,把函式的所有引數壓入堆疊,然後用call待用函式指標就可以了。

實現以上功能的主要程式:

'載入dll

libaddr = loadlibrary(byval "user32")

'獲得函式指標

procaddr = getprocaddress(libaddr, byval "messageboxa")

'原型為messagebox(hwnd, lptext, lpcaption, utype)

'---以下為assembly部分---

push utype

push lpcaption

push lptext

push hwnd

call procaddr

'--------------------

freelibrary libaddr'釋放空間

嘿,夠簡單吧!下面是動態呼叫messageboxa的源**,上面的步驟被封裝到rundll32函式中,可放到模組(callapibyname.bas)中:

dim s1() as byte, s2() as byte

dim ret as long

s1 = strconv("hello~world", vbfromunicode)

s2 = strconv("vbnote", vbfromunicode)

ret = rundll32("user32", "messageboxa", hwnd, varptr(s1(0)), varptr(s2(0)), 0&)

callapibyname.bas中的源**:

option explicit

private declare function loadlibrary lib "kernel32" alias "loadlibrarya" (byval lplibfilename as string) as long

private declare function getprocaddress lib "kernel32" (byval hmodule as long, byval lpprocname as string) 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 declare function freelibrary lib "kernel32" (byval hlibmodule as long) as long

private declare sub copymemory lib "kernel32" alias "rtlmovememory" (lpdest as any, lpsource as any, byval cbytes as long)

public m_opindex as long '寫入位置

private m_opcode() as byte  'assembly 的opcode

public function rundll32(libfilename as string, procname as string, paramarray params()) as long

dim hproc as long

dim hmodule as long

redim m_opcode(400 + 6 * ubound(params)) '保留用來寫m_opcode

'讀取api庫

hmodule = loadlibrary(byval libfilename)

if hmodule = 0 then

msgbox "library讀取失敗!"

exit function

end if

'取得函式位址

hproc = getprocaddress(hmodule, byval procname)

if hproc = 0 then

msgbox "函式讀取失敗!", vbcritical

freelibrary hmodule

exit function

end if

'執行assembly code部分

rundll32 = callwindowproc(getcodestart(hproc, params), 0, 1, 2, 3)

freelibrary hmodule '釋放空間

end function

private function getcodestart(byval lngproc as long, byval arrparams as variant) as long

'---以下為assembly部分--

'作用:將函式的引數壓入堆疊

dim lngindex as long, lngcodestart as long

'程式起始位址必須是16的倍數

'varptr函式是用來取得變數的位址

lngcodestart = (varptr(m_opcode(0)) or &hf) + 1

m_opindex = lngcodestart - varptr(m_opcode(0)) '程式開始的元素的位置

'前面部分以中斷點添滿

for lngindex = 0 to m_opindex - 1

m_opcode(lngindex) = &hcc 'int 3

next lngindex

'--------以下開始放入所需的程式----------

'將引數push到堆疊

'由於是stdcall call 引數由最後乙個開始放到堆疊

for lngindex = ubound(arrparams) to 0 step -1

addbytetocode &h68 'push的機器碼為h68

addlongtocode clng(arrparams(lngindex))  '引數位址

next lngindex

'call hproc

addbytetocode &he8 'call的機器碼為he8

addlongtocode lngproc - varptr(m_opcode(m_opindex)) - 4 '函式位址 用call的定址

'-----------結束所需的程式--------------

'返回呼叫函式

addbytetocode &hc2 'ret 10h

addbytetocode &h10

addbytetocode &h0

getcodestart = lngcodestart

end function

private sub addlongtocode(ldata as long)

'將long型別的引數寫到m_opcode中

copymemory m_opcode(m_opindex), ldata, 4

m_opindex = m_opindex + 4

end sub

private sub addinttocode(idata as byte)

'將integer型別的引數寫道m_opcode中

copymemory m_opcode(m_opindex), idata, 2

m_opindex = m_opindex + 2

end sub

private sub addbytetocode(bdata as byte)

'將byte型別的引數寫道m_opcode中

m_opcode(m_opindex) = bdata

m_opindex = m_opindex + 1

end sub

QTP呼叫外部動態庫的方法

在dll庫的函式開始要加入 extern c 這個語句告訴vc編譯器,按照c的方式進行編譯,而不是按照c 的,否則在qtp中不能識別 在qtp中的呼叫 extern.declare rettype,methodname,libname,alias argtype s example extern.d...

外部函式的呼叫

如計算下列排列函式 先建立乙個原始檔如內部函式1 includeusing namespace std int factorial int n return m 這個被呼叫的函式的主函式不是int main 而是自己定義的函式 在建立乙個內部函式2 includeusing namespace st...

C 呼叫VB動態庫方式

首先把vb動態庫引用到bin裡,然後寫乙個類,領進動態庫方法,接著在程式裡呼叫,如下所示 using system using system.collections.generic using system.text using system.runtime.interopservices name...