VB 程序大揭秘
1.Visual Basic程序概況
我用W32Dasm(Ver 8.93)解開(kāi)一個(gè)比較復雜的VB程序,其中用到了許多API 函數比如GetPrivateProfileString、OSfCreateShellLink、SHBrowseForFolder 等來(lái)自很多DLL的API。解開(kāi)以后卻發(fā)現程序只用到了一個(gè)DLL:msvbvm60.dll(我用的還是VB6)!VC、Delphi等程序語(yǔ)言編譯出的程序可是直接引用DLL的。經(jīng)過(guò)研究發(fā)現程序使用了如下幾個(gè)主要的來(lái)自MSVBVM60.dll的API:
rtcRandomize :Randomize 函數的對應API;
rtcMidCharVar :Mid 函數的對應API;
rtcLeftCharVar、rtcRightCharVar :看出來(lái)了吧,這些是Left、Right函數的對應API;
rtcUpperCaseVar :UCase 函數的對應API;
rtcKillFiles :Kill 語(yǔ)句的對應API;
rtcFileCopy :FileCopy 語(yǔ)句的對應API;
rtcFileLength :EOF、FileLen函數的對應API;
rtcGetTimer :Randomize Timer中獲取Timer的對應API;
rtcShell :Shell函數的的對應API;
rtcMakeDir :MkDir 語(yǔ)句的對應API;
rtcRemoveDir :RmDir 語(yǔ)句的對應API;
rtcDir :Dir 函數的對應API;
rtcSpaceVar :Space 函數的對應API;
沒(méi)問(wèn)題的人應該看出來(lái)了:VB的所有函數、語(yǔ)句、方法都是由調用MSVBVM60.dll 中的API實(shí)現的,一般是由“rtc”接上函數或語(yǔ)句的全名,涉及字符串的API一般還得在最后加上“Var”。
另外還有一些函數是這樣寫(xiě)的:
__vbaUbound : UBound 的對應API;
__vbaFileOpen :Open 語(yǔ)句的對應API;
__vbaStrCmp :比較兩個(gè)字符串:If String1 = String2 Then ......
__vbaVarOr :Or 運算符的對應API;
__vbaRedim :Redim 語(yǔ)句的對應API;
__vbaRedimPreserve :Redim 語(yǔ)句加上 Preserve 參數的對應API;
__vbaGet、vbaPut :Get、Put語(yǔ)句的對應API……
在運行時(shí),VB程序就調用它們完成工作。
2.其它DLL的調用
第一部分解決了。我們知道了VB程序實(shí)際上不是一個(gè)真正的可執行文件,它只是機械性地調用MSVBVM60.dll中的API執行程序。那么VB程序既然只調用了MSVBVM60.dll,它又是怎樣調用其他DLL中的API呢?
注意這個(gè)API。它能引起我們的注意:
DllFunctionCall:看到了嗎?它就是我們的主角。
從字面上看就能看懂了:它用來(lái)調用其它DLL。這樣可以使程序使用的函數集中在MSVBVM60.dll里(怎么有點(diǎn)像封建制度,中央集權……)。
3.重中之重:VB程序的啟動(dòng)
我們已經(jīng)知道了VB程序的運行方法。那么它是怎樣啟動(dòng)的呢?
再看看程序調用的API。其中有一個(gè)API雷打不動(dòng),每個(gè)VB程序都有:
ThunRTMain
首先,VB程序調用ThunRTMain。ThunRTMain為程序初始化進(jìn)程,并獲取進(jìn)程ID。
隨后它加載vb6chs.dll,為打開(kāi)新窗口準備。然后它開(kāi)始用LoadString等API 獲取窗口屬性,比如字體、標題、顏色等。再調用IMM32.dll,開(kāi)始利用它打開(kāi)新窗口。然后使用GetModuleFileName獲得VB程序名,隨后用CreateSemaphore增加信號機。信號機的作用是:當監控值大于0時(shí),信號機工作。再調用OLE32.dll,使用CreateWindowEx打開(kāi)一個(gè)叫做“DDE Server”的隱藏窗口,讓它從中作梗。退出OLE32.DLL,MSVBVM60又開(kāi)始調用程序管理器。
前面的工作為我們的VB程序注冊了一個(gè)類(lèi)名:VBFocusRT5,下面就可以使用這個(gè)類(lèi)名創(chuàng )建VB窗體。首先使用大量循環(huán)讀取半角/全角字符,然后讀取各個(gè)控件的屬性,再使用Local_Function把這些屬性、方法、事件等“拼”成一個(gè)完整的控件,最后把上面做的所有工作綜合起來(lái),開(kāi)始VB程序。
從過(guò)程來(lái)看,使用時(shí)間最多的自然是加載控件了,其次是加載字符集。VB程序速度慢主要是指啟動(dòng)速度慢。這是難以避免的,希望VB7推出時(shí)能改進(jìn)這一點(diǎn)。
不知大家看沒(méi)看出來(lái),編譯后的VB程序只是源程序的翻版,連控件屬性、方法和事件名都一模一樣。VB程序的慢就是來(lái)自這里,它們只是機械地、無(wú)休止地調用MSVBVM60.dll里的API來(lái)運行程序。要想徹底擺脫這一點(diǎn),只能改革VB程序編譯時(shí)的方法,使其成為一個(gè)標準的資源性Win32程序。
附:VB程序與VC++程序啟動(dòng)速度大火拼
注意:這里提到的只是“啟動(dòng)”速度。實(shí)際上,VB程序啟動(dòng)后的運行速度與其它程序語(yǔ)言編譯出來(lái)的EXE速度差不多(甚至更快),只不過(guò)是啟動(dòng)速度太慢而已。
我們知道,Windows附帶的計算器是用VC++編制的。我編了一個(gè)示例計算器程序,流程很簡(jiǎn)單,單擊Command1時(shí)把Text1與Text2相加,再賦值到Text3。
代碼只有一行:
Private Sub Command1_Click()
Text3 = CStr(Val(Text1) + Val(Text2))
End Sub
把它編譯為EXE。為了表現出速度差異,我選擇了一臺比較慢的電腦:
Pentium 166 MMX + 80M EDO + 3.2G硬盤(pán)。
啟動(dòng)速度對比:為了結果公平,共測試五次,取平均值。
單位:秒
運行次數 VB計算器 VC++計算器
1 2.43 0.87
2 0.85 0.74
3 0.92 0.92
4 1.02 0.78
5 0.87 0.84
平均速度 1.22 0.83
你會(huì )發(fā)現,VB計算器第一次比較慢,剩下幾次就快了。這是因為T(mén)hunRTMain 把所有控件信息寫(xiě)入內存,每次打開(kāi)程序時(shí)檢測是否有可用控件信息而且符合本程序(大概比爾也知道VB慢吧)。另外,我們只能算加法的計算器啟動(dòng)速度就和功能眾多的Windows計算器差不多,更可以知道我們如果用VB編出一個(gè)和Windows計算器功能相同的計算器的啟動(dòng)速度了。:( VB也不全是缺點(diǎn),至少它的程序設計環(huán)境是其它程序語(yǔ)言所不具備或不擅長(cháng)的。
像VB這樣簡(jiǎn)單易學(xué),可以像畫(huà)圖一樣構造程序界面的程序語(yǔ)言可以說(shuō)只有VB 一個(gè),它為編程初學(xué)者指明了方向。VB是有它存在的理由的,至少,我SuperAPI還在用它。:)
后記:
寫(xiě)這篇文章的靈感來(lái)自于兩天前VB論壇里cy72提出的問(wèn)題。昨天半夜沒(méi)上網(wǎng),集中精力調試一個(gè)VB程序,終于找出了答案。在DLL里轉來(lái)轉去的感覺(jué)真的很難受,加之我對匯編還不太懂。以每秒3條語(yǔ)句的速度進(jìn)行,調試了49218 步,共用了4個(gè)半小時(shí)。我自從接觸VB以來(lái)從沒(méi)感覺(jué)過(guò)VB程序是這樣復雜。
尤其值得一提的是,4個(gè)半小時(shí)中4個(gè)小時(shí)是泡在近似無(wú)限的循環(huán)中,這種長(cháng)時(shí)間重復一件枯燥而乏味的事情我可總算是見(jiàn)識到了,各位調試VB程序時(shí)大可不必心煩意亂,你只要想想長(cháng)時(shí)間按著(zhù)F7、F5鍵,在迎面撲來(lái)的一堆堆成山的天書(shū)般的匯編語(yǔ)言中尋找有用東西的滋味你就知道調試VB程序是最簡(jiǎn)單的了
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。