在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

相关文章

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)

《MyBatis的xml中字符串类型判空与非字符串类型判空处理方式(最新整理)》本文给大家介绍MyBatis的xml中字符串类型判空与非字符串类型判空处理方式,本文给大家介绍的非常详细,对大家的学习或... 目录完整 Hutool 写法版本对比优化为什么status变成Long?为什么 price 没事?怎

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

C# $字符串插值的使用

《C#$字符串插值的使用》本文介绍了C#中的字符串插值功能,详细介绍了使用$符号的实现方式,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录$ 字符使用方式创建内插字符串包含不同的数据类型控制内插表达式的格式控制内插表达式的对齐方式内插表达式中使用转义序列内插表达式中使用

详解MySQL中JSON数据类型用法及与传统JSON字符串对比

《详解MySQL中JSON数据类型用法及与传统JSON字符串对比》MySQL从5.7版本开始引入了JSON数据类型,专门用于存储JSON格式的数据,本文将为大家简单介绍一下MySQL中JSON数据类型... 目录前言基本用法jsON数据类型 vs 传统JSON字符串1. 存储方式2. 查询方式对比3. 索引

MySQL字符串常用函数详解

《MySQL字符串常用函数详解》本文给大家介绍MySQL字符串常用函数,本文结合实例代码给大家介绍的非常详细,对大家学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql字符串常用函数一、获取二、大小写转换三、拼接四、截取五、比较、反转、替换六、去空白、填充MySQL字符串常用函数一、

Python中反转字符串的常见方法小结

《Python中反转字符串的常见方法小结》在Python中,字符串对象没有内置的反转方法,然而,在实际开发中,我们经常会遇到需要反转字符串的场景,比如处理回文字符串、文本加密等,因此,掌握如何在Pyt... 目录python中反转字符串的方法技术背景实现步骤1. 使用切片2. 使用 reversed() 函

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

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