請(qǐng)參考:

在上蔡等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需求定制開(kāi)發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),營(yíng)銷型網(wǎng)站,外貿(mào)營(yíng)銷網(wǎng)站建設(shè),上蔡網(wǎng)站建設(shè)費(fèi)用合理。
當(dāng)我把一些舊的VB6項(xiàng)目轉(zhuǎn)變成VS2008時(shí)會(huì)出現(xiàn), “Declare”語(yǔ)句中不支持“As Any”的錯(cuò)誤說(shuō)明, 例如在National Instrument中有一個(gè)VBib-32.vb中有大量的這樣一類的語(yǔ)句:
Declare Function ibcmda32 Lib "Gpib-32.dll" Alias "ibcmda" (ByVal ud AsInteger, ByRef sstr As Any, ByVal cnt As Integer) As Integer
如何辦?
有兩種辦法:
一, 使用具體的參數(shù)
例如字符串, 就用string, 那么上面的ByRef as Any, 就寫(xiě)成ByRef as string, 或者
二, 使用特殊說(shuō)明MarshalAsAttribute
System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny)
把這段語(yǔ)句放在相應(yīng)之處, 然后把Any改成Object, 于是最上面的說(shuō)明語(yǔ)句寫(xiě)成
Declare Function ibcmda32 Lib "Gpib-32.dll" Alias "ibcmda" (ByVal ud AsInteger, System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny) ByRef sstr As Object, ByVal cnt As Integer) As Integer
對(duì)于ByVal也是一樣
Public Declare Function GetPrivateProfileString Lib "kernel32" Alias"GetPrivateProfileStringA" (ByVal lpApplicationName As String, System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.AsAny) ByVal lpKeyName As Object, ByVal lpDefault As String,ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName AsString) As Integer
MSDN官方說(shuō)明, 可以簡(jiǎn)寫(xiě)成:
Declare Sub SetData Lib "..\LIB\UnmgdLib.dll" ( ByVal x As Short, MarshalAsAttribute(UnmanagedType.AsAny)ByVal o As Object)
這種方法本人實(shí)際使用, 可行.
數(shù)學(xué)上不是有斜二測(cè)畫(huà)法,算好坐標(biāo)即可畫(huà)出
或者用AnyCAD的.Net圖形控件
也可以調(diào)用matlab 實(shí)現(xiàn)
1、vb.net ?完全符合面向?qū)ο蟮木幊陶Z(yǔ)言抽象、封裝、繼承的四大特性,而vb不支持繼承。
2、錯(cuò)誤處理不同。
vb中只是On Error.....goto和On Errer Resume Next ,這些錯(cuò)誤稱為非結(jié)構(gòu)化異常處理。而在vb.net中采用的結(jié)構(gòu)化異常處理機(jī)制,try...catch....finally控制。
3、兩者產(chǎn)生的窗體不同。
vb.net 允許創(chuàng)建不同類型的應(yīng)用程序,例如,創(chuàng)建ASP.NET和ASP.NET Web 服務(wù)應(yīng)用程序,還允許創(chuàng)建控制臺(tái)應(yīng)用程序和作為桌面服務(wù)運(yùn)行的應(yīng)用程序。但是vb 只能創(chuàng)建Windows窗體。
4、數(shù)據(jù)庫(kù)訪問(wèn)的差別。
vb6.0是通過(guò)ADO(Active X Data Objext)來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)訪問(wèn)。而vb.net 是通過(guò)ADO.NET來(lái)訪問(wèn)數(shù)據(jù)庫(kù)。
擴(kuò)展資料
Visual Basic(簡(jiǎn)稱VB)是Microsoft公司開(kāi)發(fā)的一種通用的基于對(duì)象的程序設(shè)計(jì)語(yǔ)言,為結(jié)構(gòu)化的、模塊化的、面向?qū)ο蟮摹瑓f(xié)助開(kāi)發(fā)環(huán)境的事件驅(qū)動(dòng)為機(jī)制的可視化程序設(shè)計(jì)語(yǔ)言。是一種可用于微軟自家產(chǎn)品開(kāi)發(fā)的語(yǔ)言。
“Visual” 指的是開(kāi)發(fā)圖形用戶界面 (GUI) 的方法——不需編寫(xiě)大量代碼去描述界面元素的外觀和位置,而只要把預(yù)先建立的對(duì)象add到屏幕上的一點(diǎn)即可。
“Basic”指的是 BASIC (Beginners All-Purpose Symbolic Instruction Code) 語(yǔ)言,是一種在計(jì)算技術(shù)發(fā)展歷史上應(yīng)用得最為廣泛的語(yǔ)言。
Visual Basic源自于BASIC編程語(yǔ)言。VB擁有圖形用戶界面(GUI)和快速應(yīng)用程序開(kāi)發(fā)(RAD)系統(tǒng),可以輕易的使用DAO、RDO、ADO連接數(shù)據(jù)庫(kù),或者輕松的創(chuàng)建Active?X控件,用于高效生成類型安全和面向?qū)ο蟮膽?yīng)用程序 。
參考資料:百度百科-Visual Basic
暈,眼都花了~~給你篇文章看看把,VB用指針操作內(nèi)存,就倆API
真沒(méi)想到VB也可以這樣用之指針技術(shù)
想當(dāng)年?yáng)|方不敗,黑木崖密室一戰(zhàn),僅憑一根繡花針獨(dú)戰(zhàn)四大高手,神出鬼沒(méi),堪稱天下武林第一高手。若想成為VB里的東方不敗,熟習(xí)VB《葵花寶典》,掌握VB指針技術(shù),乃是不二的法門(mén)。
欲練神功,引刀……,其實(shí)掌握VB指針技術(shù),并不需要那么痛苦。因?yàn)檎f(shuō)穿了,也就那么幾招,再勤加練習(xí),終可至神出鬼沒(méi)之境。廢話少說(shuō),讓我們先從指針的定義說(shuō)起。
一、指針是什么?
不需要去找什么標(biāo)準(zhǔn)的定義,它就是一個(gè)32位整數(shù),在C語(yǔ)言和在VB里都可以用Long類型來(lái)表示。在32位Windows平臺(tái)下它和普通的32位長(zhǎng)整型數(shù)沒(méi)有什么不同,只不過(guò)它的值是一個(gè)內(nèi)存地址,正是因?yàn)檫@個(gè)整數(shù)象針一樣指向一個(gè)內(nèi)存地址,所以就有了指針的概念。
有統(tǒng)計(jì)表明,很大一部分程序缺陷和內(nèi)存的錯(cuò)誤訪問(wèn)有關(guān)。正是因?yàn)橹羔樦苯雍蛢?nèi)存打交道,所以指針一直以來(lái)被看成一個(gè)危險(xiǎn)的東西。以至于不少語(yǔ)言,如著名的JAVA,都不提供對(duì)指針操作的支持,所有的內(nèi)存訪問(wèn)方面的處理都由編譯器來(lái)完成。而象C和C++,指針的使用則是基本功,指針給了程序員極大的自由去隨心所欲地處理內(nèi)存訪問(wèn),很多非常巧妙的東西都要依靠指針技術(shù)來(lái)完成。
關(guān)于一門(mén)高級(jí)的程序設(shè)計(jì)語(yǔ)言是不是應(yīng)該取消指針操作,關(guān)于沒(méi)有指針操作算不算一門(mén)語(yǔ)言的優(yōu)點(diǎn),我在這里不討論,因?yàn)榛ヂ?lián)網(wǎng)上關(guān)于這方面的沒(méi)有結(jié)果的討論,已經(jīng)造成了占用幾個(gè)GB的資源。無(wú)論最終你是不是要下定決心修習(xí)指針技術(shù)《葵花寶典》,了解這門(mén)功夫總是有益處的。
注意:在VB里,官方是不鼓勵(lì)使用什么指針的,本文所講的任何東西你都別指望取得官方的技術(shù)支持,一切都要靠我們自己的努力,一切都更刺激!
讓我們開(kāi)始神奇的VB指針探險(xiǎn)吧!
二、來(lái)看看指針能做什么?有什么用?
先來(lái)看兩個(gè)程序,程序的功能都是交換兩個(gè)字串:
【程序一】:
'標(biāo)準(zhǔn)的做法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
你是不是以為第一個(gè)程序要快,因?yàn)樗粗?jiǎn)單而且不用調(diào)用API(調(diào)用API需要額外的處理,VB文檔明確指出大量調(diào)用API將降低程序性能)。但事實(shí)上,在VB集成環(huán)境中運(yùn)行,程序二要比程序一快四分之一;而編譯成本機(jī)代碼或p-code,程序二基本上要比程序一快一倍。下面是兩個(gè)函數(shù)在編譯成本機(jī)代碼后,運(yùn)行不同次數(shù)所花時(shí)間的比較:
運(yùn)行100000次,SwapStr需要170毫秒,SwapPtr需要90毫秒。
運(yùn)行200000次,SwapStr需要340毫秒,SwapPtr需要170毫秒。
運(yùn)行2000000次,SwapStr需要3300毫秒,SwapPtr需要1500毫秒。
的確,調(diào)用API是需要額外指令來(lái)處理,但是由于使用了指針技術(shù),它沒(méi)有進(jìn)行臨時(shí)字串的分配和拷貝,因此速度提高了不少。
怎么樣,想不到吧!C/C++程序員那么依賴指針,無(wú)非也是因?yàn)槭褂弥羔樛芨苯拥娜ヌ幚韱?wèn)題的根源,更有駕馭一切的快感。他們不是不知道使用指針的危險(xiǎn),他們不是不愿意開(kāi)衛(wèi)星定位無(wú)級(jí)變速的汽車(chē),只是騎摩托更有快感,而有些地方只有摩托才走得過(guò)去。
和在C里類似,在VB里我們使用指針也不過(guò)三個(gè)理由:
一是效率,這是一種態(tài)度一種追求,在VB里也一樣;
二是不能不用,因?yàn)椴僮飨到y(tǒng)是C寫(xiě)的,它時(shí)刻都在提醒我們它需要指針;
三是突破限制,VB想照料我們的一切,VB給了我們很強(qiáng)的類型檢查,VB象我們老媽一樣,對(duì)我們關(guān)心到有時(shí)我們會(huì)受不了,想偶爾不聽(tīng)媽媽的話嗎?你需要指針!
但由于缺少官方的技術(shù)支持,在VB里,指針變得很神秘。因此在C里一些基本的技術(shù),在VB里就變得比較困難。本文的目的就是要提供給大家一種簡(jiǎn)單的方法,來(lái)將C處理指針的技術(shù)拿到VB里來(lái),并告訴你什么是可行的,什么可行但必須要小心的,什么是可能但不可行的,什么是根本就不可能的。
三、 程咬金的三板斧
是的,程序二基本上就已經(jīng)讓我們看到VB指針技術(shù)的模樣了。總結(jié)一下,在VB里用指針技術(shù)我們需要掌握三樣?xùn)|西:CopyMemory,VarPtr/StrPtr/ObjPtr, AdressOf. 三把斧頭,程咬金的三板斧,在VB里Hack的工具。
1、CopyMemory
關(guān)于CopyMemory和Bruce McKinney大師的傳奇,MSDN的Knowledge Base中就有文章介紹,你可以搜索"ID: Q129947"的文章。正是這位大師給32位的VB帶來(lái)了這個(gè)可以移動(dòng)內(nèi)存的API,也正是有了這個(gè)API,我們才能利用指針完成我們?cè)瓉?lái)想都不敢想的一些工作,感謝Bruce McKinney為我們帶來(lái)了VB的指針革命。
如CopyMemory的聲明,它是定義在Kernel32.dll中的RtlMoveMemory這個(gè)API,32位C函數(shù)庫(kù)中的memcpy就是這個(gè)API的包裝,如MSDN文檔中所言,它的功能是將從Source指針?biāo)柑庨_(kāi)始的長(zhǎng)度為L(zhǎng)ength的內(nèi)存拷貝到Destination所指的內(nèi)存處。它不會(huì)管我們的程序有沒(méi)有讀寫(xiě)該內(nèi)存所應(yīng)有的權(quán)限,一但它想讀寫(xiě)被系統(tǒng)所保護(hù)的內(nèi)存時(shí),我們就會(huì)得到著名的Access Violation Fault(內(nèi)存越權(quán)訪問(wèn)錯(cuò)誤),甚至?xí)鸶膅eneral protection (GP) fault(通用保護(hù)錯(cuò)誤) 。所以,在進(jìn)行本系列文章里的實(shí)驗(yàn)時(shí),請(qǐng)注意隨時(shí)保存你的程序文件,在VB集成環(huán)境中將"工具"-"選項(xiàng)"中的"環(huán)境"選項(xiàng)卡里的"啟動(dòng)程序時(shí)"設(shè)為"保存改變",并記住在"立即"窗口中執(zhí)行危險(xiǎn)代碼之前一定要保存我們的工作成果。
2、VatPtr/StrPtr/ObjPtr
它們是VB提供給我們的好寶貝,它們是VBA函數(shù)庫(kù)中的隱藏函數(shù)。為什么要隱藏?因?yàn)閂B開(kāi)發(fā)小組,不鼓勵(lì)我們用指針嘛。
實(shí)際上這三個(gè)函數(shù)在VB運(yùn)行時(shí)庫(kù)MSVBVM60.DLL(或MSVBVM50.DLL)中是同一個(gè)函數(shù)VarPtr(可參見(jiàn)我在本系列第一篇文章里介紹的方法)。
其庫(kù)型庫(kù)定義如下:
[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運(yùn)行時(shí)庫(kù)中的同一個(gè)函數(shù),我們也可以在VB里用API方式重新聲明這幾個(gè)函數(shù),如下:
Private Declare Function ObjPtr Lib "MSVBVM60" Alias "VarPtr" (var As Object) As Long
Private Declare Function VarPtr Lib "MSVBVM60" (var As Any) As Long
(沒(méi)有StrPtr,是因?yàn)閂B對(duì)字符串處理方式有點(diǎn)不同,這方面的問(wèn)題太多,我將在另一篇文章中詳談。順便提一下,聽(tīng)說(shuō)VB.NET里沒(méi)有這幾個(gè)函數(shù),但只要還能調(diào)用API,我們就可以試試上面的幾個(gè)聲明,這樣在VB.NET里我們一樣可以進(jìn)行指針操作。但是請(qǐng)注意,如果通過(guò)API調(diào)用來(lái)使用VarPtr,整個(gè)程序二SwapPtr將比原來(lái)使用內(nèi)置VarPtr函數(shù)時(shí)慢6倍。)
如果你喜歡刨根問(wèn)底,那么下面就是VarPtr函數(shù)在C和匯編語(yǔ)言里的樣子:
在C里樣子是這樣的:
long VarPtr(void* pv){
return (long)pv;
}
所對(duì)就的匯編代碼就兩行:
mov eax,dword ptr [esp+4]
ret 4 '彈出棧里參數(shù)的值并返回。
之所以讓大家了解VarPtr的具體實(shí)現(xiàn),是想告訴大家它的開(kāi)銷并不大,因?yàn)樗鼈儾贿^(guò)兩條指令,即使加上參數(shù)賦值、壓棧和調(diào)用指令,整個(gè)獲取指針的過(guò)程也就六條指令。當(dāng)然,同樣的功能在C語(yǔ)言里,由于語(yǔ)言的直接支持,僅需要一條指令即可。但在VB里,它已經(jīng)算是最快的函數(shù)了,所以我們完全不用擔(dān)心使用VarPtr會(huì)讓我們失去效率!速度是使用指針技術(shù)的根本要求。
一句話,VarPtr返回的是變量所在處的內(nèi)存地址,也可以說(shuō)返回了指向變量?jī)?nèi)存位置的指針,它是我們?cè)赩B里處理指針最重要的武器之一。
3、ByVal和ByRef
ByVal傳遞的參數(shù)值,而B(niǎo)yRef傳遞的參數(shù)的地址。在這里,我們不用去區(qū)別傳指針/傳地址/傳引用的不同,在VB里,它們根本就是一個(gè)東西的三種不同說(shuō)法,即使VB的文檔里也有地方在混用這些術(shù)語(yǔ)(但在C++里的確要區(qū)分指針和引用)
初次接觸上面的程序二SwapPtr的朋友,一定要搞清在里面的CopyMemory調(diào)用中,在什么地方要加ByVal,什么地方不加(不加ByVal就是使用VB缺省的ByRef),準(zhǔn)確的理解傳值和傳地址(指針)的區(qū)別,是在VB里正確使用指針的基礎(chǔ)。
現(xiàn)在一個(gè)最簡(jiǎn)單的實(shí)驗(yàn)來(lái)看這個(gè)問(wèn)題,如下面的程序三:
【程序三】:
'體會(huì)ByVal和ByRef
Sub TestCopyMemory()
Dim k As Long
k = 5
Note: CopyMemory ByVal VarPtr(k), 40000, 4
Debug.Print k
End Sub
上面標(biāo)號(hào)Note處的語(yǔ)句的目的,是將k賦值為40000,等同于語(yǔ)句k=40000,你可以在"立即"窗口試驗(yàn)一下,會(huì)發(fā)現(xiàn)k的值的確成了40000。
實(shí)際上上面這個(gè)語(yǔ)句,翻譯成白話,就是從保存常數(shù)40000的臨時(shí)變量處拷貝4個(gè)字節(jié)到變量k所在的內(nèi)存中。
現(xiàn)在我們來(lái)改變一個(gè)Note處的語(yǔ)句,若改成下面的語(yǔ)句:
Note2: CopyMemory ByVal VarPtr(k), ByVal 40000, 4
這句話的意思就成了,從地址40000拷貝4個(gè)字節(jié)到變量k所在的內(nèi)存中。由于地址40000所在的內(nèi)存我們無(wú)權(quán)訪問(wèn),操作系統(tǒng)會(huì)給我們一個(gè)Access Violation內(nèi)存越權(quán)訪問(wèn)錯(cuò)誤,告訴我們"試圖讀取位置0x00009c40處內(nèi)存時(shí)出錯(cuò),該內(nèi)存不能為'Read'"。
我們?cè)俑某扇缦碌恼Z(yǔ)句看看。
Note3: CopyMemory VarPtr(k), 40000, 4
這句話的意思就成了,從保存常數(shù)40000的臨時(shí)變量處拷貝4個(gè)字節(jié)到到保存變量k所在內(nèi)存地址值的臨時(shí)變量處。這不會(huì)出出內(nèi)存越權(quán)訪問(wèn)錯(cuò)誤,但k的值并沒(méi)有變。
我們可以把程序改改以更清楚的休現(xiàn)這種區(qū)別,如下面的程序四:
【程序四】:
'看看我們的東西被拷貝到哪兒去了
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
程序輸出:
5
40000
40000
由于NOTE4處使用缺省的ByVal,傳遞的是i的地址(也就是指向i的指針),所以常量40000拷貝到了變量i里,因此i的值成了40000,而k的值卻沒(méi)有變化。但是,在NOTE4前有:i=VarPtr(k),本意是要把i本身做為一個(gè)指針來(lái)使用。這時(shí),我們必須如NOTE5那樣用ByVal來(lái)傳遞指針i,由于i是指向變量k的指針,所以最后常量40000被拷貝了變量k里。
希望你已經(jīng)理解了這種區(qū)別,在后面問(wèn)題的討論中,我還會(huì)再談到它。
4、AddressOf
它用來(lái)得到一個(gè)指向VB函數(shù)入口地址的指針,不過(guò)這個(gè)指針只能傳遞給API使用,以使得API能回調(diào)VB函數(shù)。
本文不準(zhǔn)備詳細(xì)討論函數(shù)指針,關(guān)于它的使用請(qǐng)參考VB文檔。
5、拿來(lái)主義
實(shí)際上,有了CopyMemory,VarPtr,AddressOf這三把斧頭,我們已經(jīng)可以將C里基本的指針操作拿過(guò)來(lái)了。
如下面的C程序包括了大部分基本的指針指針操作:
struct POINT{
int x; int y;
};
int Compare(void* elem1, void* elem2){}
void PtrDemo(){
//指針聲明:
char c = 'X'; //聲明一個(gè)char型變量
char* pc; long* pl; //聲明普通指針
POINT* pPt; //聲明結(jié)構(gòu)指針
void* pv; //聲明無(wú)類型指針
int (*pfnCastToInt)(void *, void*);//聲明函數(shù)指針:
//指針賦值:
pc = c; //將變量c的地址值賦給指針pc
pfnCompare = Compare; //函數(shù)指針賦值。
//指針取值:
c = *pc; //將指針pc所指處的內(nèi)存值賦給變量c
//用指針賦值:
*pc = 'Y' //將'Y'賦給指針pc所指內(nèi)存變量里。
//指針移動(dòng):
pc++; pl--;
}
這些對(duì)指針操作在VB里都有等同的東西,前面討論ByVal和ByRef時(shí)曾說(shuō)過(guò)傳指針和傳地址是一回事,實(shí)際上當(dāng)我們?cè)赩B里用缺省的ByRef聲明函數(shù)參數(shù)時(shí),我們已經(jīng)就聲明了指針。
如一個(gè)C聲明的函數(shù):long Func(char* pc)
其對(duì)應(yīng)的VB聲明是:Function Func(pc As Byte) As Long
這時(shí)參數(shù)pc使用缺省的ByRef傳地址方式來(lái)傳遞,這和C里用指針來(lái)傳遞參數(shù)是一樣。
那么怎么才能象C里那樣明確地聲明一個(gè)指針呢?
很簡(jiǎn)單,如前所說(shuō),用一個(gè)32位長(zhǎng)整數(shù)來(lái)表達(dá)指針就行。在VB里就是用Long型來(lái)明確地聲明指針,我們不用區(qū)分是普通指針、無(wú)類型指針還是函數(shù)指針,通通都可用Long來(lái)聲明。而給一個(gè)指針賦值,就是賦給它用VarPar得到的另一個(gè)變量的地址。具體見(jiàn)程序五。
【程序五】:同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) '對(duì)應(yīng)C里的long、int型指針
pc = VarPtr(c) '對(duì)應(yīng)char、short型指針
pPt = VarPtr(Pt) '結(jié)構(gòu)指針
pv = VarPtr(ca(0)) '字節(jié)數(shù)組指針,可對(duì)應(yīng)任何類型,也就是void*
pfnCompare = FnPtrToLong(AddressOf Compare) '函數(shù)指針
CopyMemory c, ByVal pc, LenB(c) '用指針取值
CopyMemory ByVal pc, AscB("Y"), LenB(c) '用指針賦值
pc = pc + LenB(c) : pl = pl - LenB(l) '指針移動(dòng)
End Sub
我們看到,由于VB不直接支持指針操作,在VB里用指針取值和用指針賦值都必須用CopyMemory這個(gè)API,而調(diào)用API的代價(jià)是比較高的,這就決定了我們?cè)赩B里使用指針不能象在C里那樣自由和頻繁,我們必須要考慮指針操作的代價(jià),在后面的"指針應(yīng)用"我們會(huì)再變談這個(gè)問(wèn)題。
程序五中關(guān)于函數(shù)指針的問(wèn)題請(qǐng)參考VB文檔,無(wú)類型指針void*會(huì)在下面"關(guān)于Any的問(wèn)題"里說(shuō)。
程序五基本上已經(jīng)包括了我們能在VB里進(jìn)行的所有指針操作,僅此而已。
下面有一個(gè)小測(cè)試題,如果現(xiàn)在你就弄懂了上面程咬金的三板斧,你就應(yīng)該能做得出來(lái)。
上面提到過(guò),VB.NET中沒(méi)有VarPtr,我們可以用聲明API的方式來(lái)引入MSVBVM60.DLL中的VarPtr。現(xiàn)在的問(wèn)題如果不用VB的運(yùn)行時(shí)DLL文件,你能不能自己實(shí)現(xiàn)一個(gè)ObjPtr。答案在下一節(jié)后給出。
四、指針使用中應(yīng)注意的問(wèn)題
1、關(guān)于ANY的問(wèn)題
如果以一個(gè)老師的身份來(lái)說(shuō)話,我會(huì)說(shuō):最好永遠(yuǎn)也不要用Any!是的,我沒(méi)說(shuō)錯(cuò),是永遠(yuǎn)!所以我沒(méi)有把它放在程咬金的三板斧里。當(dāng)然,這個(gè)問(wèn)題和是不是應(yīng)該使用指針這個(gè)問(wèn)題一樣會(huì)引發(fā)一場(chǎng)沒(méi)有結(jié)果的討論,我告訴你的只是一個(gè)觀點(diǎn),因?yàn)橛袝r(shí)我們會(huì)為了效率上的一點(diǎn)點(diǎn)提高或想偷一點(diǎn)點(diǎn)懶而去用Any,但這樣做需要要承擔(dān)風(fēng)險(xiǎn)。
Any不是一個(gè)真正的類型,它只是告訴VB編譯器放棄對(duì)參數(shù)類型的檢查,這樣,理論上,我們可以將任何類型傳遞給API。
Any在什么地方用呢?讓我們來(lái)看看,在VB文檔里的是怎么說(shuō)的,現(xiàn)在就請(qǐng)打開(kāi)MSDN(Visual Studio 6自帶的版本),翻到"Visual Basic文檔"-"使用Visual Basic"-"部件工具指南"-"訪問(wèn)DLL和Windows API"部分,再看看"將 C 語(yǔ)言聲明轉(zhuǎn)換為 Visual Basic 聲明"這一節(jié)。文檔里告訴我們,只有C的聲明為L(zhǎng)PVOID和NULL時(shí),我們才用Any。實(shí)際上如果你愿意承擔(dān)風(fēng)險(xiǎn),所有的類型你都可以用Any。當(dāng)然,也可以如我所說(shuō),永遠(yuǎn)不要用Any。
為什么要這樣?那為什么VB官方還要提供Any?是信我的,還是信VB官方的?有什么道理不用Any?
如前面所說(shuō),VB官方不鼓勵(lì)我們使用指針。因?yàn)閂B所標(biāo)榜的優(yōu)點(diǎn)之一,就是沒(méi)有危險(xiǎn)的指針操作,所以的內(nèi)存訪問(wèn)都是受VB運(yùn)行時(shí)庫(kù)控制的。在這一點(diǎn)上,JAVA語(yǔ)言也有著同樣的標(biāo)榜。但是,同JAVA一樣,VB要避免使用指針而得到更高的安全性,就必須要克服沒(méi)有指針而帶來(lái)的問(wèn)題。VB已經(jīng)盡最大的努力來(lái)使我們遠(yuǎn)離指針的同時(shí)擁有強(qiáng)類型檢查帶來(lái)的安全性。但是操作系統(tǒng)是C寫(xiě)的,里面到處都需要指針,有些指針是沒(méi)有類型的,就是C程序員常說(shuō)的可怕的void*無(wú)類型指針。它沒(méi)有類型,因此它可以表示所有類型。如CopyMemory所對(duì)應(yīng)的是C語(yǔ)言的memcpy,它的聲明如下:
void *memcpy( void *dest, const void *src, size_t count );
因memcpy前兩個(gè)參數(shù)用的是void*,因此任何類型的參數(shù)都可以傳遞給他。
一個(gè)用C的程序員,應(yīng)該知道在C函數(shù)庫(kù)里這樣的void*并不少見(jiàn),也應(yīng)該知道它有多危險(xiǎn)。無(wú)論傳遞什么類型的變量指針給上面memcpy的void*,C編譯器都不會(huì)報(bào)錯(cuò)或給任何警告。
在VB里大多數(shù)時(shí)候,我們使用Any就是為了使用void*,和在C里一樣,VB也不對(duì)Any進(jìn)行類型檢查,我們也可以傳遞任何類型給Any,VB編譯器也都不會(huì)報(bào)錯(cuò)或給任何警告。
但程序運(yùn)行時(shí)會(huì)不會(huì)出錯(cuò),就要看使用它時(shí)是不是小心了。正因?yàn)樵贑里很多錯(cuò)誤是和void*相關(guān)的,所以,C++鼓勵(lì)我們使用satic_castvoid*來(lái)明確指出這種不安全的類型的轉(zhuǎn)換,已利于發(fā)現(xiàn)錯(cuò)誤。
說(shuō)了這么多C/C++,其實(shí)我是想告訴所有VB的程序員,在使用Any時(shí),我們必須和C/C++程序員使用void*一樣要高度小心。
VB里沒(méi)有satic_cast這種東西,但我們可以在傳遞指針時(shí)明確的使用long類型,并且用VarPtr來(lái)取得參數(shù)的指針,這樣至少已經(jīng)明確地指出我們?cè)谑褂梦kU(xiǎn)的指針。如程序二經(jīng)過(guò)這樣的處理就成了下面的程序:
【程序五】:
'使用更安全的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位的地址值,當(dāng)我們將一個(gè)別的類型傳遞給這個(gè)API時(shí),編譯器會(huì)報(bào)錯(cuò),比如現(xiàn)在我們用下面的語(yǔ)句:
【程序六】:
'有點(diǎn)象【程序四】,但將常量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)
'下面的語(yǔ)句會(huì)引起類型不符的編譯錯(cuò)誤,這是好事!
'CopyMemory i, z, 4
'應(yīng)該用下面的
CopyMemory i, ByVal VarPtr(z), 2
Debug.Print k
End Sub
編譯會(huì)出錯(cuò)!是好事!這總比運(yùn)行時(shí)不知道錯(cuò)在哪兒好!
象程序四那樣使用Any類型來(lái)聲明CopyMemory的參數(shù),VB雖然不會(huì)報(bào)錯(cuò),但運(yùn)行時(shí)結(jié)果卻是錯(cuò)的。不信,你試試將程序四中的40000改為1,結(jié)果i的值不是我們想要的1,而是327681。為什么在程序四中,常量為1時(shí)結(jié)果會(huì)出錯(cuò),而常量為40000時(shí)結(jié)果就不錯(cuò)?
原因是VB對(duì)函數(shù)參數(shù)中的常量按Variant的方式處理。是1時(shí),由于1小于Integer型的最大值32767,VB會(huì)生成一個(gè)存儲(chǔ)值1的Integer型的臨時(shí)變量,也就是說(shuō),當(dāng)我們想將1用CopyMemroy拷貝到Long型的變量i時(shí),這個(gè)常量1是實(shí)際上是Integer型臨時(shí)變量!VB里Integer類型只有兩個(gè)字節(jié),而我們實(shí)際上拷貝了四個(gè)字節(jié)。知道有多危險(xiǎn)了吧!沒(méi)有出內(nèi)存保護(hù)錯(cuò)誤那只是我們的幸運(yùn)!
如果一定要解釋一下為什么i最后變成了327681,這是因?yàn)槲覀儗的低16位的值5也拷貝到了i值的高16位中去了,因此有5*65536+1=327681。詳談這個(gè)問(wèn)題涉及到VB局部變量聲明順序,CopyMemory參數(shù)的壓棧順序,long型的低位在前高位在后等問(wèn)題。如果你對(duì)這些問(wèn)題感興趣,可以用本系列第一篇文章所提供的方法(DebugBreak這個(gè)API和VC調(diào)試器)來(lái)跟蹤一下,可以加深你對(duì)VB內(nèi)部處理方式的認(rèn)識(shí),由于這和本文討論的問(wèn)題無(wú)關(guān),所以就不詳談了。到這里,大家應(yīng)該明白,程序三和程序四實(shí)際上有錯(cuò)誤!!!我在上面用常量40000而不用1,不是為了在文章中湊字?jǐn)?shù),而是因?yàn)?0000這個(gè)常量大于32767,會(huì)被VB解釋成我們需要的Long型的臨時(shí)變量,只有這樣程序三和程序四才能正常工作。對(duì)不起,我這樣有意的隱藏錯(cuò)誤只是想加深你對(duì)Any危害的認(rèn)識(shí)。
總之,我們要認(rèn)識(shí)到,編譯時(shí)就找到錯(cuò)誤是非常重要的,因?yàn)槟泷R上就知道錯(cuò)誤的所在。所以我們應(yīng)該象程序五和程序六那樣明確地用long型的ByVal的指針,而不要用Any的ByRef的指針。
但用Any已經(jīng)如此的流行,以至很多大師們也用它。它唯一的魅力就是不象用Long型指針那樣,需要我們自己調(diào)用VarPtr來(lái)得到指針,所有處理指針的工作由VB編譯器來(lái)完成。所以在參數(shù)的處理上,只用一條匯編指令:push ,而用VarPtr時(shí),由于需要函數(shù)調(diào)用,因此要多用五條匯編指令。五條多余的匯編指令有時(shí)的確能我們冒著風(fēng)險(xiǎn)去用Any。
VB開(kāi)發(fā)小組提供Any,就是想用ByRef xxx As Any來(lái)表達(dá)void* xxx。我們也完全可以使用VarPtr和Long型的指針來(lái)處理。我想,VB開(kāi)發(fā)小組也曾猶豫過(guò)是公布VarPtr,還是提供Any,最后他們決定還是提供Any,而繼續(xù)隱瞞VarPtr。的確,這是個(gè)兩難的決定。但是經(jīng)過(guò)我上面的分析,我們應(yīng)該知道,這個(gè)決定并不符合VB所追求的"更安全"的初衷。因?yàn)樗赡軙?huì)隱藏類型不符的錯(cuò)誤,調(diào)試和找到這種運(yùn)行時(shí)才產(chǎn)生的錯(cuò)誤將花貴更多的時(shí)間和精力。
所以我有了"最好永遠(yuǎn)不要用Any"這個(gè)"驚人"的結(jié)論。
不用Any的另一個(gè)好處是,簡(jiǎn)化了我們將C聲明的API轉(zhuǎn)換成VB聲明的方式,現(xiàn)在它變成了一句話:除了VB內(nèi)置的可以進(jìn)行類型檢查的類型外,所以其它的類型我們都應(yīng)該聲明成Long型。
2、關(guān)于NULL的容易混淆的問(wèn)題
有很多文章講過(guò),一定要記在心里:
VbNullChar 相當(dāng)于C里的'\0',在用字節(jié)數(shù)組構(gòu)造C字串時(shí)常用它來(lái)做最后1個(gè)元素。
vbNullString 這才是真正的NULL,就是0,在VB6中直接用0也可以。
只有上面的兩個(gè)是API調(diào)用中會(huì)用的。還有Empty、Null是Variant,而Nothing只和類對(duì)象有關(guān),一般API調(diào)用中都不會(huì)用到它們。
另:本文第三節(jié)曾提出一個(gè)小測(cè)驗(yàn)題,做出來(lái)了嗎?現(xiàn)在公布正確答案:
【測(cè)驗(yàn)題答案】
Function ObjPtr(obj as Object) as long
Dim lpObj As Long
CopyMemory lpObj, Obj, 4
ObjectPtr = lpObj
End Function
五、VB指針應(yīng)用
如前面所說(shuō)VB里使用指針不象C里那樣靈活,用指針處理數(shù)據(jù)時(shí)都需要用CopyMemory將數(shù)據(jù)在指針和VB能夠處理的變量之間來(lái)回拷貝,這需要很大的額外開(kāi)銷。因此不是所有C里的指針操作都可以移值到VB里來(lái),我們只應(yīng)在需要的時(shí)候才在VB里使用指針。
1、動(dòng)態(tài)內(nèi)存分配:完全不可能、可能但不可行,VB標(biāo)準(zhǔn)
在C和C++里頻繁使用指針的一個(gè)重要原因是需要使用動(dòng)態(tài)內(nèi)存分配,用Malloc或New來(lái)從堆棧里動(dòng)態(tài)分配內(nèi)存,并得到指向這個(gè)內(nèi)存的指針。在VB里我們也可以自己
用API來(lái)實(shí)現(xiàn)動(dòng)態(tài)分配內(nèi)存,并且實(shí)現(xiàn)象C里的指針鏈表。
但我們不可能象C那樣直接用指針來(lái)訪問(wèn)這樣動(dòng)態(tài)分配的內(nèi)存,訪問(wèn)時(shí)我們必須用CopyMemory將數(shù)據(jù)拷貝到VB的變量?jī)?nèi),大量的使用這種技術(shù)必然會(huì)降低效率,以至于要象C那樣用指針來(lái)使用動(dòng)態(tài)內(nèi)存根本就沒(méi)有可行性。要象C、PASCAL那樣實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),在VB里還是應(yīng)該老老實(shí)實(shí)用對(duì)象技術(shù)來(lái)實(shí)現(xiàn)。
本文配套代碼中的LinkedList里有完全用指針實(shí)現(xiàn)的鏈表,它是使用HeapAlloc從堆棧中動(dòng)態(tài)分配內(nèi)存,另有一個(gè)調(diào)用FindFirstUrlCacheEntry這個(gè)API來(lái)操作IE的Cache的小程序IECache,它使用了VirtualAlloc來(lái)動(dòng)態(tài)分配內(nèi)存。但實(shí)際上這都不是必須的,VB已經(jīng)為我們提供了標(biāo)準(zhǔn)的動(dòng)態(tài)內(nèi)存分配的方法,那就是:
對(duì)象、字符串和字節(jié)數(shù)組
限于篇幅,關(guān)于對(duì)象的技術(shù)這里不講,LinkedList的源代碼里有用對(duì)象實(shí)現(xiàn)的鏈表,你可以參考。
字符串可以用Space$函數(shù)來(lái)動(dòng)態(tài)分配,VB的文檔里就有詳細(xì)的說(shuō)明。
關(guān)于字節(jié)數(shù)組,這里要講講,它非常有用。我們可用Redim來(lái)動(dòng)態(tài)改變它的大小,并將指向它第一個(gè)元素的指針傳給需要指針的API,如下:
dim ab() As Byte , ret As long
'傳遞Null值A(chǔ)PI會(huì)返回它所需要的緩沖區(qū)的長(zhǎng)度。
ret = SomeApiNeedsBuffer(vbNullString)
'動(dòng)態(tài)分配足夠大小的內(nèi)存緩沖區(qū)
ReDim ab(ret) As Byte
'再次把指針
VB.NET和VB6.0有什么區(qū)別
Visual Basic .NET是Microsoft Visual Studio .NET套件中主要組成部分之一。.NET版本的Visual Basic增加了更多特性,而且演化為完全面向?qū)ο螅ň拖馛++)的編程語(yǔ)言。本文將介紹VB.NET的新特性,并比較VB6.0/VB.NET之間的區(qū)別,闡述如何利用VB.NET編寫(xiě)簡(jiǎn)單的應(yīng)用程序。
1.1 什么是 VB.NET? Microsoft推出全新的編程和操作系統(tǒng)Framework——.NET,支持多種語(yǔ)言利用公共.NET庫(kù)開(kāi)發(fā)應(yīng)用程序,這些應(yīng)用程序在.NET Framework上運(yùn)行。使用Visual Basic在.NET Framework上編程,這就是VB.NET。
首先,讓我演示在VB.NET中寫(xiě)最簡(jiǎn)單的控制臺(tái)程序:Hello World。
1.2 Hello, World!“Hello World!”是初學(xué)者學(xué)習(xí)Windows編程的代表性程序。我們的第一個(gè)程序就叫做“Hello VB.NET World!”。該程序在控制臺(tái)輸出一句話:“Hello VB.NET World!”,代碼如下所示:
代碼 1.1: Hello VB.NET World例子Imports System
Module Module1
Sub Main()
System.Console.WriteLine("Hello VB.NET World!")
End Sub
End Module
1.3 VB.NET 編輯器和編譯器你可以在記事本或VS.NET IDE等任意文本編輯器中撰寫(xiě)上述代碼,然后保存為HelloWorld.vb。 代碼編寫(xiě)完成之后,要么在命令行、要么在VS.NET IDE中編譯它。在Microsoft .NET Framework SDK中已經(jīng)包括VB.NET編譯器vbc.exe[][1],從IDE或是命令行都可以調(diào)用。要從命令行編譯HelloWorld.vb,請(qǐng)?jiān)诿钚写翱谳斎?/p>
vbc HelloWorld.vb /out:HelloWorld.exe /t:exe
編譯結(jié)束后,HelloWorld.exe被創(chuàng)建到當(dāng)前目錄下。在資源管理其中雙擊圖標(biāo)或在命令行執(zhí)行,程序正確地運(yùn)行了。祝賀你進(jìn)入VB.NET開(kāi)發(fā)者的行列。
Imports 語(yǔ)句
如你所知,大部分的.NET類型都在名字空間(namespace)中定義。Namespace是定義和管理類別的范疇。察看.NET Framework Class Library,可以看到數(shù)以百計(jì)的namespace。例如,System namespace就包括了Console、Object等類型定義。如果想使用Console類,需要用Imports指令導(dǎo)入System namespace。如下所示:
Imports System甚至可以明確地調(diào)用namespace而無(wú)需用Import導(dǎo)入。下面的例子展示了不用Import的“Hello World!”程序:
代碼1.2: Hello VB.NET World例子Module Module1
Sub Main()
System.Console.WriteLine("Hello VB.NET World!")
End SubEnd Module1.4 解析 "Hello VB.NET World!"程序第一行是:
Imports System; System namespace定義了Console類,該類用于讀寫(xiě)控制臺(tái)(命令行窗口)。然后你定義了一個(gè)module:Module Module1
…End Module所有的VB程序都包括一個(gè)Main()方法,即應(yīng)用程序入口點(diǎn)。在例子程序中,我們調(diào)用Console.WriteLine()向控制臺(tái)寫(xiě)入“Hello VB.NET World!”:
Sub Main()
Console.WriteLine(“Hello VB.NET World!”) End SubWriteLine()方法歸屬于Console類,它負(fù)責(zé)向控制臺(tái)寫(xiě)一個(gè)帶有行結(jié)束符的字符串。如前所述,Console類定義于System namespace,你通過(guò)直接引用來(lái)控制類成員。
Console類負(fù)責(zé)讀寫(xiě)系統(tǒng)控制臺(tái)。讀控制臺(tái)輸入用Read和ReadLine方法,向控制臺(tái)輸出用WriteLine方法。
表1.1 Console類定義的方法
方法 用途 例子
Read 讀入單個(gè)字符 int i = Console.Read();
ReadLine 讀入一行 string str = Console.ReadLine();
Write 寫(xiě)一行 Console.Write("Write: 1");
WriteLine 寫(xiě)一行,并帶上行結(jié)束符
Console.WriteLine("Test Output Data with Line");
1.5 VB.NET有什么新特點(diǎn)? VB.NET比 VB6.0更加穩(wěn)定,而且完全面向?qū)ο蟆R苍S你還記得,VB6.0不支持繼承、重載和接口,所以不是真正面向?qū)ο蟮摹6鳹B.NET則支持這些面向?qū)ο筇匦浴B6.0有兩個(gè)薄弱環(huán)節(jié)——多線程和異常處理。在VB.NET中,開(kāi)發(fā)多線程應(yīng)用和使用C++/C#別無(wú)二致,結(jié)構(gòu)化異常處理也得到支持。稍后我們會(huì)詳細(xì)解釋這些特性。
下面是VB.NET的特性列表——
·面向?qū)ο蟮木幊陶Z(yǔ)言。支持繼承、重載、接口、共享成員和構(gòu)造器。·支持所有的CLS特性,如存取控制.NET類、與其它.NET語(yǔ)言交互、元數(shù)據(jù)、公共數(shù)據(jù)類型、委托等等。·多線程支持。·結(jié)構(gòu)化異常處理。 1.6 名字空間與集合 前面討論了我們的第一個(gè)VB.NET程序。該程序中首先引人注意的是名字空間(namespace)。在.NET參考文檔中,你會(huì)發(fā)現(xiàn)每個(gè)類都?xì)w屬于某個(gè)namespace。那么,namespace到底是什么?
一個(gè)namespace是類和組件的邏輯組合,其目的在于將.NET class按類別定義。微軟借用了C++ class packaging概念:namespace來(lái)描述這種組合。.NET Framework中的組件被稱為集合(assembly)。全部.NET代碼在數(shù)百個(gè)庫(kù)文件(DLL)中定義。Namespace把a(bǔ)ssembly中定義的類組織起來(lái)。一個(gè)namespace可以包括多個(gè)assembly,一個(gè)assembly也可以在多個(gè)namespace中定義。 namespace樹(shù)的根節(jié)點(diǎn)是System namespace。在.NET Library中,每個(gè)class都在一組相似的類別中定義。例如,System.Data namespace只包括數(shù)據(jù)相關(guān)類。同樣,System.Multithreading只包括多線程類。
在使用.NET支持的語(yǔ)言(如C#、VB.NET、C++.NET等)創(chuàng)建新應(yīng)用程序時(shí),你會(huì)注意到每個(gè)應(yīng)用程序都被定義為一個(gè)namespace,而所有的class都?xì)w屬于這個(gè)namespace。通過(guò)引用這個(gè)namespace,其它應(yīng)用程序就能存取這些class。 在.NET中,代碼被編譯為中間語(yǔ)言(Intermediate Language,IL),assembly中存儲(chǔ)了IL代碼、元數(shù)據(jù)和其它資源文件。同一個(gè)assembly可以附屬于一個(gè)或多個(gè)Exe/DLL。所有的.NET庫(kù)都存儲(chǔ)在assembly中。
1.7 VB.NET: 完全面向?qū)ο蟮木幊陶Z(yǔ)言抽象、封裝、多態(tài)、繼承是面向?qū)ο笳Z(yǔ)言的四個(gè)基本屬性。VB6.0不支持繼承,而VB.NET則不然。所以,和C++一樣,VB.NET也是完全面向?qū)ο蟮木幊陶Z(yǔ)言。
Class 和 ModuleVB.NET用Class...End Class語(yǔ)句對(duì)創(chuàng)建class。每個(gè)VB.NET至少包括一個(gè)Module(模塊)。Module在Module…End Module語(yǔ)句對(duì)中實(shí)現(xiàn)。應(yīng)用程序的主要模塊是Main方法,亦即應(yīng)用程序入口點(diǎn)。
和VB6.0相似的地方是,都可以使用Function/Sub關(guān)鍵字定義方法。下面的例子顯示了如何創(chuàng)建class、添加方法,并從主程序調(diào)用方法: Imports System
Module Module1
Sub Main()
Dim cls As TestClass = New TestClass
Console.WriteLine(cls.MyMethod)
End Sub
Class TestClass
Function MyMethod() As String
Return "Test Method"
End Function
End Class
End ModuleProperty屬性(Property)是類變量的公共描述。Property…End Property語(yǔ)句用以創(chuàng)建property。屬性的Get/Set方法分別用于取得和設(shè)置屬性值。下面的例子中,Data是TestClass的屬性。
Imports System
Imports System.Console
Module Module1
Sub Main()
Dim cls As TestClass = New TestClass
WriteLine(cls.MyMethod)
WriteLine(cls.Data)
cls.Data = "New Data"
WriteLine(cls.Data)
End Sub
End Module
Class TestClass
Private strData As String = "Some Data"
Function MyMethod() As String
Return "Test Method!"
End Function
' Adding Data property to the class
Public Property Data() As String
Get
Return strData
End Get
Set(ByVal Value As String)
strData = Value
End Set
End Property
重載VB.NET通過(guò)overload關(guān)鍵字支持方法重載。使用這個(gè)關(guān)鍵字,你可以定義同名但不同參數(shù)的方法。
類成員訪問(wèn)域
除了原有的Private和Public,VB.NET引入了幾個(gè)新關(guān)鍵字。全部訪問(wèn)域關(guān)鍵字列表如下:
關(guān)鍵字 作用域
Private 限于class內(nèi)部
Public 可以從class外訪問(wèn)
Friend 限于class所屬的應(yīng)用程序內(nèi)
Protected 只能被class和其派生類訪問(wèn)
Protected Friend 能被class、應(yīng)用程序和派生類訪問(wèn)
繼承繼承是面向?qū)ο缶幊陶Z(yǔ)言中最常用的技術(shù)。繼承讓你能夠重用類代碼和功能。
VB.NET支持繼承,而VB6.0則不支持。繼承的好處在于你能使用任何人編寫(xiě)的類,從這些類派生自己的類,然后在自己的類中調(diào)用父類功能。在下面的例子中,Class B派生自Class A,我們將從Class B中調(diào)用Class A的方法MethodA。
Imports System
Imports System.Console
Module Module1
Sub Main()
Dim bObj As B = New B
WriteLine(bObj.MethodA())
End Sub
End Module
' Class A defined
Public Class A
Function MethodA() As String
Return "Method A is called."
End Function
End Class
'Class B, inherited from Class A. All members (Public and Protected)
' can be access via B now.
Public Class B
Inherits A
Function MethodB() As String
Return "Method B is called."
End Function
End Class
可以從一個(gè)class中派生多個(gè)自定義class,也可以從多個(gè)class派生一個(gè)自定義class。
共享的成員類的共享成員被類的所有實(shí)體共享。共享成員可能是屬性、方法或字段/值域。在你不想讓用戶全面控制自己的類時(shí),共享成員相當(dāng)有用。例如,你可以開(kāi)發(fā)一個(gè)類庫(kù),讓用戶通過(guò)共享成員使用其中的部分功能。
可以通過(guò)class本身引用共享成員,而無(wú)需通過(guò)類的實(shí)體。例如:Module Module1
Sub Main()
WriteLine(A.MethodA())
End Sub
End Module
' Class A defined
Public Class A
Shared Function MethodA() As String
Return "Method A is called."
End Function
End Class
多線程VB語(yǔ)言的一大弱點(diǎn)就是缺乏編寫(xiě)自由線程(free-threaded)程序的能力。在.NET Framework中,所有語(yǔ)言共享CRL(Common Runtime Library,公共運(yùn)行庫(kù)),也就是說(shuō),你可以用VB.NET、C#或其它.NET語(yǔ)言編寫(xiě)同樣的程序。
System.Threading namespace定義了線程類。我們只需要引入System.Threading namespace,即可使用線程類。
System.Threading.Thread類提供線程對(duì)象,可以使用Thread類創(chuàng)建或破壞線程。
創(chuàng)建線程使用Thread類的實(shí)體創(chuàng)建一個(gè)新線程,然后用Thread.Start方法開(kāi)始執(zhí)行線程。線程構(gòu)造器接受一個(gè)參數(shù),該參數(shù)指明你要在線程中執(zhí)行的procedure。在下例中,我想在oThread1(Thread類的一個(gè)實(shí)體)的第二線程中執(zhí)行SecondThread過(guò)程:
oThread1 = New Thread(AddressOf SecondThread)
SecondThread procedure looks like below:
Public Sub SecondThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine(i.ToString())
Next
End Sub
然后,調(diào)用Thread.Start()開(kāi)始線程:
oThread1.Start()
下面的代碼創(chuàng)建兩個(gè)第二線程:
Imports System
Imports System.Threading
Module Module1
Public oThread1 As Thread
Public oThread2 As Thread
Sub Main()
oThread1 = New Thread(AddressOf SecondThread)
oThread2 = New Thread(AddressOf ThirdThread)
oThread1.Start()
oThread2.Start()
End Sub
Public Sub SecondThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine(i.ToString())
Next
End Sub
Public Sub ThirdThread()
Dim i As Integer
For i = 1 To 10
Console.WriteLine("A" + i.ToString())
Next
End Sub
End Module
破壞線程 調(diào)用Abort方法來(lái)破壞(中止)一個(gè)線程。在調(diào)用Abort之前,確保用IsAlive判斷線程處于活動(dòng)狀態(tài)。
If oThread1.IsAlive Then
oThread1.Abort()
End If
暫停線程可以使用Sleep方法來(lái)暫停線程執(zhí)行。Sleep方法接受一個(gè)以毫秒為單位的參數(shù),指明線程應(yīng)當(dāng)暫停多長(zhǎng)時(shí)間。
下面的例子讓線程暫停1秒鐘:
oThread2.Sleep(1000)你也可以使用Suspend和Resume方法來(lái)掛起和繼續(xù)線程執(zhí)行。
設(shè)定線程優(yōu)先級(jí)Thread類的Priority屬性用于設(shè)定線程優(yōu)先級(jí)。該屬性可以設(shè)置為Normal,AboveNormal,BelowNormal,Highest和Lowest。如:
oThread2.Priority = ThreadPriority.Highest線程與Apartment使用ApartmentState屬性設(shè)置線程的apartment類型,該屬性值可以為STA,MTA或是Unknown[][2]:
oThread.ApartmentState = ApartmentState.MTAMTS意味著可以使用多線程模式,而STA則只能是單線程執(zhí)行。
Public Enum ApartmentState
{
STA = 0,
MTA = 1,
Unknown = 2,
}
1.8 結(jié)構(gòu)化異常處理異常處理也被稱之為錯(cuò)誤處理。作為VB程序員,你一定對(duì)On Error Goto和On Error Resume Next這些VB6.0錯(cuò)誤處理語(yǔ)句耳熟能詳。這種類型的錯(cuò)誤處理被稱為非結(jié)構(gòu)化異常處理(Unstructured Exception Handling)。而在VB.NET中,Microsoft推出了結(jié)構(gòu)化異常處理機(jī)制。VB.NET支持類似C++的TryCatch..Finally控制。Try..Catch..Finally結(jié)構(gòu)如下: Try
' 可能導(dǎo)致異常的代碼
Catch
' 當(dāng)異常發(fā)生時(shí)處理異常的代碼
Finally
' 清理現(xiàn)場(chǎng)
End Try
Try語(yǔ)句塊用以拋出異常。如果異常發(fā)生,在Catch語(yǔ)句塊中處理。Finally語(yǔ)句塊是可選的,在需要釋放資源時(shí)特別有用。
1.9 VB6.0與VB.NET的不同之處除了上面談到的語(yǔ)言進(jìn)化,還有一些語(yǔ)法上的變化。所有這些語(yǔ)言和語(yǔ)法的變化在MSDN中均可查到,本文只作簡(jiǎn)單介紹。
數(shù)據(jù)類型(Data Type)的改變VB.NET中有些數(shù)據(jù)類型得到改進(jìn)。下面是變化對(duì)照表。
數(shù)據(jù)類型 VB6.0 VB.NET
Integer 16 bit size 32 bit size
Long 32 bit size 64 bit size
Currency 用于存儲(chǔ)大浮點(diǎn)數(shù) 被decimal替代,支持更高精度
Variant 可以存儲(chǔ)任意類型數(shù)據(jù) 被Object類型替代,也可以存儲(chǔ)任意類型數(shù)據(jù),但結(jié)果更好
Date Date類型被存儲(chǔ)為double 引入DateTime類型,用于存儲(chǔ)不同格式的日期
在VB.NET中,Short數(shù)據(jù)類型是16 bit的。Short,Integer和Long都等同于CLR的System.Int16、System.Int32和System.Int64類型。 變量聲明的變化在VB6.0中,變量聲明有許多限制。其中之一就是不能同行聲明多個(gè)變量。如果一定要在一行中聲明多個(gè)變量,就一定得指明每個(gè)變量的類型,否則將被默認(rèn)為Variant類型。
Dim a1, a2 As Integer Dim a3 As Integer, a4 As Integer 第一行中的a1是Variant類型,a2是Integer類型。第二行中兩個(gè)變量都是Integer類型。VB.NET支持同行聲明多個(gè)變量,舉例如下:
Dim a1, a2, a3 As Integer 變量初始化是另一個(gè)問(wèn)題。在VB6.0中不能同時(shí)聲明和初始化變量,而VB.NET則支持這個(gè)特性。
Dim name As String = "Mahesh"System.Console.Write(name) 聲明常量也可以照此辦理:Const DT_COUNT As Integer = 23 New關(guān)鍵字。在VB.NET中,New關(guān)鍵字用于創(chuàng)建對(duì)象。由于數(shù)據(jù)類型是對(duì)象,所以New關(guān)鍵字用以創(chuàng)建一個(gè)數(shù)據(jù)類型對(duì)象。
Dim i As Integer = New Integer()i = 10System.Console.WriteLine(i.ToString()) 代碼塊級(jí)別支持。像C++一樣,VB.NET支持代碼塊級(jí)別的作用域檢查。在語(yǔ)句塊中聲明的變量只在塊內(nèi)有效。
For i = 1 To 10Dim p As LongSystem.Console.WriteLine(i.ToString())NextSystem.Console.WriteLine(p.ToString()) 這段代碼在VB.NET中會(huì)得到一個(gè)編譯錯(cuò)誤,因?yàn)閜在For..Next語(yǔ)句塊之外不可訪問(wèn)。在VB6.0中這段代碼可以通過(guò)。
改進(jìn)了的類型安全
在VB6.0中,當(dāng)你聲明一個(gè)對(duì)外部過(guò)程的引用時(shí),可以指定任意類型的參數(shù)為As Any。Any關(guān)鍵字禁止了類型檢查,允許任意數(shù)據(jù)類型傳入和返回。
VB.NET不支持Any關(guān)鍵字。你必須指定每個(gè)參數(shù)和返回值的數(shù)據(jù)類型。數(shù)組VB.NET對(duì)數(shù)組作了明顯的改動(dòng)。
數(shù)組范圍。在VB.NET中,你需要格外留意數(shù)組范圍問(wèn)題。VB6.0默認(rèn)數(shù)組下界為0,故數(shù)組中的元素?cái)?shù)量等與數(shù)組上界值加一。下面的數(shù)組界限從A(0)到A(10),共有11個(gè)元素:
Dim A(10) As Single可以使用Option Base改變下界值為1。在VB.NET中,數(shù)組和C++一樣,下界值為0,不支持Option Base。注意:MSDN文檔指出數(shù)組只能包括與其尺寸相等的元素?cái)?shù)量,例如:Dim A(10) As Integer 只能包括10個(gè)元素(從A(0)到A(9)),但在編譯下面這段代碼時(shí)我發(fā)現(xiàn)它運(yùn)行良好,看起來(lái)數(shù)組中容納了11個(gè)元素。
Dim A(10) As Integer A(0) = 12 A(2) = 24 A(10) = 23 System.Console.WriteLine(A(0).ToString()) System.Console.WriteLine(A(10).ToString())System.Console.WriteLine(UBound(A).ToString()) System.Console.WriteLine(LBound(A).ToString()) Lbound和Ubound分別返回 0與10。ReDim和Fixed Array。你可以在VB6.0中指定固定長(zhǎng)度的數(shù)組。
Dim ArrWeekDays(0 To 6) As Integer
這里的ArrWeekDays數(shù)組是固定長(zhǎng)度的,不能用ReDim語(yǔ)句改變長(zhǎng)度。VB.NET不支持固定長(zhǎng)度數(shù)組,所以ReDim總是有效。
可以用下面兩種方式聲明數(shù)組: Dim ArrWeekDays(6) As IntegerDim ArrWeekDays() As Integer = {1, 2, 3, 4, 5, 6} ReDim語(yǔ)句。在VB6.0中,ReDim用于初始化動(dòng)態(tài)數(shù)組。在VB.NET中你不能把它當(dāng)作聲明用。ReDim只能用于改變數(shù)組長(zhǎng)度,不過(guò)不能改變數(shù)組維度。
Variant對(duì)陣ObjectVB6.0中的Variant數(shù)據(jù)類型能存儲(chǔ)任意類型變量,VB.NET中Object具備相同能力。
算術(shù)操作符VB.NET支持類似C++的快捷方式。下面的表格顯示了常規(guī)操作與快捷操作的不同之處。快捷方式也可用于*、/、|、等操作符。
操作符 常規(guī)語(yǔ)法 快捷方式加法 A = A+5 A +=5 減法 A = A – 5 A -+ 5固定長(zhǎng)度字符串
在VB6.0中,可以在聲明字符串時(shí)指定其長(zhǎng)度。VB.NET不支持固定長(zhǎng)度字符串。
布爾操作符VB6.0中的And、Or或是Xor語(yǔ)句是按位操作符。而在VB.NET中,它們是布爾操作符。執(zhí)行這些操作將返回true或false。VB.NET引入新操作符來(lái)完成按位操作。
操作符 描述 BitAnd 按位AndBitOr 按位OrBitXor 按位XorBitNot 按位Not結(jié)構(gòu)與自定義類型在VB6.0中,你使用Type…End Type語(yǔ)句塊創(chuàng)建結(jié)構(gòu)或自定義類型。例如:
Type StdRec
StdId As Integer
StdName As String End Type
VB.NET引入新的語(yǔ)法:Structure。Type…End Type不再被支持。Structure…End Structure與C++用法相同。可以指定結(jié)構(gòu)中每個(gè)元素的可訪問(wèn)域,如Public、Protected、Friend、Protected Friend、Private等。例如:
Structure StdRec
Public StdId As Integer Public StdName As String
Private StdInternal As String End StructureVB.NET中的Structures就像類一樣,也可以擁有方法和屬性。New和Nothing關(guān)鍵字VB6.0中,AS New和Nothing關(guān)鍵字用于聲明一個(gè)對(duì)象并初始化它。 VB.NET不支持隱式創(chuàng)建對(duì)象。如前所言,甚至連數(shù)據(jù)類型都是對(duì)象。你可以采用以下兩種方法創(chuàng)建數(shù)據(jù)類型或?qū)ο螅?Dim i As Integer Dim i As Integer = New Integer() // Do something if i = Nothing Then End If 不支持Set語(yǔ)句VB6.0使用Set語(yǔ)句指派對(duì)象。例如:Set myObj = new MyObjectSet a = b在VB.NET中,不需要使用Set指派對(duì)象。例如:myObj = new MyObj()a = b過(guò)程(procedure)語(yǔ)法的變化在VB.NET中過(guò)程語(yǔ)法有了很多變化。例如類似C++的過(guò)程調(diào)用方式、ByVal(傳值)為默認(rèn)類型、Optional關(guān)鍵字、return語(yǔ)句等等。類似C++的過(guò)程調(diào)用方式 VB6.0允許不用加圓括號(hào)調(diào)用過(guò)程(sub)。不過(guò),用Call語(yǔ)句調(diào)用函數(shù)或sub時(shí),一定要使用圓括號(hào)。例如:Dim I as IntegerCall EvaluateData(2, i) EvaluateData 2, i 在VB.NET中,所有的方法調(diào)用都需要圓括號(hào),而Call語(yǔ)句則是可選的。 ByVal是默認(rèn)參數(shù)類型在VB6.0中,在調(diào)用函數(shù)或sub時(shí)ByRef(傳址)是默認(rèn)類型。那意味著所有改變將反映到傳入的變量。VB.NET改變了這種方式。現(xiàn)在,默認(rèn)的參數(shù)類型是ByVal(傳值)。 Optional關(guān)鍵字VB6.0使用Optional關(guān)鍵字可用來(lái)讓用戶決定傳入一個(gè)默認(rèn)值,之后在調(diào)用IsMissing函數(shù)判斷參數(shù)是否有效。 而在VB.NET中,每個(gè)可選參數(shù)必須聲明其默認(rèn)值,無(wú)需調(diào)用IsMissing函數(shù)。例如:Sub MyMethod(Optional ByVal i As Integer = 3)
Return語(yǔ)句VB.NET的Return語(yǔ)句與C++相似。使用Return語(yǔ)句把控制權(quán)從過(guò)程返還給調(diào)用者。在VB6.0中,Return語(yǔ)句與GoSub語(yǔ)句一起使用。VB.NET不再支持GoSub語(yǔ)句。流程控制的改變下面是VB.NET對(duì)流程控制語(yǔ)句的修改:1. GoSub不再受到支持。2. Call、Function和Sub語(yǔ)句均可用于調(diào)用過(guò)程。3. On ... GoSub和On ... GoTo語(yǔ)句不再受支持。可以使用Select Case語(yǔ)句來(lái)替代。4. While ... Wend語(yǔ)句現(xiàn)在改為While…End While語(yǔ)句。不再支持Wend關(guān)鍵字。小結(jié) Visual Basic .NET是.NET版本的Visual Basic,已經(jīng)從根本發(fā)生了變化!通過(guò)本文你了解到VB6.0和VB.NET的區(qū)別是很大的,可以說(shuō)根本就是兩種不同的語(yǔ)言,因?yàn)樗鼈兊膬?nèi)核發(fā)生了變化,VB6.0是基于COM而vb.net是基于.net框架的,因?yàn)檫@個(gè)變化,所以在構(gòu)造類時(shí)也發(fā)生了根本性的變化。
DatagramSocket用于接收和發(fā)送UDP的Socket實(shí)例。該類有3個(gè)構(gòu)造函數(shù):DatagramSocket():通常用于客戶端編程,它并沒(méi)有特定監(jiān)聽(tīng)的端口,僅僅使用一個(gè)臨時(shí)的。程序會(huì)讓操作系統(tǒng)分配一個(gè)可用的端口。DatagramSocket(int port):創(chuàng)建實(shí)例,并固定監(jiān)聽(tīng)Port端口的報(bào)文。通常用于服務(wù)端。
新聞名稱:vb.net支持any嗎 vb和net
URL鏈接:http://chinadenli.net/article20/hepejo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供虛擬主機(jī)、云服務(wù)器、定制網(wǎng)站、搜索引擎優(yōu)化、移動(dòng)網(wǎng)站建設(shè)、Google
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)