计算机工作组:在VB6中用CopyMemory拷贝字符串的种种猫腻(一)
来源:百度文库 编辑:九乡新闻网 时间:2024/05/07 06:42:20
在VB6中用CopyMemory拷贝字符串的种种猫腻(一)收藏
出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4549926.aspx
本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心参与。本文其他部分在:(二)、(三)、(四)。
话说VB6是个很认真细致的妈妈,它会悄没声地帮你做很多事。今天我们来说的是VB6在API调用时自动对字符串参数进行转换的“好人好事”。 第一节 体贴的VB妈妈我们知道,在VB6中字符串都是以Unicode编码存在的,而Windows API函数中很多时候用的是ANSI字符串。VB妈妈害怕程序员们累着,所以在VB程序员调用API时,会自动的对其中的字符串参数做Unicode到ANSI的转换(以下简称UA转换),在API调用结束后会再把字符串参数做ANSI到Unicode的转换(以下简称AU转换)。这样说可能有点抽象,我们来看下面的例子。view plaincopy to clipboardprint?- '正确的ByVal String的用法
- Option Explicit
- Const STR_E = "PowerVB"
- Private String1 As String
- Private String2 As String
- Private pString1 As Long
- Sub test7()
- Dim String1 As String
- Dim String2 As String
- ' Dim _tmp1 As String, _tmp2 As String
- String1 = "PowerVB" '14 bytes
- String2 = String$(7, 0) '14 bytes
- CopyMemory ByVal String2, ByVal String1, 7
- ' _tmp1 = StrConv(String1, vbFromUnicode) '7 bytes
- ' _tmp2 = StrConv(String2, vbFromUnicode) '7 bytes
- ' CopyMemory ByVal _tmp2, ByVal _tmp1, 7
- Debug.Print String2
- End Sub
- Dim _tmp1 As String, _tmp2 As String
- _tmp1 = StrConv(String1, vbFromUnicode) '7 bytes
- _tmp2 = StrConv(String2, vbFromUnicode) '7 bytes
- Type String
- dwSize as long '后面实际数据的长度'
- pData() as Integer '实际数据,每一个word就是一个字符,16位'
- wEnd as Integer '字符串结束点\0\0,一个Unicode字符占双字节,不计入长度
- end type
- Option Explicit
- 'From Myjian
- 'http://topic.csdn.net/u/20090901/09/dddf35aa-7838-4415-85b2-222358422d81_2.html 187楼
- Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
- ByVal Destination As Long, _
- ByVal Source As Long, _
- ByVal Length As Long)
- Sub TestBstr()
- Dim str1 As String, J As Long, K As Long
- str1 = "IamSlow慢"
- Debug.Print VarPtr(str1) '得到变量本身的地址
- Call CopyMemory(VarPtr(J), VarPtr(str1), 4) '取得str1里面保存的指针,与StrPtr一样
- Debug.Print J, StrPtr(str1)
- K = LenPtr(J) '得到字符串的长度,实际字节值
- Debug.Print K, Len(str1), LenB(str1)
- Debug.Print GetBSTRFromPtr(J) '根据这个指针得到字符串
- Debug.Print GetBSTRFromPtr(StrPtr(str1))
- End Sub
- Private Function GetBSTRFromPtr(ByVal lpStr As Long) As String
- '从指针得到BSTR字符串
- Dim InStrLen As Long, OutStrArr() As Byte
- InStrLen = LenPtr(lpStr) '得到输入字符串的长度
- ReDim OutStrArr(InStrLen - 1)
- Call CopyMemory(VarPtr(OutStrArr(0)), lpStr, InStrLen)
- GetBSTRFromPtr = OutStrArr
- End Function
- Private Function LenPtr(ByVal lpStr As Long) As Long
- '根据指针取BSTR长度
- Dim InStrLen As Long
- If lpStr = 0 Then Exit Function
- CopyMemory VarPtr(InStrLen), lpStr - 4, 4 '得到输入字符串的长度
- LenPtr = InStrLen
- End Function
- Sub testNull()
- Dim str1 As String
- str1 = "aa" & Chr(0) & "bb"
- Debug.Print str1, Len(str1), LenB(str1)
- MsgBox str1
- End Sub
- Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
- (pDest As Any, pSource As Any, ByVal byteLen As Long)
- Sub Test2_Ptr()
- string1 = STR_E
- '结果:StrPtr((string1)
- '把VarPtr(String1)的值作为地址,拷这个地址里的值出来:)
- CopyMemory pString1, ByVal VarPtr(string1), 4
- Debug.Print pString1, StrPtr(string1), VarPtr(string1)
- '结果:VarPtr(String1)
- '把VarPtr(String1)这个变量的值拷出来
- CopyMemory pString1, VarPtr(string1), 4
- Debug.Print pString1, StrPtr(string1), VarPtr(string1)
- '结果:StrPtr(_tmp1)
- CopyMemory pString1, string1, 4
- Debug.Print pString1, StrPtr(string1), VarPtr(string1)
- '结果:"ewoP"的ANSI编码
- '从内部临时ANSI变量的字符串缓冲区取4个字节出来
- '"Powe"是50-6F-77-65,取到的pString1里是(65776F50),正好倒过来
- '因为Long型在书写时是大端在前的
- CopyMemory pString1, ByVal string1, 4
- Debug.Print pString1, StrPtr(string1), VarPtr(string1)
- Debug.Print Hex(pString1)
- End Sub
- '从内部临时ANSI变量的字符串缓冲区取头4个字节出来
- CopyMemory pString1, ByVal String1, 4
(2)当用Long变量去拷贝字符串的部分内容的时候,Long的高字节对应它取到的最后一个字符,低字节则对应第一个字符。而在数字世界里,我们是把高字节写在左边、低字节写在右边的。所以我们从Long里去观察取到的字符,看起来是最后一个字符在左边、第一个字符在右边,好像倒了。 下面的例子可以帮助你更好的理解这一点:view plaincopy to clipboardprint?
- '测试Long在内存的存储顺序和拷贝顺序
- Sub test11()
- Dim Long1 As Long
- Dim Long2 As Long
- Dim i As Long
- Long1 = &H1020304
- Debug.Print Hex(Long1)
- For i = 1 To 4
- CopyMemory Long2, Long1, i
- Debug.Print Hex(Long2)
- Next i
- End Sub
view plaincopy to clipboardprint?
- '测试String在内存的存储顺序和拷贝顺序
- Sub test12()
- Dim String1 As String
- Dim String2 As String
- Dim i As Long
- String1 = "1234"
- String2 = String$(4, 0)
- Debug.Print String1
- For i = 1 To 4
- CopyMemory ByVal String2, ByVal String1, i
- Debug.Print String2
- Next i
- End Sub
这里要补充一些关于字节序的知识。Big Endian和Little Endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian,译作大端序。还是将49写在前面,就是little endian,译作小端序。 “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。大端序指的是:从最大的一端开始存储(从低地址存起),MSB的地址最低。
小端序指的是:从最小的一端开始存储(从低地址存起),MSB的地址最高。 像我们上面测试的Long,它的最高位是1,最低位是4,从拷贝出来的结果可以看出来4在最低位,也就是从小端开始存储,所以我们说它是小端序的。实际上Intel处理器都是小端序的。 而在Big-endian处理器(如苹果Macintosh电脑)上建立的Unicode文件中的文字位元组(存放单位)排列顺序,与在Intel处理器上建立的文件的文字位元组排列顺序相反。最重要的位元组(MSB)拥有最低的地址,且会先储存文字中较大的一端。为使这类电脑的用户能够存取你的文件,可选择Unicodebig-endian格式。 2.4 如何传参数会被VB6当做字符串?Q:VB6根据什么判断要传给CopyMemory的参数是字符串,因而会触发自动的UA /AU转换?以下这些传法,哪种会转,哪种不会转?
(1)ByVal String2
(2)ByVal StrPtr(String1)
(3)ByRef String1
(4)ByVal VarPtr(String1)A:(1)ByVal String2:字符串参数,自动转换。
(2)ByVal StrPtr(String1):指针,不转换。
(3)ByRef String1:编译错误,去掉 ByRef 可以通过编译,也会引起UA/AU转换。但其实 Any 类型的参数不支持这种用法,会导致无法预期的结果甚至程序崩溃(见后续讨论)。
(4)ByVal VarPtr(String1):指针的指针,不转换。但是这其实是变量 String1 所在的位置,不当操作也会导致无法预期的结果甚至程序崩溃(见后续讨论)。
在VB6中用CopyMemory拷贝字符串的种种猫腻(一)
那个渐行渐远的时代:绝唱老三届拷贝本(一)
使用VB6编写COM加载项(方法一:AddinInstance)
在同花顺中用的135均线选股
在电脑中用自已的笔迹签名
C语言拷贝一个字符串到另一个函数代码
在同花顺中用的135均线选股公式2
在棒针编织中用到的荷叶边
学会在工作中用你的表格发言^
教你在表格中用图片制作精美的边框
分享一些我在开发过程中用过的资源
在困境中用真情诠释母爱的女人
如何在Excel怎样查找重复的字符串--Bravo
值得一读的街头骗术种种
唯美爱情种种-英汉对照(一)
在纽约停车的种种烦恼
人在临死前的种种表现
英文简历中用到的
一、皈依三宝在修学佛法中的地位 三、因皈依而有种种法门的施设
媒体曝团购抽奖猫腻 种种手段稀释中奖号码_
在棒针编织中用到的荷叶边的制作教程
拷贝成功(代前言)
拷贝成功(代前言)
拷贝成功(代前言)