一、今天講解VB的使用,明天講解VC與VB的相互調用:
1.指針是什么?
不需要去找什么標准的定義,它就是一個32位整數,在C語言和在VB里都可以用Long類型來表示。在32位Windows平台下它和普通的32位長整型數沒有什么不同,只不過它
的值是一個內存地址,正是因為這個整數象針一樣指向一個內存地址,所以就有了指針的概念。
有統計表明,很大一部分程序缺陷和內存的錯誤訪問有關。正是因為指針直接和內存打交道,所以指針一直以來被看成一個危險的東西。以至於不少語言,如著名的JAVA,
都不提供對指針操作的支持,所有的內存訪問方面的處理都由編譯器來完成。而像C和C++,指針的使用則是基本功,指針給了程序員極大的自由去隨心所欲地處理內存訪問,
很多非常巧妙的東西都要依靠指針技術來完成。
注意:在VB里,官方是不鼓勵使用什么指針的,本文所講的任何東西你都別指望取得官方的技術支持,一切都要靠我們自己的努力,一切都更刺激!
2.來看看指針能做什么?有什么用?
先來看兩個程序,程序的功能都是交換兩個字串:
【程序一】:注釋:標准的做法SwapStr
Dim sTmp As String
sTmp = sA: sA = sB: sB = sTmp
End Sub
【程序二】:注釋:用指針的做法SwapPtr
Sub SwapPtr(sA As String, sB As String)
Dim lTmp As Long
CopyMemory lTmp, ByVal VarPtr(sA), 4
CopyMemory ByVal VarPtr(sA), ByVal VarPtr(sB), 4
CopyMemory ByVal VarPtr(sB), lTmp, 4
End Sub
的確,調用API是需要額外指令來處理,但是由於使用了指針技術,它沒有進行臨時字串的分配和拷貝,因此速度提高了不少。
一是效率,這是一種態度一種追求,在VB里也一樣;
二是不能不用,因為操作系統是C寫的,它時刻都在提醒我們它需要指針;
三是突破限制,VB想照料我們的一切,VB給了我們很強的類型檢查,VB像我們老媽一樣,對我們關心到有時我們會受不了,想偶爾不聽媽媽的話嗎?你需要指針!
但由於缺少官方的技術支持,在VB里,指針變得很神秘。因此在C里一些基本的技術,在VB里就變得比較困難。本文的目的就是要提供給大家一種簡單的方法,來將C處理指針的技術拿到VB里來,並告訴你什么是可行的,什么可行但必須要小心的,什么是可能但不可行的,什么是根本就不可能的。
3. 程咬金的三板斧
是的,程序二基本上就已經讓我們看到VB指針技術的模樣了。總結一下,在VB里用指針技術我們需要掌握三樣東西:CopyMemory,VarPtr/StrPtr/ObjPtr, AdressOf. 三把斧頭,程咬金的三板斧,在VB里Hack的工具。
1)、CopyMemory
關於CopyMemory和Bruce McKinney大師的傳奇,MSDN的Knowledge Base中就有文章介紹,你可以搜索"ID: Q129947"的文章。正是這位大師給32位的VB帶來了這個可以移動內存的API,也正是有了這個API,我們才能利用指針完成我們原來想都不敢想的一些工作,感謝Bruce McKinney為我們帶來了VB的指針革命。
如CopyMemory的聲明,它是定義在Kernel32.dll中的RtlMoveMemory這個API,32位C函數庫中的memcpy就是這個API的包裝,如MSDN文檔中所言,它的功能是將從Source指針所指處開始的長度為Length的內存拷貝到Destination所指的內存處。它不會管我們的程序有沒有讀寫該內存所應有的權限,一但它想讀寫被系統所保護的內存時, 我們就會得到著名的Access Violation Fault(內存越權訪問錯誤),甚至會引起更著名的general protection (GP) fault(通用保護錯誤) 。所以,在進行本系列文章里的實驗時,請注意隨時保存你的程序文件,在VB集成環境中將"工具"->"選項"中的"環境"選項卡里的"啟動程序時"設為"保存改變",並記住在"立即"窗口中執行危險代碼之前一定要保存我們的工作成果。
2)、VatPtr/StrPtr/ObjPtr
它們是VB提供給我們的好寶貝,它們是VBA函數庫中的隱藏函數。為什么要隱藏?因為VB開發小組,不鼓勵我們用指針嘛。
實際上這三個函數在VB運行時庫MSVBVM60.DLL(或MSVBVM50.DLL)中是同一個函數VarPtr(可參見我在本系列第一篇文章里介紹的方法)。
其庫型庫定義如下:
[entry("VarPtr"), hidden]
long _stdcall VarPtr([in] void* Ptr);
[entry("VarPtr"), hidden]
long _stdcall StrPtr([in] BSTR Ptr);
[entry("VarPtr"), hidden]
long _stdcall ObjPtr([in] IUnknown* Ptr);
即然它們是VB運行時庫中的同一個函數,我們也可以在VB里用API方式重新聲明這幾個函數,如下:
Private Declare Function ObjPtr Lib "MSVBVM60" Alias "VarPtr" _
(var As Object) As Long
Private Declare Function VarPtr Lib "MSVBVM60" _
(var As Any) As Long
(沒有StrPtr,是因為VB對字符串處理方式有點不同,這方面的問題太多,在本系列中另用一篇《VB字符串全攻略》來詳談。
順便提一下,聽說VB.NET里沒有這幾個函數,但只要還能調用API,我們就可以試試上面的幾個聲明,這樣在VB.NET里我們一樣可以進行指針操作。
但是請注意,如果通過API調用來使用VarPtr,整個程序二SwapPtr將比原來使用內置VarPtr函數時慢6倍。)
如果你喜歡刨根問底,那么下面就是VarPtr函數在C和匯編語言里的樣子:
在C里樣子是這樣的:
long VarPtr(void* pv){
return (long)pv;
}
所對就的匯編代碼就兩行:
moveax,dword ptr [esp+4]
ret 注釋:彈出棧里參數的值並返回。
之所以讓大家了解VarPtr的具體實現,是想告訴大家它的開銷並不大,因為它們不過兩條指令,即使加上參數賦值、壓棧和調用指令,整個獲取指針的過程也就六條指令。當然,同樣的功能在C語言里,由於語言的直接支持,僅需要一條指令即可。但在VB里,它已經算是最快的函數了,所以我們完全不用擔心使用VarPtr會讓我們失去效率!速度是使用指針技術的根本要求。
一句話,VarPtr返回的是變量所在處的內存地址,也可以說返回了指向變量內存位置的指針,它是我們在VB里處理指針最重要的武器之一。
3)、ByVal和ByRef
ByVal傳遞的參數值,而ByRef傳遞的參數的地址。在這里,我們不用去區別傳指針/傳地址/傳引用的不同,在VB里,它們根本就是一個東西的三種不同說法,即使VB的文檔里也有地方在混用這些術語(但在C++里的確要區分指針和引用)
初次接觸上面的程序二SwapPtr的朋友,一定要搞清在里面的CopyMemory調用中,在什么地方要加ByVal,什么地方不加(不加ByVal就是使用VB缺省的ByRef)
准確的理解傳值和傳地址(指針)的區別,是在VB里正確使用指針的基礎。
現在一個最簡單的實驗來看這個問題,如下面的程序三:
【程序三】:注釋:體會ByVal和ByRef
Dim k As Long
k = 5
Note: CopyMemory ByVal VarPtr(k), 40000, 4
Debug.Print k
End Sub
實際上上面這個語句,翻譯成白話,就是從保存常數40000的臨時變量處拷貝4個字節到變量k所在的內存中。
現在我們來改變一個Note處的語句,若改成下面的語句:
Note2: CopyMemory ByVal VarPtr(k), ByVal 40000, 4
這句話的意思就成了,從地址40000拷貝4個字節到變量k所在的內存中。由於地址40000所在的內存我們無權訪問,操作系統會給我們一個Access Violation內存越權訪問錯誤,告訴我們"試圖讀取位置0x00009c40處內存時出錯,該內存不能為注釋:Read注釋:"。
我們再改成如下的語句看看。
Note3: CopyMemory VarPtr(k), 40000, 4
這句話的意思就成了,從保存常數40000的臨時變量處拷貝4個字節到到保存變量k所在內存地址值的臨時變量處。這不會出出內存越權訪問錯誤,但k的值並沒有變。
我們可以把程序改改以更清楚的休現這種區別,如下面的程序四:
【程序四】:注釋:看看我們的東西被拷貝到哪兒去了
Dim i As Long, k As Long
k = 5
i = VarPtr(k)
NOTE4: CopyMemory i, 40000, 4
Debug.Print k
Debug.Print i
i = VarPtr(k)
NOTE5: CopyMemory ByVal i, 40000, 4
Debug.Print k
End Sub
5
40000
40000
由於NOTE4處使用缺省的ByVal,傳遞的是i的地址(也就是指向i的指針),所以常量40000拷貝到了變量i里,因此i的值成了40000,而k的值卻沒有變化。但是,在NOTE4前有:i=VarPtr(k),本意是要把i本身做為一個指針來使用。這時,我們必須如NOTE5那樣用ByVal來傳遞指針i,由於i是指向變量k的指針,所以最后常量40000被拷貝了變量k里。
希望你已經理解了這種區別,在后面問題的討論中,我還會再談到它。
4)、AddressOf
它用來得到一個指向VB函數入口地址的指針,不過這個指針只能傳遞給API使用,以使得API能回調VB函數。
本文不准備詳細討論函數指針,關於它的使用請參考VB文檔。
5)、拿來主義。
實際上,有了CopyMemory,VarPtr,AddressOf這三把斧頭,我們已經可以將C里基本的指針操作拿過來了。
如下面的C程序包括了大部分基本的指針指針操作:
int x; int y;
};
int Compare(void* elem1, void* elem2){}
void PtrDemo(){
//指針聲明:
char c = 注釋:X注釋:; //聲明一個char型變量
char* pc; long* pl; //聲明普通指針
POINT* pPt; //聲明結構指針
void* pv; //聲明無類型指針
int (*pfnCastToInt)(void *, void*);//聲明函數指針:
//指針賦值:
pc = &c; //將變量c的地址值賦給指針pc
pfnCompare = Compare; //函數指針賦值。
//指針取值:
c = *pc; //將指針pc所指處的內存值賦給變量c
//用指針賦值:
*pc = 注釋:Y注釋: //將注釋:Y注釋:賦給指針pc所指內存變量里。
//指針移動:
pc++; pl--;
}
前面討論ByVal和ByRef時曾說過傳指針和傳地址是一回事,實際上當我們在VB里用缺省的ByRef聲明函數參數時,我們已經就聲明了指針。
如一個C聲明的函數:long Func(char* pc)
其對應的VB聲明是:Function Func(pc As Byte) As Long
這時參數pc使用缺省的ByRef傳地址方式來傳遞,這和C里用指針來傳遞參數是一樣。
那么怎么才能象C里那樣明確地聲明一個指針呢?
很簡單,如前所說,用一個32位長整數來表達指針就行。在VB里就是用Long型來明確地聲明指針,我們不用區分是普通指針、無類型指針還是函數指針,通通都可用Long來聲明。而給一個指針賦值,就是賦給它用VarPar得到的另一個變量的地址。具體見程序五。
【程序五】:同C一樣,各種指針。
X As Integer
Y As Integer
End Type
Public Function Compare(elem1 As Long, elem2 As Long) As Long
注釋:
End Function
Function FnPtrToLong(ByVal lngFnPtr As Long) As Long
Sub PtrDemo()
Dim l As Long, c As Byte, ca() As Byte, Pt As POINT
Dim pl As Long, pc As Long, pv As Long, pPt As Long, pfnCompare As Long
c = AscB("X")
pl = VarPtr(l) 注釋:對應C里的long、int型指針
pc = VarPtr(c) 注釋:對應char、short型指針
pPt = VarPtr(Pt) 注釋:結構指針
pv = VarPtr(ca(0)) 注釋:字節數組指針,可對應任何類型,也就是void*
pfnCompare = FnPtrToLong(AddressOf Compare) 注釋:函數指針
CopyMemory c, ByVal pc, LenB(c) 注釋:用指針取值
CopyMemory ByVal pc, AscB("Y"), LenB(c) 注釋:用指針賦值
pc = pc + LenB(c) : pl = pl - LenB(l) 注釋:指針移動
End Sub
程序五中關於函數指針的問題請參考VB文檔,無類型指針void*會在下面"關於Any的問題"里說。
程序五基本上已經包括了我們能在VB里進行的所有指針操作,僅此而已。
下面有一個小測試題,如果現在你就弄懂了上面程咬金的三板斧,你就應該能做得出來。
上面提到過,VB.NET中沒有VarPtr,我們可以用聲明API的方式來引入MSVBVM60.DLL中的VarPtr。現在的問題如果不用VB的運行時DLL文件,你能不能自己實現一個ObjPtr。答案在下一節后給出。
4.指針使用中應注意的問題
1)、關於ANY的問題
如果以一個老師的身份來說話,我會說:最好永遠也不要用Any!是的,我沒說錯,是永遠!所以我沒有把它放在程咬金的三板斧里。當然,這個問題和是不是應該使用指針這個問題一樣會引發一場沒有結果的討論,我告訴你的只是一個觀點,因為有時我們會為了效率上的一點點提高或想偷一點點懶而去用Any,但這樣做需要要承擔風險。
Any不是一個真正的類型,它只是告訴VB編譯器放棄對參數類型的檢查,這樣,理論上,我們可以將任何類型傳遞給API。
Any在什么地方用呢?讓我們來看看,在VB文檔里的是怎么說的,現在就請打開MSDN(Visual Studio 6自帶的版本),翻到"Visual Basic文檔"->"使用Visual Basic"->"部件工具指南"->"訪問DLL和Windows API"部分,再看看"將 C 語言聲明轉換為 Visual Basic 聲明"這一節。文檔里告訴我們,只有C的聲明為LPVOID和NULL時,我們才用Any。實際上如果你願意承擔風險,所有的類型你都可以用Any。當然,也可以如我所說,永遠不要用Any。
為什么要這樣?那為什么VB官方還要提供Any?是信我的,還是信VB官方的?有什么道理不用Any?
如前面所說,VB官方不鼓勵我們使用指針。因為VB所標榜的優點之一,就是沒有危險的指針操作,所以的內存訪問都是受VB運行時庫控制的。在這一點上,JAVA語言也有着同樣的標榜。但是,同JAVA一樣,VB要避免使用指針而得到更高的安全性,就必須要克服沒有指針而帶來的問題。VB已經盡最大的努力來使我們遠離指針的同時擁有強類型檢查帶來的安全性。但是操作系統是C寫的,里面到處都需要指針,有些指針是沒有類型的,就是C程序員常說的可怕的void*無類型指針。它沒有類型,因此它可以表示所有類型。如CopyMemory所對應的是C語言的memcpy,它的聲明如下:
void *memcpy( void *dest, const void *src, size_t count );
因memcpy前兩個參數用的是void*,因此任何類型的參數都可以傳遞給他。
一個用C的程序員,應該知道在C函數庫里這樣的void*並不少見,也應該知道它有多危險。無論傳遞什么類型的變量指針給上面memcpy的void*,C編譯器都不會報錯或給任何警告。
在VB里大多數時候,我們使用Any就是為了使用void*,和在C里一樣,VB也不對Any進行類型檢查,我們也可以傳遞任何類型給Any,VB編譯器也都不會報錯或給任何警告。
但程序運行時會不會出錯,就要看使用它時是不是小心了。正因為在C里很多錯誤是和void*相關的,所以,C++鼓勵我們使用satic_cast<void*>來明確指出這種不安全的類型的轉換,已利於發現錯誤。
說了這么多C/C++,其實我是想告訴所有VB的程序員,在使用Any時,我們必須和C/C++程序員使用void*一樣要高度小心。
VB里沒有satic_cast這種東西,但我們可以在傳遞指針時明確的使用long類型,並且用VarPtr來取得參數的指針,這樣至少已經明確地指出我們在使用危險的指針。如程序二經過這樣的處理就成了下面的程序:
【程序五】:注釋:使用更安全的CopyMemory,明確的使用指針!
Sub SwapStrPtr2(sA As String, sB As String)
Dim lTmp As Long
Dim pTmp As Long, psA As Long, psB As Long
pTmp = VarPtr(lTmp): psA = VarPtr(sA): psB = VarPtr(sB)
CopyMemory pTmp, psA, 4
CopyMemory psA, psB, 4
CopyMemory psB, pTmp, 4
End Sub
【程序六】:注釋:有點象【程序四】,但將常量40000換成了值為1的變量.
Sub TestCopyMemory()
Dim i As Long,k As Long, z As Interger
k = 5 : z = 1
i = VarPtr(k)
注釋:下面的語句會引起類型不符的編譯錯誤,這是好事!
注釋:CopyMemory i, z, 4
注釋:應該用下面的
CopyMemory i, ByVal VarPtr(z), 2
Debug.Print k
End Sub
象程序四那樣使用Any類型來聲明CopyMemory的參數,VB雖然不會報錯,但運行時結果卻是錯的。不信,你試試將程序四中的40000改為1,結果i的值不是我們想要的1,而是327681。為什么在程序四中,常量為1時結果會出錯,而常量為40000時結果就不錯?
原因是VB對函數參數中的常量按Variant的方式處理。是1時,由於1小於Integer型的最大值32767,VB會生成一個存儲值1的Integer型的臨時變量,也就是說,當我們想將1用CopyMemroy拷貝到Long型的變量i時,這個常量1是實際上是Integer型臨時變量!VB里Integer類型只有兩個字節,而我們實際上拷貝了四個字節。知道有多危險了吧!沒有出內存保護錯誤那只是我們的幸運!
如果一定要解釋一下為什么i最后變成了327681,這是因為我們將k的低16位的值5也拷貝到了i值的高16位中去了,因此有5*65536+1=327681。詳談這個問題涉及到VB局部變量聲明順序,CopyMemory參數的壓棧順序,long型的低位在前高位在后等問題。如果你對這些問題感興趣,可以用本系列第一篇文章所提供的方法(DebugBreak這個API和VC調試器)來跟蹤一下,可以加深你對VB內部處理方式的認識,由於這和本文討論的問題無關,所以就不詳談了。到這里,大家應該明白,程序三和程序四實際上有錯誤!!!我在上面用常量40000而不用1,不是為了在文章中湊字數,而是因為40000這個常量大於32767,會被VB解釋成我們需要的Long型的臨時變量,只有這樣程序三和程序四才能正常工作。對不起,我這樣有意的隱藏錯誤只是想加深你對Any危害的認識。
總之,我們要認識到,編譯時就找到錯誤是非常重要的,因為你馬上就知道錯誤的所在。所以我們應該象程序五和程序六那樣明確地用long型的ByVal的指針,而不要用Any的ByRef的指針。
但用Any已經如此的流行,以至很多大師們也用它。它唯一的魅力就是不象用Long型指針那樣,需要我們自己調用VarPtr來得到指針,所有處理指針的工作由VB編譯器來完成。所以在參數的處理上,只用一條匯編指令:push ,而用VarPtr時,由於需要函數調用,因此要多用五條匯編指令。五條多余的匯編指令有時的確能我們冒着風險去用Any。
VB開發小組提供Any,就是想用ByRef xxx As Any來表達void* xxx。我們也完全可以使用VarPtr和Long型的指針來處理。我想,VB開發小組也曾猶豫過是公布VarPtr,還是提供Any,最后他們決定還是提供Any,而繼續隱瞞VarPtr。的確,這是個兩難的決定。但是經過我上面的分析,我們應該知道,這個決定並不符合VB所追求的"更安全"的初衷。因為它可能會隱藏類型不符的錯誤,調試和找到這種運行時才產生的錯誤將花貴更多的時間和精力。
所以我有了"最好永遠不要用Any"這個"驚人"的結論。
不用Any的另一個好處是,簡化了我們將C聲明的API轉換成VB聲明的方式,現在它變成了一句話:除了VB內置的可以進行類型檢查的類型外,所以其它的類型我們都應該聲明成Long型。
2)、關於NULL的容易混淆的問題
有很多文章講過,一定要記在心里:
VbNullChar 相當於C里的注釋:\0注釋:,在用字節數組構造C字串時常用它來做最后1個元素。
vbNullString 這才是真正的NULL,就是0,在VB6中直接用0也可以。
只有上面的兩個是API調用中會用的。還有Empty、Null是Variant,而Nothing只和類對象有關,一般API調用中都不會用到它們。
另:本文第三節曾提出一個小測驗題,做出來了嗎?現在公布正確答案:
【測驗題答案】
Dim lpObj As Long
CopyMemory lpObj, Obj, 4
ObjectPtr = lpObj
End Function
如前面所說VB里使用指針不象C里那樣靈活,用指針處理數據時都需要用CopyMemory將數據在指針和VB能夠處理的變量之間來回拷貝,這需要很大的額外開銷。因此不是所有C里的指針操作都可以移值到VB里來,我們只應在需要的時候才在VB里使用指針。
1)、動態內存分配:完全不可能、可能但不可行,VB標准
在C和C++里頻繁使用指針的一個重要原因是需要使用動態內存分配,用Malloc或New來從堆棧里動態分配內存,並得到指向這個內存的指針。在VB里我們也可以自己
用API來實現動態分配內存,並且實現象C里的指針鏈表。
但我們不可能象C那樣直接用指針來訪問這樣動態分配的內存,訪問時我們必須用CopyMemory將數據拷貝到VB的變量內,大量的使用這種技術必然會降低效率,以至於要象C那樣用指針來使用動態內存根本就沒有可行性。要象C、PASCAL那樣實現動態數據結構,在VB里還是應該老老實實用對象技術來實現。
本文配套代碼中的LinkedList里有完全用指針實現的鏈表,它是使用HeapAlloc從堆棧中動態分配內存,另有一個調用FindFirstUrlCacheEntry這個API來操作IE的Cache的小程序IECache,它使用了VirtualAlloc來動態分配內存。但實際上這都不是必須的,VB已經為我們提供了標准的動態內存分配的方法,那就是:
對象、字符串和字節數組
限於篇幅,關於對象的技術這里不講,LinkedList的源代碼里有用對象實現的鏈表,你可以參考。
字符串可以用Space$函數來動態分配,VB的文檔里就有詳細的說明。
關於字節數組,這里要講講,它非常有用。我們可用Redim來動態改變它的大小,並將指向它第一個元素的指針傳給需要指針的API,如下:
dim ab() As Byte , ret As long
注釋:傳遞Null值API會返回它所需要的緩沖區的長度。
ret = SomeApiNeedsBuffer(vbNullString)
注釋:動態分配足夠大小的內存緩沖區
ReDim ab(ret) As Byte
注釋:再次把指針傳給API,此時傳字節數組第一個元素的指針。
SomeApiNeedsBuffer(ByVal VarPtr(ab(1)))
在本文配套程序中的IECache中,我也提供了用字節數組來實現動態分配緩沖區的版本,比用VirtualAlloc來實現更安全更簡單。
2)、突破限制
下面是一個突破VB類型檢查來實現特殊功能的經典應用,出自Bruce Mckinney的《HardCore Visual Basic》一書。
將一個Long長整數的低16位作為Interger型提取出來,
【程序七】 注釋:標准的方法,也是高效的方法,但不容易理解。
If dw And &H8000& Then
LoWord = dw Or &HFFFF0000
Else
LoWord = dw And &HFFFF&
End If
End Function
CopyMemory ByVal VarPtr(LoWord), ByVal VarPtr(dw), 2
End Function
用指針進行大批量數組數據的移動,從效率上考慮是很有必要的,看下面的兩個程序,它們功能都是將數組的前一半數據移到后一半中:
【程序九】:注釋:標准的移動數組的做法
Dim i As Long, n As Long
n = CLng(UBound(ab) / 2)
For i = 1 To n
Value(n + i) = Value(i)
Value(i).data = 0
Next
End Sub
(ByVal dest As Long, ByVal source As Long, ByVal bytes As Long)
Private Declare Sub ZeroMemory Lib "kernel32" Alias "RtlZeroMemory" _
(ByVal dest As Long, ByVal numbytes As Long)
Private Declare Sub FillMemory Lib "kernel32" Alias "RtlFillMemory" _
(ByVal dest As Long, ByVal Length As Long, ByVal Fill As Byte)
Private Sub ShitArrayByPtr(ab() As MyTpye)
Dim n As Long
n = CLng(UBound(ab) / 2)
Dim nLenth As Long
nLenth = Len(Value(1))
注釋:DebugBreak
CopyMemory ByVal VarPtr(Value(1 + n)), _
ByVal VarPtr(Value(1)), n * nLenth
ZeroMemory ByVal VarPtr(Value(1)), n * nLenth
End Sub
程序十中又介紹兩個在指針操作中會用到的API: ZeroMemory是用來將內存清零;FillMemory用同一個字節來填充內存。當然,這兩個API的功能,也完全可以用CopyMemory來完成。象在C里一樣,作為一個好習慣,在VB里我們也可以明確的用ZeroMemory來對數組進行初始化,用FillMemory在不立即使用的內存中填入怪值,這有利於調試。
4)、最后的一點
當然,VB指針的應用決不止這些,還有什么應用就要靠自己去摸索了。對於對象指針和字符串指針的應用我會另寫文章來談,做為本文的結束和下一篇文章《VB字符串全攻略》的開始,我在這里給出交換兩個字符串的最快的方法:
【程序十一】注釋:交換兩個字符串最快的方法
Sub SwapStrPtr3(sA As String, sB As String)
Dim lTmp As Long
Dim pTmp As Long, psA As Long, psB As Long
pTmp = StrPtr(sA): psA = VarPtr(sA): psB = VarPtr(sB)
CopyMemory ByVal psA, ByVal psB, 4
CopyMemory ByVal psB, pTmp, 4
End Sub
a.什么是Access VBA?
Access VBA是指VBA開發人員在ACCESS[1]編程中應用VBA,通過設計VBA代碼bai來實現所需要的程序功能目標;VBA是應用程序開發語言VASUAL BASIC 的子集。
b、Access VBA與 VB的區別是什么?
1. VB是設計用於創建標准的應用程序,而VBA是使已有的應用程序(Access、Excel、Word等)自動化;
2. VB具有自己的開發環境,而VBA必須寄生於已有的應用程序;
3. 要運行VB開發的應用程序,用戶不必安裝VB,因為VB開發出的應用程序是可執行文件(*.EXE),而VBA開發的程序必須依賴於它的"父"應用程序。
c、Access開發軟件必須學好VBA
掌握了VBA,可以規范用戶的操作,控制用戶的操作行為;
掌握了VBA,可以讓操作界面人性化,方便用戶的操作;
掌握了VBA,可以將多個步驟的手工操作通過一步來實現;
掌握了VBA,可以完成一些無法實現的功能。
使用ACCESS如果不會用VBA,那么就局限於表、查詢、綁定窗體和簡單報表的簡單應用,更多地體現在自身的使用,在與同事間的數據收集、整理、分析、信息共享方面基本是處於一個初級階段。
為了能讓自已開發軟件更加得心應手、給軟件用戶更方便與人性化的操作,我們就需要學好VBA知識。
d、怎樣去學習VBA?
1.去書店購買相關的Access書籍,在購買時注意書籍的章節,要有關VBA代碼的頁數多一點的;
2.利用免費的網絡資源,例如百度搜索,通過搜索相關的關鍵詞,檢索出相關的文章;
3.訪問專業的技術網站、論壇,例如Access軟件網,該網站由上海盟威軟件有限公司[1]提供,有大量的VBA源代碼示例可供學習。
4.結合工作實踐去學習,利用VBA解決工作上的問題,達到提高工作效率目標,從而越學越有興趣。
改變自己,從現在做起-----------久館