透過使用型別庫提高VB呼叫DLL函式的效能 (轉)

gugu99發表於2008-01-09
透過使用型別庫提高VB呼叫DLL函式的效能 (轉)[@more@]

 
本文始發於水木清華BBS(smth.org),轉載請保留有關資訊,謝謝! 


透過使用型別庫提高VBDLL的


RoachCock@smth 
to:chen3feng@163.com">mailto:chen3feng@163.com,
,
.com">chen3fengx@hotmail.com
 
VB雖然是個很好玩的東西,但是自身功能有限,限制了VB在某些方面的應用.
MS提供了兩種兩種方式來增強VB的功能,一種是呼叫COM,另一種就是呼叫Dll函式
 
說到呼叫COM,大家都很熟悉,在Project->Reference或者Project->Component
選單彈出對話方塊中選擇需要的型別庫或者,就可以把它加入Project.
 
呼叫Dll函式也很簡單,需要使用Declare函式宣告先:
Declare Function MessageBox Lib "user32" Alias "MessageBoxA" ( _
ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, _
ByVal wType As Long) As Long
 
然後就可以像內部函式一樣呼叫了:
MessageBox Me.hWnd, "hehe", "Test", 0
 
下面我分析一下在上面的呼叫過程中,到底發生了什麼.
 
我們知道,VB的字串是Unicode字串,每個字元佔兩個位元組,但是翻開WinUser.h
我們可以看到MessageBoxA的真實定義,
WINUSER int WINAPI MessageBoxA(HWND hWnd ,LPCSTR lpText,
 LPCSTR lpCaption, UINT uType);
 
LPCSTR是一個const char *型別,是一個指向單位元組ANSI字串的指標,
把呼叫這個函式的Project編譯成exe,,用VC帶的depends.exe看到底到如何引用的函式
 
竟然除了MSVBVM60.dll.什麼也沒有!
 
這中間到底發生了什麼呢?
 
透過反跟蹤生成的程式碼可以知道,VB要先呼叫__vbaStrToAnsi把字串轉化成ANSI字
符串,再把得到的Ansi字串傳給一個內部函式(一般稱為DllFunctionCall),這個函式再
真正去呼叫Dll中的函式
 
如果函式呼叫後還要使用傳給Dll函式的字串引數的話(比如GetWindowText什麼的),就
更復雜了,VB會呼叫一個叫做__vbaStrToUnicode的函式,把引數轉化成Unicode,再
傳遞給函式.
 
也許你會說,MessageBox並不會改變傳給它的引數,根本不需要在轉換成Unicode.是的,你
知道,我也知道但是VB編譯器不知道,宣告函式的Declare語句裡可沒指出引數傳遞的方向.
 
也許你會說,我的只在WinNT,,下執行,他們內部都是用的Unicode字串
,我直接呼叫Unicode版本的MessageBoxW不是更好嗎?
 
對不起,由於只基本完全實現了ANSI函式,而基本沒有實現Unicode版本的函式,而W
inNT則兩者都實現了.為了照顧Win9x,VB把宣告語句中的String都當成ANSI字串.
//需要Unicode,卻依賴於ANSI操作,可憐的VB!
 
由此,我想你已經看到了Declare語句到底在那裡影響了.好吧.讓我們開始新的歷程
!
 
[idl語言和型別庫]
型別庫對VB程式設計師來說應該很熟悉了,這裡我只簡單的介紹一下idl語言.
IDL (Interface Definition Language)是主要用於COM和的一種介面定義語言,用
來描述各種介面,只是一種描述語言而不是一種語言.IDL的風格類似於C.IDL語言不
但可以描述更多的資料型別還可以描述引數的傳遞方向,因此上面的兩個問題都不再是問
題了.
 
一般使用IDL語言都是為了描述COM介面等.其實IDL也一樣可以用於描述全域性函式.讓我們
看一個例子,
 
還舉那個MessageBox吧
 
開啟VC6,新建一個Utitity Project,名字就叫MyTlb吧
新建一個文字檔案,名字還叫MyTlb.idl吧,輸入以下內容:
 
[ // 方括號表示一個idl屬性,用來描述一些額外的資訊,要和下面的東西連起來看.
 uuid("9F7B1BC0-28C6-11d7-8F56-0080C8F0A08C") // 用VC帶的GUidgen生成一個新的
GUID,別和我的這個一樣哦
]
library MyLib // library說明了一個型別庫,MyLib是庫的名字
{
 
 [
  dllname("user32.dll") file://因為下面的模組描述了函式,所以需要dll庫的名字
 ]
 module Test // test是模組的名字
 {
  [
  entry("MessageBoxW") // entry屬性表明下面函式的入口,可以是函式的名字或者序號
  ]
  long __stdcall Message(HWND hWnd, [in]LPCWSTR msg, [in]LPCWSTR title, long
 flags);
  // __stdcall表示函式的呼叫約定,後面是參數列
  // [in]表示引數只傳入,[out][in]表示引數只傳入,[in,out]表示既傳入又傳出,類推
 
  // 對於簡單的值型別,可以省略,預設就是[in]了
  // 再有函式可以接著寫
 }
}
 
按F7編譯,一個嶄新的型別庫誕生了! // btw,直接呼叫midl編譯器也可以,不過我覺得很
多人都不喜歡命令列了, :)
 
把它引入我們的工程,不需要宣告直接就可以呼叫MessageBox了.反彙編程式碼,可以發現沒
有多餘的轉換.
 
而且因為明確的指出了引數傳遞方向,不需要假設引數會被改變,生成的程式碼會更高效,
避免了DllFunctionCall的開支,更節省時間.

 
其他函式也可以一樣處理, 98 Re Kit帶了一個Win.tlb,包含了常用Wind
ows型別,常量,以及API的定義,非常方便,完全可以擺脫Declared語句的使用.可以在
()搜到.
 
不過這個tlb是ANSI版本的(嘛),如果需要Unicode版本的,就得自己寫idl檔案編譯

 
至於其他不帶型別庫的的DLL函式,你可以找這上面的例子自己來,函式數目比較多的情況
下完全劃的來.
 
祝各位假期程式設計愉快!
 
--
 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-996974/,如需轉載,請註明出處,否則將追究法律責任。

相關文章