久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

在VB6中用CopyMemory拷貝字符串的種種貓膩(一)

 nxhujiee 2011-04-05
  1. '正確的ByVal String的用法  
  2. Option Explicit  
  3.   
  4. Const STR_E = "PowerVB"  
  5. Private String1 As String  
  6. Private String2 As String  
  7. Private pString1 As Long  
  8.   
  9. Sub test7()  
  10.     Dim String1 As String  
  11.     Dim String2 As String  
  12. '    Dim _tmp1 As String, _tmp2 As String  
  13.       
  14.     String1 = "PowerVB" '14 bytes  
  15.     String2 = String$(7, 0) '14 bytes  
  16.       
  17.     CopyMemory ByVal String2, ByVal String1, 7  
  18.       
  19. '    _tmp1 = StrConv(String1, vbFromUnicode) '7 bytes  
  20. '    _tmp2 = StrConv(String2, vbFromUnicode) '7 bytes  
  21. '    CopyMemory ByVal _tmp2, ByVal _tmp1, 7  
  22.       
  23.     Debug.Print String2  
  24. End Sub  
clip_image002 如上圖所示,當(dāng)我們在VB中調(diào)用CopyMemory ByVal String2, ByVal String1, 7的時(shí)候發(fā)生了如下事情:
①首先VB媽媽幫我們對String1和String2自動(dòng)做了UA轉(zhuǎn)換,,也就是相當(dāng)于做了如下事情:
  1. Dim _tmp1 As String, _tmp2 As String  
  2.   
  3. _tmp1 = StrConv(String1, vbFromUnicode) '7 bytes  
  4. _tmp2 = StrConv(String2, vbFromUnicode) '7 bytes  
也就是說,,兩個(gè)14字節(jié)的Unicode字符串現(xiàn)在被存在兩個(gè)7字節(jié)的ANSI字符串里了。
②然后CopyMemory函數(shù)就做實(shí)際的拷貝動(dòng)作,。注意,,這時(shí)CopyMemory得到的參數(shù)不是String1, String2了,而是VB媽媽傳給它的_tmp1, _tmp2了,。所以,,實(shí)際上,CopyMemory同學(xué)是在這么干活:CopyMemory ByVal _tmp2, ByVal _tmp1, 7,。也就是,,從_tmp1的緩沖區(qū)拷貝7個(gè)字節(jié)到_tmp2的緩沖區(qū)。
③CopyMemory同學(xué)干完活,,VB媽媽又細(xì)心地做善后工作,。它把_tmp2的內(nèi)容再轉(zhuǎn)成14字節(jié)的Unicode字符串,并把它給String2,。
PS:
(1) 文字中帶圈標(biāo)號1與圖上的1是一一對應(yīng)的,。
(2) 注意①和③VB自動(dòng)進(jìn)行的,和CopyMemory函數(shù)無關(guān)。也就是VB只要看到API函數(shù)調(diào)用中涉及到字符串參數(shù),,就會(huì)自動(dòng)做這種轉(zhuǎn)換,!
 
看完上面的例子,也許你就會(huì)對VB媽媽這種細(xì)致體貼的勁頭有點(diǎn)體會(huì)了,。但是正如現(xiàn)實(shí)生活中媽媽的過多干涉會(huì)給我們帶來困擾一樣,,VB媽媽的這種體貼有時(shí)也會(huì)帶來讓人哭笑不得的效果。
 
第二節(jié) 基礎(chǔ)知識
在展示VB媽媽的各種“杰作”之前,,我們先來準(zhǔn)備一些基礎(chǔ)知識,。
2.1 VB中字符串的存儲(chǔ)結(jié)構(gòu)
當(dāng)你在VB里聲明了一個(gè)String型的變 量,比如:Dim str1 As String,。這個(gè)Str1本身其實(shí)是一個(gè)4字節(jié)的Long型,,里面存的是一個(gè)指針,指向的是實(shí)際字符串的緩沖區(qū)開始地址,,這個(gè)開始地址前面4字節(jié)里存放 的是這個(gè)緩沖區(qū)的長度,,單位為字節(jié)。也就是,,VB里的String其實(shí)是像下面這樣定義的:
  1. Type String   
  2.     dwSize as long  '后面實(shí)際數(shù)據(jù)的長度'   
  3.     pData() as Integer '實(shí)際數(shù)據(jù),每一個(gè)word就是一個(gè)字符,16位'   
  4.     wEnd as Integer '字符串結(jié)束點(diǎn)\0\0,,一個(gè)Unicode字符占雙字節(jié),不計(jì)入長度   
  5. end type  
所以,,VarPtr取到的地址是字符串變量的地址,,也就是字符串變量指針,也就是存放"指向pData這個(gè)地址的 指針"的變量的地址,;而StrPtr取到的值就是指向pData地址的指針,,也就是字符串緩沖區(qū)指針。所以,,有時(shí)候人們會(huì)說,,同一個(gè)字符串有兩個(gè)指針,一 個(gè)是字符串變量指針,、另一個(gè)是字符串緩沖區(qū)指針,。看下面的示例,,可以更好的理解以上的說法:
  1. Option Explicit  
  2.   
  3. 'From Myjian  
  4. 'http://topic.csdn.net/u/20090901/09/dddf35aa-7838-4415-85b2-222358422d81_2.html 187樓  
  5.   
  6. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _  
  7.      ByVal Destination As Long, _  
  8.      ByVal Source As Long, _  
  9.      ByVal Length As Long)  
  10.   
  11. Sub TestBstr()  
  12.     Dim str1 As String, J As Long, K As Long  
  13.       
  14.     str1 = "IamSlow慢"  
  15.     Debug.Print VarPtr(str1)                            '得到變量本身的地址  
  16.       
  17.     Call CopyMemory(VarPtr(J), VarPtr(str1), 4)         '取得str1里面保存的指針,與StrPtr一樣  
  18.     Debug.Print J, StrPtr(str1)  
  19.       
  20.     K = LenPtr(J)                                       '得到字符串的長度,實(shí)際字節(jié)值  
  21.     Debug.Print K, Len(str1), LenB(str1)  
  22.       
  23.     Debug.Print GetBSTRFromPtr(J)                       '根據(jù)這個(gè)指針得到字符串  
  24.     Debug.Print GetBSTRFromPtr(StrPtr(str1))  
  25. End Sub  
  26.   
  27. Private Function GetBSTRFromPtr(ByVal lpStr As LongAs String  
  28.     '從指針得到BSTR字符串  
  29.     Dim InStrLen As Long, OutStrArr() As Byte  
  30.       
  31.     InStrLen = LenPtr(lpStr)        '得到輸入字符串的長度  
  32.     ReDim OutStrArr(InStrLen - 1)  
  33.     Call CopyMemory(VarPtr(OutStrArr(0)), lpStr, InStrLen)  
  34.       
  35.     GetBSTRFromPtr = OutStrArr  
  36. End Function  
  37.   
  38. Private Function LenPtr(ByVal lpStr As LongAs Long  
  39.     '根據(jù)指針取BSTR長度  
  40.     Dim InStrLen As Long  
  41.       
  42.     If lpStr = 0 Then Exit Function  
  43.     CopyMemory VarPtr(InStrLen), lpStr - 4, 4 '得到輸入字符串的長度  
  44.     LenPtr = InStrLen  
  45. End Function  
注意,,上面的LenPtr函數(shù),是直接通過從字符串緩沖區(qū)的長度前綴中拷貝內(nèi)存得到的,。這其實(shí)是BSTR指針的特點(diǎn),,你只要保證傳入的指針是BSTR指針就可以這樣得到字符串的長度。
 
BSTR是COM中的一種字符串標(biāo)準(zhǔn),,與普通字符串的最大不同在于有長度前綴,,所以可以包含NULL在內(nèi)的字符串,。而如果沒有長度前綴,字符串中有NULL就會(huì)被認(rèn)為是結(jié)束了,,從而截?cái)?。VB中的字符串就是BSTR類型的。下面這個(gè)丑陋但清晰的圖說明了一切,。
clip_image004
我們還可以用以下的代碼來驗(yàn)證上面的說法:
  1. Sub testNull()  
  2.     Dim str1 As String  
  3.     str1 = "aa" & Chr(0) & "bb"  
  4.     Debug.Print str1, Len(str1), LenB(str1)  
  5.     MsgBox str1  
  6. End Sub  
可以看出,VB中的字符串中間可以含有NULL字符,,但是MsgBox這樣的函數(shù)由于是封裝的API函數(shù)MessageBox,,所以它會(huì)按照C字符串的標(biāo)準(zhǔn)來解釋字符串長度,因此會(huì)把a(bǔ)a以后的字符截掉,。
 
2.2 CopyMemory函數(shù)
下面我們來熟悉一下本文重點(diǎn)討論的這個(gè)函數(shù),。
  1. Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _  
  2.     (pDest As Any, pSource As Any, ByVal byteLen As Long)  
這個(gè)函數(shù)的功能是從pSource拷貝byteLen個(gè)字節(jié)的數(shù)據(jù)到pDest,其中源地址和目標(biāo)地址都是聲明為Any類型,。下面是CopyMemory對不同形式參數(shù)的理解:
(1) 傳一個(gè)變量給pSource,,那么源地址就是變量所在的地址
(2) 以ByVal形式傳一個(gè)變量給pSource,那么源地址就是變量的值
(3) 字符串變量的值是個(gè)指針,,指向字符串緩沖區(qū)的地址,,也就是StrPtr(String1)。因此,,以ByVal形式傳一個(gè)字符串變量給pSource,,那么源地址就是字符串變量的值,也就是字符串緩沖區(qū)的地址,。
 
下表總結(jié)了幾種常見的傳參數(shù)給CopyMemory的形式:
表格-CopyMemory使用字符串參數(shù)的各種可能形式
注:
(1)取到的內(nèi)容根據(jù)byteLen實(shí)際規(guī)定的字節(jié)數(shù)的多少,,可能有所不同,這里只是個(gè)大概,。
(2)帶高亮的兩行,,VB對字符串參數(shù)做了自動(dòng)的UA轉(zhuǎn)換,所以實(shí)際的CopyMemory動(dòng)作針對的是由String1轉(zhuǎn)換得到的ANSI字符串_tmp1而進(jìn)行的,。
(3)字節(jié)數(shù)那 一列給出了要取到有效的數(shù)據(jù)byteLen參數(shù)可以使用的數(shù)字范圍,。簡單的說,如果pSource的參數(shù)是字符串類型的話,,那么byteLen的字節(jié)數(shù)要 取為String1對應(yīng)的ANSI字符串的長度,。要理解這個(gè)也容易,你只要記住CopyMemory這時(shí)候?qū)嶋H上是對ANSI字符串做操作就可以了,。而如 果不發(fā)生字符串轉(zhuǎn)換的話,,像表里第4行,那么你就要拷貝String1的LebB長度,。這也好理解,,不發(fā)生轉(zhuǎn)換的話,,CopyMemory實(shí)際上是在直接 拷貝Unicode字符串的內(nèi)容啊。
 
繼續(xù)學(xué)習(xí)后續(xù)內(nèi)容前,,不妨做以下練習(xí),,以確認(rèn)你已經(jīng)掌握本節(jié)內(nèi)容。
  1. Sub Test2_Ptr()  
  2.     string1 = STR_E  
  3.       
  4.     '結(jié)果:StrPtr((string1)  
  5.     '把VarPtr(String1)的值作為地址,,拷這個(gè)地址里的值出來:)  
  6.     CopyMemory pString1, ByVal VarPtr(string1), 4  
  7.     Debug.Print pString1, StrPtr(string1), VarPtr(string1)  
  8.       
  9.     '結(jié)果:VarPtr(String1)  
  10.     '把VarPtr(String1)這個(gè)變量的值拷出來  
  11.     CopyMemory pString1, VarPtr(string1), 4  
  12.     Debug.Print pString1, StrPtr(string1), VarPtr(string1)  
  13.       
  14.     '結(jié)果:StrPtr(_tmp1)  
  15.     CopyMemory pString1, string1, 4  
  16.     Debug.Print pString1, StrPtr(string1), VarPtr(string1)  
  17.       
  18.     '結(jié)果:"ewoP"的ANSI編碼  
  19.     '從內(nèi)部臨時(shí)ANSI變量的字符串緩沖區(qū)取4個(gè)字節(jié)出來  
  20.     '"Powe"是50-6F-77-65,,取到的pString1里是(65776F50),正好倒過來  
  21.     '因?yàn)長ong型在書寫時(shí)是大端在前的  
  22.     CopyMemory pString1, ByVal string1, 4  
  23.     Debug.Print pString1, StrPtr(string1), VarPtr(string1)  
  24.     Debug.Print Hex(pString1)  
  25. End Sub  
2.3 大端序和小端序
Test2_Ptr里的結(jié)果你都猜的正確么,?我猜除了最后一個(gè),,應(yīng)該都正確,呵呵,。學(xué)習(xí)完以上的基礎(chǔ)知識,,下面這個(gè)語句的基本意思不難推測出來:
  1. '從內(nèi)部臨時(shí)ANSI變量的字符串緩沖區(qū)取頭4個(gè)字節(jié)出來  
  2. CopyMemory pString1, ByVal String1, 4  
但是有趣的是,頭4個(gè)字節(jié)"Powe"對應(yīng)的編碼是50-6F-77-65,,可是取到的pString1里是(65776F50),,正好倒過來。這是為什么呢,?看下面的解釋:
(1)字符串的數(shù)據(jù)相當(dāng)于Byte數(shù)組,,它的字符是放在一個(gè)連續(xù)的內(nèi)存塊里的。第一個(gè)字符地址最低,,最后一個(gè)字符最高,。
(2) 當(dāng)用Long變量去拷貝字符串的部分內(nèi)容的時(shí)候,Long的高字節(jié)對應(yīng)它取到的最后一個(gè)字符,,低字節(jié)則對應(yīng)第一個(gè)字符,。而在數(shù)字世界里,我們是把高字節(jié)寫 在左邊,、低字節(jié)寫在右邊的,。所以我們從Long里去觀察取到的字符,看起來是最后一個(gè)字符在左邊,、第一個(gè)字符在右邊,,好像倒了。
 
下面的例子可以幫助你更好的理解這一點(diǎn):
  1. '測試Long在內(nèi)存的存儲(chǔ)順序和拷貝順序  
  2. Sub test11()  
  3.     Dim Long1 As Long  
  4.     Dim Long2 As Long  
  5.     Dim i As Long  
  6.       
  7.     Long1 = &H1020304  
  8.     Debug.Print Hex(Long1)  
  9.     For i = 1 To 4  
  10.         CopyMemory Long2, Long1, i  
  11.         Debug.Print Hex(Long2)  
  12.     Next i  
  13. End Sub  
輸出
1020304
4
304
20304
1020304

 

  1. '測試String在內(nèi)存的存儲(chǔ)順序和拷貝順序  
  2. Sub test12()  
  3.     Dim String1 As String  
  4.     Dim String2 As String  
  5.     Dim i As Long  
  6.       
  7.     String1 = "1234"  
  8.     String2 = String$(4, 0)  
  9.     Debug.Print String1  
  10.     For i = 1 To 4  
  11.         CopyMemory ByVal String2, ByVal String1, i  
  12.         Debug.Print String2  
  13.     Next i  
  14. End Sub  

 

輸出
1234
1
12
123
1234
這里要補(bǔ)充一些關(guān)于字節(jié)序的知識,。Big Endian和Little Endian是CPU處理多字節(jié)數(shù)的不同方式,。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時(shí),,究竟是將6C寫在前面,,還是將49寫在前面?如果將6C寫在前面,,就是big endian,,譯作大端序,。還是將49寫在前面,就是little endian,,譯作小端序,。
 
“endian”這個(gè)詞出自《格列佛游記》。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時(shí)是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,,由此曾發(fā)生過六次叛亂,,其中一個(gè)皇帝送了命,另一個(gè)丟了王位,。
大端序指的是:從最大的一端開始存儲(chǔ)(從低地址存起),,MSB的地址最低。
小端序指的是:從最小的一端開始存儲(chǔ)(從低地址存起),,MSB的地址最高,。
 
像我們上面測試的Long,,它的最高位是1,,最低位是4,從拷貝出來的結(jié)果可以看出來4在最低位,,也就是從小端開始存儲(chǔ),,所以我們說它是小端序的。實(shí)際上Intel處理器都是小端序的,。
 
而在Big-endian處理器(如蘋果 Macintosh電腦)上建立的Unicode文件中的文字位元組(存放單位)排列順序,,與在Intel處理器上建立的文件的文字位元組排列順序相反。 最重要的位元組(MSB)擁有最低的地址,,且會(huì)先儲(chǔ)存文字中較大的一端,。為使這類電腦的用戶能夠存取你的文件,可選擇Unicode big-endian格式,。
 
2.4 如何傳參數(shù)會(huì)被VB6當(dāng)做字符串,?
Q:VB6根據(jù)什么判斷要傳給CopyMemory的參數(shù)是字符串,因而會(huì)觸發(fā)自動(dòng)的UA /AU轉(zhuǎn)換,?以下這些傳法,,哪種會(huì)轉(zhuǎn),哪種不會(huì)轉(zhuǎn),?
(1)ByVal String2
(2)ByVal StrPtr(String1)
(3)ByRef String1
(4)ByVal VarPtr(String1)
A
(1)ByVal String2:字符串參數(shù),,自動(dòng)轉(zhuǎn)換。
(2)ByVal StrPtr(String1):指針,,不轉(zhuǎn)換,。
(3)ByRef String1:編譯錯(cuò)誤,去掉 ByRef 可以通過編譯,,也會(huì)引起UA/AU轉(zhuǎn)換,。但其實(shí) Any 類型的參數(shù)不支持這種用法,,會(huì)導(dǎo)致無法預(yù)期的結(jié)果甚至程序崩潰(見后續(xù)討論)。
(4)ByVal VarPtr(String1):指針的指針,,不轉(zhuǎn)換,。但是這其實(shí)是變量 String1 所在的位置,不當(dāng)操作也會(huì)導(dǎo)致無法預(yù)期的結(jié)果甚至程序崩潰(見后續(xù)討論),。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多