首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VB >

用VarPtr传给CopyMemory拷贝字符串 为啥结果不正确解决思路

2012-01-11 
用VarPtr传给CopyMemory拷贝字符串 为啥结果不正确VB codeOption ExplicitDeclare Sub CopyMemory Lib ke

用VarPtr传给CopyMemory拷贝字符串 为啥结果不正确

VB code
Option ExplicitDeclare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _    (pDest As Any, pSource As Any, ByVal byteLen As Long)Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = String$(7, 0)        'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, ByVal VarPtr(String1), 4    CopyMemory ByVal String2, ByVal pString1, 14    Debug.Print String2 '得到的不是PowerVB,而是“P o w e”?为什么?End Sub


 slowgrace
 2 分钟前
 
以下为本帖小结 ================================================== 
谢谢许多朋友热心回复,尤其是赵老虎的全程指点和陈辉版主的推荐!我觉得这个帖子信息量挺大的,想了解VB6对API函数的字符串参数处理机制的同学可以仔细看一看。后来的朋友推荐按以下顺序看: 
基础知识:196楼+197楼;187楼+222楼;184楼+189楼;30+31楼;88楼+119楼+这个帖子的17楼+25楼;35+36+39楼;74楼+78楼;118楼;160楼;191楼+192楼 
例1:194楼+198楼; 
例2:0楼+90楼+210楼 
例3:229楼 
例4:16楼+162楼+132楼+184楼+246楼 
例5:10楼+248楼+252楼 
例6:34楼+40楼+92楼+125楼 
例7:129楼+130+134+141+143+269+276  

或者看我在博客里写的小结:(一)、(二)、(三)、(四)。 


[解决办法]
VB code
Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = String$(14, 0)        'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, ByVal VarPtr(String1), 4    CopyMemory ByVal String2, ByVal pString1, 14    Debug.Print StrConv(String2, vbFromUnicode) '得到的不是PowerVB,而是“P o w e”?为什么?End Sub
[解决办法]
VB code
Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = String$(7, 0)        'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, StrPtr(String1), 4    CopyMemory ByVal StrPtr(String2), ByVal pString1, 14    Debug.Print String2 '得到的不是PowerVB,而是“P o w e”?为什么?End Sub
[解决办法]
CopyMemory pString1, ByVal VarPtr(String1), 4
CopyMemory String2, pString1, 14
[解决办法]
VB code
Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = String$(14, 0)        'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, VarPtr(String1), 4    CopyMemory String2, ByVal pString1, 4    Debug.Print StrConv(String2, vbFromUnicode) '得到的不是PowerVB,而是“P o w e”?为什么?End Sub
[解决办法]
没注意到你的代码

这样才对

VB code
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _    (pDest As Any, pSource As Any, ByVal byteLen As Long)Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = Space$(Len(String1))'    pString1 = Space$(Len(String1))    CopyMemory pString1, ByVal VarPtr(String1), 4    CopyMemory ByVal StrPtr(String2), ByVal (pString1), LenB(String2)    Debug.Print String2 '得到的不是PowerVB,而是“P o w e”?为什么?End Sub
------解决方案--------------------


VB code
CopyMemory ByVal String2, ByVal pString1, 14
[解决办法]
通俗易懂 还不出错
VB code
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal byteLen As Long)Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long    Dim pString2 As Long        String1 = "PowerVB"    String2 = Space$(Len(String1))    CopyMemory pString1, String1, 4    CopyMemory pString2, String2, 4        CopyMemory pString2, pString1, LenB(String1)    Debug.Print String2End Sub
[解决办法]
VB code
Option ExplicitPrivate Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _    (pDest As Any, pSource As Any, ByVal byteLen As Long)Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long        String1 = "PowerVB"    String2 = String$(7, 0)        'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, ByVal VarPtr(String1), 4    CopyMemory String2, pString1,  Len(String2)    Debug.Print StrConv(String2, vbFromUnicode)  '得到的是PowerVBEnd SubPrivate Sub Command1_Click()        test2End Sub
[解决办法]
VB code
Option ExplicitPrivate Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _    (pDest As Any, pSource As Any, ByVal byteLen As Long)Sub test2()    Dim String1 As String    Dim String2 As String    Dim pString1 As Long    Dim lngA As Long        String1 = "PowerVB"    String2 = String$(7, 0)        lngA = VarPtr(String1)    'Try three: Get the string's pointer from VarPtr    CopyMemory pString1, ByVal lngA, LenB(pString1)    CopyMemory String2, pString1, 14    Debug.Print String2 '得到的不是PowerVB,而是“P o w e”?为什么?End SubPrivate Sub Command1_Click()    Call test2End Sub
[解决办法]
千万别用没分配内存的指针(pString1)来拷贝,否则很容易崩溃的

[解决办法]
探讨
另外一个问题是:String2 = String$(7, 0)。这一句是必要的,相当于给它初始申请内存,对么?


[解决办法]
不可能波及整个系统,因为VB之所以崩溃,就是因为系统搞的鬼,它为了自己没事,把VB搞掉了
[解决办法]
以下代码可以Copymemory中英文混合字符串(至于中英文之外的可以根据具体情况调整)
VB code
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)Private Function GetStringLenght(ByVal strString As String) As Integer   Dim nLen As Integer   Dim oneChar As String   Dim i As Integer      GetStringLenght = 0   nLen = Len(strString)   For i = 1 To nLen       oneChar = Mid(strString, i, 1)       If CLng("&H" & Hex(AscW(oneChar))) > 255 Then        '具体汉字的编码范围可以再查证,这里只是做一个推测。           GetStringLenght = GetStringLenght + 2       Else           GetStringLenght = GetStringLenght + 1       End If   Next iEnd FunctionPrivate Sub Form_Load()Dim String1C As String, String1E As String    Dim String2_7 As String, String2_14 As String    Dim String2_1C As String    Dim String2_1E As String    Dim nLenght As Integer        String1C = "我有点Slow"    String1E = "WYDSlow"        nLenght = GetStringLenght(String1C)    String2_1C = Space(nLenght)    CopyMemory ByVal String2_1C, ByVal String1C, nLenght    Debug.Print String2_1C & "*", Len(String2_1C),nLenght     nLenght = GetStringLenght(String1E)    String2_1E = Space(nLenght)    CopyMemory ByVal String2_1E, ByVal String1E, nLenght    Debug.Print String2_1E & "*", Len(String2_1E),nLenghtEnd Sub 


[解决办法]
整如36楼所说的,操作系统为了保护自己,会强行关闭它自认为潜在的“威胁”。而一旦保护不了,而且出错,就出现所谓的“蓝屏”
[解决办法]

VB code
'说明'XX - 源值确定 And 目标溢出'?? - 源值不确定'?X - 源值不确定 And 目标溢出String1C = "我有点Slow"'Unicode : 11-62-09-67-B9-70-53-00-6C-00-6F-00-77-00'Ansi    : CE-D2-D3-D0-B5-E3-53-6C-6F-77String1E = "WYDSlow"'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00'Ansi    : 57-59-44-53-6C-6F-77String2_7 = String$(7, 0)'Unicode : 00-00-00-00-00-00-00-00-00-00-00-00-00-00'Ansi    : 00-00-00-00-00-00-00String2_14 = String$(14, 0)'Unicode : 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00'Ansi    : 00-00-00-00-00-00-00-00-00-00-00-00-00-00CopyMemory ByVal String2_7, ByVal String1C, 7   '例1 我有点S*         4  8'Ansi    : CE-D2-D3-D0-B5-E3-53 ;复制 7 字节'Unicode : 11-62-09-67-B9-70-53-00CopyMemory ByVal String2_14, ByVal String1C, 7  '例2 我有点S       * 11 22'Ansi    : CE-D2-D3-D0-B5-E3-53-00-00-00-00-00-00-00 ;复制 7 字节,后 7 字节不变'Unicode : 11-62-09-67-B9-70-53-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00CopyMemory ByVal String2_7, ByVal String1E, 7   '例3 WYDSlow*         7 14'Ansi    : 57-59-44-53-6C-6F-77 ;复制 7 字节'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00CopyMemory ByVal String2_14, ByVal String1E, 7  '例4 WYDSlow       * 14 28'Ansi    : 57-59-44-53-6C-6F-77-00-00-00-00-00-00-00 ;复制 7 字节,后 7 字节不变'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00CopyMemory ByVal String2_7, ByVal String1C, 14  '例5 我有点S*         4  8'Ansi    : CE-D2-D3-D0-B5-E3-53-XX-XX-XX-?X-?X-?X-?X ;复制 14 字节,后 7 字节溢出(丢弃)'Unicode : 11-62-09-67-B9-70-53-00CopyMemory ByVal String2_14, ByVal String1C, 14 '例6 我有点Slow    * 11 22'Ansi    : CE-D2-D3-D0-B5-E3-53-6C-6F-77-??-??-??-?? ;复制 14 字节,后 4 字节不确定'Unicode : 11-62-09-67-B9-70-53-00-6C-00-6F-00-77-00-??... ;长度也不确定CopyMemory ByVal String2_7, ByVal String1E, 14  '例7 WYDSlow*         7 14'Ansi    : 57-59-44-53-6C-6F-77-?X-?X-?X-?X-?X-?X-?X ;复制 14 字节,后 7 字节溢出(丢弃)'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00CopyMemory ByVal String2_14, ByVal String1E, 14 '例8 WYDSlow       * 14 28'Ansi    : 57-59-44-53-6C-6F-77-??-??-??-??-??-??-?? ;复制 14 字节,后 7 字节不确定'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00-??... ;长度也不确定CopyMemory ByVal String2_7, ByVal String1C, 28  '例9 我有点S*         4  8'Ansi    : CE-D2-D3-D0-B5-E3-53-XX-XX-XX-?X-?X-?X... ;复制 28 字节,后 21 字节溢出(丢弃)'Unicode : 11-62-09-67-B9-70-53-00CopyMemory ByVal String2_14, ByVal String1C, 28 '例10 我有点Slow    * 11 22'Ansi    : CE-D2-D3-D0-B5-E3-53-6C-6F-77-??-??-??-??-?X... ;复制 28 字节,4 字节不确定,后 14 字节溢出(丢弃)'Unicode : 11-62-09-67-B9-70-53-00-6C-00-6F-00-77-00-??... ;长度也不确定CopyMemory ByVal String2_7, ByVal String1E, 28  '例11 WYDSlow*         7 14'Ansi    : 57-59-44-53-6C-6F-77-?X-?X-?X-?X-?X-?X-?X... ;复制 28 字节,后 21 字节溢出(丢弃)'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00CopyMemory ByVal String2_14, ByVal String1E, 28 '例12 WYDSlow       *         14    28'Ansi    : 57-59-44-53-6C-6F-77-??-??-??-??-??-??-??-?X... ;复制 28 字节,7 字节不确定,后 14 字节溢出(丢弃)'Unicode : 57-00-59-00-44-00-53-00-6C-00-6F-00-77-00-??... ;长度也不确定
[解决办法]
其实CopyMemory等函数是VB学习者的一个槛,过了这个槛技术水平就要“上升”一个层次可。
[解决办法]
可以说理解一半,也有理解错了的,不是api把什么当做是Ansi,而是你自己要区分字符串类型,vb默认是unicode.

其实你写的函数不需要那么复杂直接这么就可以了

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal byteLen As Long)

Sub test2()
Dim String1 As String
Dim String2 As String
Dim pString1 As Long

String1 = "PowerVB"
String2 = String$(7, 0)

'Try three: Get the string's pointer from VarPtr


' CopyMemory pString1, ByVal VarPtr(String1), 4
' CopyMemory ByVal String2, ByVal pString1, 14
CopyMemory ByVal String2, ByVal String1, 7

Debug.Print String2 '得到的不是PowerVB,而是“P o w e”?为什么?
End Sub
[解决办法]
好了我刚才稍微验证了一下的出结论了

写下测试代码如下:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal byteLen As Long)

Sub test2()
Dim String1 As String
Dim String2 As String
Dim pString1 As Long

String1 = "PowerVB"
String2 = String$(14, 0)

'Try three: Get the string's pointer from VarPtr
' CopyMemory pString1, ByVal VarPtr(String1), 4
' CopyMemory ByVal String2, ByVal pString1, 14
CopyMemory ByVal String2, ByVal StrPtr(String1), 14

Debug.Print StrConv(String2, vbFromUnicode) '这时字符串是一个Unicode字符串所以要打印出来需要转换一下
End Sub


Private Sub Form_Load()
test2
End Sub

从这里代码可以看出VB默认是按Unicode处理是正确的
因为StrPtr(String1)得到的是字符串在内存中的开始地址
那么这段地址指向的就是一串Unicode字符串我用内存察看工具也再次验证
这时我们分配的内存就应该是14个字节不然只能拷贝一半

再看下面代码

Option Explicit


Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal byteLen As Long)

Sub test2()
Dim String1 As String
Dim String2 As String
Dim pString1 As Long

String1 = "PowerVB"
String2 = String$(7, 0)

'Try three: Get the string's pointer from VarPtr
' CopyMemory pString1, ByVal VarPtr(String1), 4
' CopyMemory ByVal String2, ByVal pString1, 14
CopyMemory ByVal String2, ByVal String1, 7

Debug.Print String2
End Sub


Private Sub Form_Load()
test2
End Sub

这是因为有了vb内部修饰符号Byval 所以vb会自动转换成Ansi字符串所以我们申请的内存只需要7个字节就行了
[解决办法]
Option Explicit
'Ansi版(中文)

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal byteLen As Long)
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long

Sub test2()
Dim String1 As String
Dim String2 As String
Dim Length As Long

String1 = "abc我爱你"
Length = lstrlen(String1)
String2 = String$(Length, 0)

CopyMemory ByVal String2, ByVal String1, Length

Debug.Print String2
End Sub


Private Sub Form_Load()
test2
End Sub
[解决办法]
Option Explicit
'Unicode版(中文)

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, pSource As Any, ByVal byteLen As Long)

Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As String) As Long


Sub test2()
Dim String1 As String
Dim String2 As String
Dim Length As Long

String1 = "abc我爱你"
Length = LenB(String1)
String2 = String$(Length, 0)

'或者直接转换成Ansi就用
'CopyMemory ByVal StrPtr(String2), ByVal StrPtr(String1), Length
CopyMemory ByVal String2, ByVal StrPtr(String1), Length

Debug.Print StrConv(String2, vbFromUnicode)
End Sub


Private Sub Form_Load()
test2
End Sub


[解决办法]
VB 对所有 API 中的 String 参数,调用前进行 Unicode-Ansi 转换,调用后进行 Ansi-Unicode 转换。
------解决方案--------------------


探讨
此外,还需要注意初始化string.
如何非要用varptr,则要使用两次copymemory,因为string是间接指针.

[解决办法]
探讨

[解决办法]
更正:
String2(Unicode) : 50-00-00-00-6F-00-00-00-77-00-00-00-65-00 

[解决办法]
按照 0 楼定义,参数都是 Any 的情况下
(1)ByVal String2
字符串参数,自动转换。
(2)ByVal StrPtr(String1)
指针,不转换。
(3)ByRef String1
编译错误,虽然去掉 ByRef 可以通过编译,其实 Any 类型的参数不支持这种用法,导致无法预期的结果甚至程序崩溃。
(4)ByVal VarPtr(String1)
指针的指针,不转换。但是这其实是变量 String1 所在的位置,不当操作也会导致无法预期的结果甚至程序崩溃。

[解决办法]
探讨
(2)请问中文字符串的Unicode-Ansi 转换是如何进行的?

String1C = "我有点"
'Unicode : 11-62-09-67-B9-70
'Ansi    : CE-D2-D3-D0-B5-E3

如果1162是我的Unicode编码,CED2是对应的ANSI编码。
那如果我把1162当做ANSI编码,并试图把它对应Unicode编码找到,会发生什么?

[解决办法]
探讨弱弱的问下,你在哪儿查的Unicode码和ANSI码啊?

[解决办法]
《高级Visual Basic编程》(Advanced Visual Basic)的 14 章对 VB 字符串有介绍。

宝书啊宝书,又用到它了。
[解决办法]
探讨另,我感觉你50楼第一个例子的结论也许错了。因为UA 和 AU转换自动进行,你觉得好像缺省用了Unicode,其实内部还是ANSI的。

[解决办法]
学习,帮顶,路过,灌水,蹭分........

我当初玩这东西时是笨办法,硬整的.....

直接在每一步CopyMemory前调用MessageBoxA,然后编译后到OD里对MSGBOX下断&单步,看看最根本的汇编代码是怎么搞的.....

然后就一下子明白本质了.....就没问题了....

热点排行