在VB6中用CopyMemory拷贝字符串的种种猫腻(四)

2024-01-09 14:58

本文主要是介绍在VB6中用CopyMemory拷贝字符串的种种猫腻(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

版权声明:可以任意转载,转载时请务必以超链接形式标明如下文章原始出处和作者信息及本声明
作者:xixi
出处:http://blog.csdn.net/slowgrace/archive/2009/09/14/4550530.aspx

本文来自此帖的冗长讨论,感谢Tiger_Zhao的全程指点和陈辉、阿勇、马云剑等很多朋友的热心参与。本文其他部分在:(一)、(二)、(三)。

第四节 如何用CopyMemory正确的拷贝字符串
分析了这么多有问题的代码,我们来看看如何用CopyMemory正确的拷贝字符 串。假设String1 = “我有点Slow”
(1) 最简单的不会惹麻烦的方法是直接传地址。像下面这样
String2 = String$(Len(String1), 0) '14 bytes,和String1一样大小就OK了
CopyMemory ByVal StrPtr(String2), ByVal StrPtr(String1), LenB(String1)
这种方法由于不涉及UA/AU转换,要拷贝的就是Unicode字符串本身,所以字节数就取LenB(String1)就可以了,而String2的初始长度只要够接受String1的所有字符就行,所以就取Len(String1)。注意:1个是LenB,1个是Len,别弄混了。
(2) 或者你不嫌VB妈妈烦,像下面这样做
lngALen = LenB(StrConv(String1, vbFromUnicode))
String2 = String$(lngALen, 0)
CopyMemory ByVal String2, ByVal String1, lngALen
这里要拷贝的字节数和String2的初始化字符数都是用的lngALen,也即String1对应的ANSI字符串的字节数。这是因为传给CopyMemory的实际上是ANSI字符串临时变量_tmp1,所以要拷贝的字节数当然要按这个算;另一方面,String2初始成lngALen个0后(每个0字符占两个字节),它对应的ANSI字符串的字节数(每个0字符缩减为1个字节)就是lngALen,这样可以确保转换后的ANSI字符串_tmp2可以接收完整的_tmp1的字符串。
另外,要注意,在这种用法里,String2的初始长度不必等于它的最终长度。因为String2的字符串缓冲区在API函数调用完毕后要重新分配的,所以string2的初始长度只由_tmp1的LenB长度决定。附上完整的代码:
可以看到,推荐的方法有个共同的特征,就是目标地址和源地址的对称性。如果传Long就两者都传Long,如果传字符串就两者都传字符串,这样才能保证得到正确的结果;如果一个地址传Long,一个地址传字符串,那就会出问题,原因嘛,其实也就是因为VB妈妈对一个做了UA/AU转换,对另一个没做,导致互相对不上号,就要多加额外的语句来进行修正。
另外要注意正确处理好如下两个数字:
(1)CopyMemory的第3个参数byteLen。根据源地址参数是否涉及UA/AU转换而定,涉及的话就取对应的ANSI字符串的字节数,不涉及的话就直接LenB(String1)
(2)String2的初始字符数(注意是字符数不是字节数)。根据目标地址参数是否涉及UA/AU转换而定,涉及的话就保证对应的ANSI字符串字节数等于byteLen,不涉及的话就保证Unicode字符串字节数等于byteLen。
第五节 练习:这些回复你都能读懂么?
除了( 二)、( 三)详细讨论过的,还有许多朋友在 这个帖子里给出了自己的修正代码,不妨看一遍当做练习巩固一下你对本文的学习成果。综述如下:
8楼,正确。
这个实际是对0楼代码的直接修正。先扩充String2的长度以确保对应的_tmp2长度足够;最后再做UA转换以抵消VB多做的一次AU转换。
9楼,正确。
不经过UA/AU转换,直接拷贝字符串缓冲区。
12楼,正确。
不经过UA/AU转换,直接拷贝字符串缓冲区。
15楼(1),正确。
目的和源同时做UA/AU转换,直接拷贝字符串缓冲区。
15楼(2),正确。
不经过UA/AU转换,直接拷贝字符串缓冲区。
22楼,错误。
同10楼。见248楼解释。
23楼,错误。
同10楼。见248楼解释。
38楼,正确。
直接传递字符串参数要得到正确结果,很重要的一点是要算对字节数。坏上帝专门写了个函数来算ASCI字符串所占的字节数,所以能够得到正确的结果。不过的话,我觉得这个GetStringLength函数完全可以简化为这一条语句:LenB(StrConv("我有点slow", vbFromUnicode))
46楼,正确。
同15楼第一种方法。
50楼(1),正确。
同8楼。
50楼(2),正确。
同15楼第一种方法。(但解释不完全准确:)
57楼,正确。
这个其实同15楼第一种方法,但是更通用。在直接传字符串参数的时候,要用ANSI字符串来计算字节。
58楼,不完全正确。
String2的初始化长度取为Len(String1)就可以了。
62楼,正确。
用ByVal VarPtr(ByVal String1),呵呵,真够绕的。
129楼,错误。
拷贝字符串缓冲区指针会导致重复释放内存。见134,141楼解释。
165楼,正确。同9楼。
但使用了Len和LenB函数,使得代码更通用了。
231楼,错误。
拷贝字符串缓冲区指针会导致重复释放内存和内存泄露。见134,141楼解释。
237楼,错误。
拷贝字符串缓冲区指针会导致重复释放内存和内存泄露。见134,141楼解释。
第六节 补充提问
6.1 如何查看内存
其实要熟悉CopyMemory函数的话,猜来猜去不如直接看内存。怎样看内存呢?可以把目标地址的内存CopyMemory 到数组中,然后逐个字节 Hex。分析内存经常需要这样做。
另外还可以用一些辅助调试工具。比如OllyDbg:RING3下的调试工具,可用于实时浏览各变量值或者寄存器的值。再比如:Ida和冰刃等静态反汇编的工具可以帮助你了解高级语言的实际执行动作。
6.2 在VB6中调用API使用字符串参数时有可能避开UA/AU转换么
VB调用API,总是要经过一个中介,msvbvm60.dll中的DllFunctionCall,这是UA/AU转换的始作俑者。如果利用自己定义的TLB绕过DllFunctionCall,字符串就不会进行UA/AU转换了。
6.3 所有的API函数都把字符串参数当做ANSI字符串处理么
答案是否。有些API函数是,有些不是。对于期待Unicode字符串的API函数(成为W版API),我们直接通过VB调用传给它字符串参数就会出问题。因为VB不会管API函数期待的是啥,都会一股脑地给转成ANSI字符串才传。所以,对于不同类型的API我们需要区别对待,详见 这篇博文。
第七节 全文摘要
在VB6中要使用CopyMemory函数正确的拷贝字符串有点啰嗦。本文通过分析若干段经典代码,介绍了这其中涉及的各方面的知识,包括:
(1)VB6对字符串参数的自动UA/AU转换机制
(2)关于VB中的字符串:BSTR结构、StrPtr和VtrPtr函数的意义、字符串内存的初始化、查看字符串编码的方法、VB6释放字符串变量的方法等。
(3)关于CopyMemory函数;它的各种不同形式参数的含义、它如何自动处理覆盖、VB6调用CopyMemory后如何确定String2的长度等。
(4)其他内容:大端序、小端序、ANSI编码的内存结构、栈、关于VB崩溃的处理、查看内存的方法、用Tlb避开UA/AU转换的方法等。

附:经典代码段索引
第一节:详细图示、“表”示and“文字”示VB6对字符串参数的自动UA/AU转换机制。
第三节:例1:非对称的参数形式导致非对称的UA/AU转换,之后初始化字节数不够导致结果被截短;例2:非对称的参数形式导致非对称的UA/AU转换,得到“浮肿”的结果;例3:直接用String1传参数带来的危险后果。例4,本该传值却传了地址之后的后果;例5,中英文混杂的字符串如何算字节数;例6,危险的替换指针法。
第四节:正确方法剖析。
第五节:超多其他代码的简析。
全文完

这篇关于在VB6中用CopyMemory拷贝字符串的种种猫腻(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/587511

相关文章

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

MySQL 获取字符串长度及注意事项

《MySQL获取字符串长度及注意事项》本文通过实例代码给大家介绍MySQL获取字符串长度及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 获取字符串长度详解 核心长度函数对比⚠️ 六大关键注意事项1. 字符编码决定字节长度2

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

使用Python实现base64字符串与图片互转的详细步骤

《使用Python实现base64字符串与图片互转的详细步骤》要将一个Base64编码的字符串转换为图片文件并保存下来,可以使用Python的base64模块来实现,这一过程包括解码Base64字符串... 目录1. 图片编码为 Base64 字符串2. Base64 字符串解码为图片文件3. 示例使用注意

golang float和科学计数法转字符串的实现方式

《golangfloat和科学计数法转字符串的实现方式》:本文主要介绍golangfloat和科学计数法转字符串的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望... 目录golang float和科学计数法转字符串需要对float转字符串做处理总结golang float

Python如何判断字符串中是否包含特殊字符并替换

《Python如何判断字符串中是否包含特殊字符并替换》这篇文章主要为大家详细介绍了如何使用Python实现判断字符串中是否包含特殊字符并使用空字符串替换掉,文中的示例代码讲解详细,感兴趣的小伙伴可以了... 目录python判断字符串中是否包含特殊字符方法一:使用正则表达式方法二:手动检查特定字符Pytho

MySQL 字符串截取函数及用法详解

《MySQL字符串截取函数及用法详解》在MySQL中,字符串截取是常见的操作,主要用于从字符串中提取特定部分,MySQL提供了多种函数来实现这一功能,包括LEFT()、RIGHT()、SUBST... 目录mysql 字符串截取函数详解RIGHT(str, length):从右侧截取指定长度的字符SUBST

Python将字符串转换为小写字母的几种常用方法

《Python将字符串转换为小写字母的几种常用方法》:本文主要介绍Python中将字符串大写字母转小写的四种方法:lower()方法简洁高效,手动ASCII转换灵活可控,str.translate... 目录一、使用内置方法 lower()(最简单)二、手动遍历 + ASCII 码转换三、使用 str.tr

Java如何用乘号来重复字符串的功能

《Java如何用乘号来重复字符串的功能》:本文主要介绍Java使用乘号来重复字符串的功能,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java乘号来重复字符串的功能1、利用循环2、使用StringBuilder3、采用 Java 11 引入的String.rep