VB的使用

1、今天講解VB的使用,明天講解VC與VB的相互調用:c++

 1.指針是什麼? 
   不須要去找什麼標準的定義,它就是一個32位整數,在C語言和在VB裏均可以用Long類型來表示。在32位Windows平臺下它和普通的32位長整型數沒有什麼不一樣,只不過它程序員

的值是一個內存地址,正是由於這個整數象針同樣指向一個內存地址,因此就有了指針的概念。 編程

    有統計代表,很大一部分程序缺陷和內存的錯誤訪問有關。正是由於指針直接和內存打交道,因此指針一直以來被當作一個危險的東西。以致於很多語言,如著名的JAVA,小程序

都不提供對指針操做的支持,全部的內存訪問方面的處理都由編譯器來完成。而像C和C++,指針的使用則是基本功,指針給了程序員極大的自由去爲所欲爲地處理內存訪問,數組

不少很是巧妙的東西都要依靠指針技術來完成。 安全

 注意:在VB裏,官方是不鼓勵使用什麼指針的,本文所講的任何東西你都別期望取得官方的技術支持,一切都要靠咱們本身的努力,一切都更刺激! 網絡

 2.來看看指針能作什麼?有什麼用? 數據結構

先來看兩個程序,程序的功能都是交換兩個字串: 函數

【程序一】:註釋:標準的作法SwapStr 工具

Sub SwapStr(sA As String, sB As String) 
        Dim sTmp As String 
        sTmp = sA: sA = sB: sB = sTmp 
End Sub 

【程序二】:註釋:用指針的作法SwapPtr 

    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _  (Destination As Any, Source As Any, ByVal Length As Long) 
    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(調用API須要額外的處理,VB文檔明確指出大量調用API將下降程序性能)。但事實上,在VB集成環境中運
行,程序二要比程序一快四分之一;而編譯成本機代碼或p-code,程序二基本上要比程序一快一倍。下面是兩個函數在編譯成本機代碼後,運行不一樣次數所花時間的比較: 
運行100000次,SwapStr須要170毫秒,SwapPtr須要90毫秒。 
運行200000次,SwapStr須要340毫秒,SwapPtr須要170毫秒。 
運行2000000次,SwapStr須要3300毫秒,SwapPtr須要1500毫秒。 
   的確,調用API是須要額外指令來處理,可是因爲使用了指針技術,它沒有進行臨時字串的分配和拷貝,所以速度提升了很多。 
  怎麼樣,想不到吧!C/C++程序員那麼依賴指針,無非也是由於使用指針每每能更直接的去處理問題的根源,更有駕馭一切的快感。他們不是不知道使用指針的危險,他們不是不肯意開衛星定位無級變速的汽車,只是騎摩托更有快感,而有些地方只有摩托才走得過去。 
    和在C裏相似,在VB裏咱們使用指針也不過三個理由: 
    一是效率,這是一種態度一種追求,在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 
    Sub TestCopyMemory() 
        Dim k As Long 
        k = 5 
Note:   CopyMemory ByVal VarPtr(k), 40000, 4 
        Debug.Print k 
    End Sub 
上面標號Note處的語句的目的,是將k賦值爲40000,等同於語句k=40000,你能夠在"當即"窗口試驗一下,會發現k的值的確成了40000。 
    實際上上面這個語句,翻譯成白話,就是從保存常數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的值並無變。 
    咱們能夠把程序改改以更清楚的休現這種區別,以下面的程序四: 
【程序四】:註釋:看看咱們的東西被拷貝到哪兒去了  
    Sub TestCopyMemory() 
        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 
程序輸出: 

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程序包括了大部分基本的指針指針操做: 
    struct POINT{ 
        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--;  
    } 
這些對指針操做在VB裏都有等同的東西, 
    前面討論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同樣,各類指針。 
    Type POINT 
        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 
        FnPtrToLong = lngFnPtr 
  End Function 

    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不直接支持指針操做,在VB裏用指針取值和用指針賦值都必須用CopyMemory這個API,而調用API的代價是比較高的,這就決定了咱們在VB裏使用指針不能象在C裏那樣自由和頻繁,咱們必需要考慮指針操做的代價,在後面的"指針應用"咱們會再變談這個問題。 
    程序五中關於函數指針的問題請參考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,明確的使用指針! 
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long) 

    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 
    注意,上面CopyMemory的聲明,用的是ByVal和long,要求傳遞的是32位的地址值,當咱們將一個別的類型傳遞給這個API時,編譯器會報錯,好比如今咱們用下面的語句: 
【程序六】:註釋:有點象【程序四】,但將常量40000換成了值爲1的變量. 
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long,  Length As Long) 
    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調用中都不會用到它們。   
    另:本文第三節曾提出一個小測驗題,作出來了嗎?如今公佈正確答案: 
   【測驗題答案】 
    Function ObjPtr(obj as Object) as long 
Dim lpObj As Long 
        CopyMemory lpObj, Obj, 4 
        ObjectPtr = lpObj 
    End Function     
5.VB指針應用 
    如前面所說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型提取出來, 
【程序七】 註釋:標準的方法,也是高效的方法,但不容易理解。
     Function LoWord(ByVal dw As Long) As Integer     
        If dw And &H8000& Then         
            LoWord = dw Or &HFFFF0000     
        Else         
            LoWord = dw And &HFFFF&     
        End If 
     End Function
【程序八】    註釋:用指針來作效率雖不高,但思想清楚。 
     Function LoWord(ByVal dw As Long) As Integer     
         CopyMemory ByVal VarPtr(LoWord), ByVal VarPtr(dw), 2 
     End Function 
     3)、對數組進行批量操做 
     
用指針進行大批量數組數據的移動,從效率上考慮是頗有必要的,看下面的兩個程序,它們功能都是將數組的前一半數據移到後一半中: 
【程序九】:註釋:標準的移動數組的作法 
    Private Sub ShitArray(ab() As MyType) 
        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 
【程序十】:註釋:用指針的作法 
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ 
           (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 
    當數組較大,移動操做較多(好比用數組實現HashTable)時程序十比程序九性能上要好得多。 
    程序十中又介紹兩個在指針操做中會用到的API: ZeroMemory是用來將內存清零;FillMemory用同一個字節來填充內存。固然,這兩個API的功能,也徹底能夠用CopyMemory來完成。象在C裏同樣,做爲一個好習慣,在VB裏咱們也能夠明確的用ZeroMemory來對數組進行初始化,用FillMemory在不當即使用的內存中填入怪值,這有利於調試。 
    4)、最後的一點 
    固然,VB指針的應用決不止這些,還有什麼應用就要靠本身去摸索了。對於對象指針和字符串指針的應用我會另寫文章來談,作爲本文的結束和下一篇文章《VB字符串全攻略》的開始,我在這裏給出交換兩個字符串的最快的方法: 
   
【程序十一】註釋:交換兩個字符串最快的方法
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)  
    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
2、總結

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解決工做上的問題,達到提升工做效率目標,從而越學越有興趣。

                                改變本身,從如今作起-----------久館          

相關文章
相關標籤/搜索