在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

相关文章

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

C和指针:字符串

字符串、字符和字节 字符串基础 字符串就是一串零个或多个字符,并且以一个位模式为全0的NUL字节结尾。 字符串长度就是字符串中字符数。 size_t strlen( char const *string ); string为指针常量(const修饰string),指向的string是常量不能修改。size_t是无符号数,定义在stddef.h。 #include <stddef.h>

PHP字符串全排列

方法一: $str = 'abc';$a =str_split($str);perm($a, 0, count($a)-1);function perm(&$ar, $k, $m) {if($k == $m){ echo join('',$ar), PHP_EOL;}else {for($i=$k; $i<=$m; $i++) {swap($ar[$k], $ar[$i]);perm($ar

PHP7扩展开发之字符串处理

前言 这次,我们来看看字符串在PHP扩展里面如何处理。 示例代码如下: <?phpfunction str_concat($prefix, $string) {$len = strlen($prefix);$substr = substr($string, 0, $len);if ($substr != $prefix) {return $prefix." ".$string;} else

十一、C语言:字符串函数

目录 一、strlen 二、strcpy 三、strcat  四、strcmp 五、strstr 六、strtok 七、strerror 一、strlen 注意:strlen()函数的返回值是size_t,两个size_t相减仍为无符号数 int main(){char arr[10] = "abc";char brr[10] = "abc123";if (strl

NC 把数字翻译成字符串

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 描述 有一种将字母编码成数字的方式:‘a’->1, ‘b->2’, … , ‘z->26’。 现在给一串数字,返回有多少种可能的译码结果 import java.u

C语言进阶【1】--字符函数和字符串函数【1】

本章概述 字符分类函数字符转换函数strlen的使用和模拟实现strcpy的使用和模拟实现strcat的使用和模拟实现strcmp的使用和模拟实现彩蛋时刻!!! 字符分类函数 字符: 这个概念,我们在以前的文章中讲过了。我们键盘输入的信息都是字符。字符大体可以分为两类——单个字符,字符串。而单个字符又可以进行分类——字母字符,数字字符,特殊字符和不可见字符。进行思维图展示: 在日

nyoj 685 查找字符串

当初一开始没做出来。 后来,学习过一段时间之后,在返回来做这道题,忽然发现,map类容器可以做。 PS:需要注意的是:此题如果用c++的输入输出的话,会超时。 O(time):gets()<  scanf() < cin。   附上代码: #include<stdio.h>#include<map>#include<string>#include<string.h>usin

Linux 使用rsync拷贝文件

显示进度条 rsync 可以显示进度条,您可以使用 --progress 或 -P 选项来显示每个文件的传输进度和已完成文件的统计信息。 显示进度条的常用选项: --progress 选项 使用 --progress 显示每个文件的传输进度信息:rsync -av --progress /src/ /dest/ -a:归档模式,表示递归拷贝并保持文件权限、时间戳等。-v:详细模式,显示更

python基础语法十一-赋值、浅拷贝、深拷贝

书接上回: python基础语法一-基本数据类型 python基础语法二-多维数据类型 python基础语法三-类 python基础语法四-数据可视化 python基础语法五-函数 python基础语法六-正则匹配 python基础语法七-openpyxl操作Excel python基础语法八-异常 python基础语法九-多进程和多线程 python基础语法十-文件和目录操作