




已阅读5页,还剩29页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第二章、字节集字节集是易语言独有的基本数据类型,按字面的意思来理解,所谓“字节集”就是“字节的集合”,其本质就是字节数组。从计算机基础知识中,我们知道,一个字节就是8位(bit),也就是8个“0”或“1”。计算机中所有的东西(指令和数据)都是用0和1表示的,而以字节作为保存数据的最小单位,所以,字节集可以保存任何的数据数字、文本、声音、图像、可执行文件等等;反过来,一段字节集数据具体表示什么,关键看你是如何解读它你可以认为它是一段文本、一张图片或是一首mp3中的一段。易语言的核心支持库提供了很多字节集相关的函数(2-a),这些函数使得我们对字节集的处理异常方便。我们先来看看易语言本身对这些函数的简要介绍。字节集操作命令简要说明取字节集长度取字节集型数据的长度。到字节集将指定数据转换为字节集后返回转换结果。取字节集数据取出字节集中指定位置指定数据类型的数据。取字节集左边返回一个字节集,其中包含指定字节集中从左边算起指定数量的字节。取字节集右边返回一个字节集,其中包含指定字节集中从右边算起指定数量的字节。取字节集中间返回一个字节集,其中包含指定字节集中从指定位置算起指定数量的字节。寻找字节集返回一字节集在另一字节集中最先出现的位置,位置值从 1 开始。如果未找到,返回 -1。倒找字节集返回一字节集在另一字节集中最后出现的位置,位置值从 1 开始。如果未找到,返回 -1。字节集替换将指定字节集的某一部分用其它的字节集替换,然后返回替换后的结果。子字节集替换返回一个字节集,该字节集中指定的子字节集已被替换成另一子字节集,并且替换发生的次数也是被指定的。取空白字节集返回具有特定数目 0 字节的字节集。取重复字节集返回一个字节集,其中包含指定次数的字节集重复结果。分割字节集将指定字节集进行分割,返回分割后的一维字节集数组。指针到字节集返回指定内存指针所指向地址处的一段数据,注意调用本命令前一定要确保所提供的内存地址段真实有效。本命令的最佳使用场合就是在易语言回调子程序和易语言DLL公开子程序用作获取外部数据。这些函数的使用都很简单,但有些函数依然会使人迷惑,或者想更深入地了解其中的相关细节。所以我们先围绕某些函数作一点深入的讨论。2.1 深入讨论字节集相关函数2.1.1 取字节集长度首先我们来研究一下“取字节集长度”函数是如何取得一个字节集长度的,因为它的效率决定了我们是否适合把它放在循环体中执行。系统要计算一段字节集的长度,不外乎有两个方法: 逐一累计,也就是把字节一个一个地数出来。 把字节集的长度存放在某个特殊的地方,需要的时候读取出来,在字节集操作的过程中即时更新该长度数据。系统具体采用的是哪一种方法,我们作一个简单的测验便知。新建一个易语言程序,在窗体上放一个按钮,为该按钮写如下代码:.版本 2.子程序 _按钮测试计算方式_被单击.局部变量 数据, 字节集.局部变量 上次时间数据 取空白字节集 (1) 字节集的长度不论是1还是1000000,计算的时间不变上次时间 取启动时间 ().计次循环首 (1000000, ) 取字节集长度 (数据).计次循环尾 ()输出调试文本 (取启动时间 () 上次时间)这段代码先分配一定长度的字节集数据,然后执行“取字节集长度”函数一百万次,我的机器测得所花的时间是31毫秒左右。如果你的机器速度很快,测得的时间是0毫秒,请将循环次数增加。然后我把字节集数据的长度改为1000000,再次运行该程序,测得的结果依然大约是31毫秒。由此可见,易语言的“取字节集长度”函数并不是蠢蠢地一个一个字节字节地数,而是把字节集的长度存放在了某个特殊的地方,需要的时候就把它读出来。那么具体存放在何处呢? 这也有几种可能: 存放在字节集的开始处。 存放在字节集的末尾处。 存放在字节集开始处更前面的位置。 存放在内存堆栈中的某个表中,然后与指定的字节集变量建立联系。很显然,存放在字节集末尾的可能性很小,不然系统如何知道一段字节集到何处结束?而如果存放在内存中的表中,需要进行额外的查表操作,显得过于烦琐,我们先来测试简单的,这也需要做试验。首先我们需要获得字节集数据的内存地址,这个我们可以通过“取变量地址”函数获得就像第一章中“自定义数据类型的内存存储”一节中那样,如果不太清楚,请先转回去看那一节。不过字节集的变量地址更简单我们只用取字节集的第一个元素的地址就得到了,不用转来转去那么麻烦。得到地址之后,我们就看该地址的第一个整数型数据是否是字节集的长度,如果不是,那显然没有把长度信息放在开头;如果不在开头,我们再把地址指针向内存的低地址方向移动一个整数长度,也就是4个字节,我猜想长度信息也有可能存放在那里。具体的试验代码如下:.版本 2.支持库 spec.子程序 _按钮存储位置_被单击.局部变量 数据, 字节集.局部变量 地址, 整数型.局部变量 临时地址, 整数型.局部变量 临时字节集, 字节集.局部变量 长度, 整数型数据 到字节集 (“abcdefg”) 长度7,你可以更改此字符串,观察其他长度地址 取变量地址 (数据 1) 获得字节集数据的内存地址输出调试文本 (指针到文本 (地址) 此处输出正确的字符串,证明地址是正确的临时字节集 指针到字节集 (地址, 4)长度 取字节集数据 (临时字节集, #整数型, )输出调试文本 (长度) 此处输出错误的结果。地址 地址 4 将内存指针回退一个整数型的数据长度,也就是4个字节临时字节集 指针到字节集 (地址, 4)长度 取字节集数据 (临时字节集, #整数型, )输出调试文本 (长度) 此处输出正确的字节集长度7 由此可见,字节集的长度存在该字节的首地址前面的一个整数中。试验的结果再一次证实了我的猜想。知道了易语言取字节集长度的核心方式,我们就可以大胆地在循环中使用“取字节集长度”函数而不用担心影响效率了,同时也深入地理解了字节集的存储方式。2.1.2 取字节集数据前面我们已经说过,字节集中保存的是什么数据,关键看我们如何转译它。因此我们可以从一个“文本”字节集中读取整数型数据,也可以从一个“整数型”字节集中读取日期时间型数据,也可以在文本和字节集类型中方便地相互转换。此外,系统还提供其他的诸如“到文本”、“到数值”函数,但到底何时采用哪个函数,这是一个问题。我的建议是对字节集数据不要使用“到数值”函数,应该使用“取字节集数据”函数,根据需要传递#整数型、#字节型、#小数型等参数。否则你会发现程序运行的结果不对头,但即使是拿了放大镜也看不出源代码问题在哪里,请看下面的代码片断,输出结果是什么:.版本 2.子程序 _按钮字节集到数值_被单击.局部变量 某字节集, 字节集某字节集 到字节集 (1234)输出调试文本 (到数值 (某字节集)某字节集 到字节集 (“1234”)输出调试文本 (到数值 (某字节集)也许你觉得至少有一个要输出1234吧,但很不幸,输出结果却都是0。但使用“输出调试文本(取字节集数据(某字节集,#整数型,)”可以得到可预见的正确数据。其次是关于“到文本()”和“取字节集数据(.,#文本型,)”的区别问题。对于字节集参数来说,“到文本()”相当于“取字节集数据(.,#文本型,1)”,显然,“取字节集数据()”功能要强大些,可以取指定位置的文本数据。我们知道,在易语言中,文本变量是以0作为文本的结尾的,而一段字节集中可能有多个0分割的文本,“到文本()”函数只能取出第一个文本。请看示例代码:.版本 2.子程序 _按钮字节集到文本_被单击.局部变量 某字节集, 字节集某字节集 到字节集 (“abcdefghijklmnopq”)输出调试文本 (到文本 (某字节集) 输出全部某字节集 8 0 插入一个输出调试文本 (到文本 (某字节集) 文本被截断,只输出abcdefg输出调试文本 (取字节集数据 (某字节集, #文本型, 1) 与上一行输出结果相同输出调试文本 (取字节集数据 (某字节集, #文本型, 9) 输出后一半,ijklmnopq由代码的运行结果可以看出,“取字节集数据(.,#文本型,)”很灵活,可以任意指定从指定位置开始的文本,直到遇到结束。此外,“取字节集数据”函数还允许我们像读取文件那样按顺序读取一段数据,请看易语言集成开发环境中对“取字节集数据”函数第三个参数的描述:“参数的名称为“起始索引位置”,类型为“整数型(int)”,可以被省略。指定从字节集的什么地方开始取数据,索引值从1开始。如果被省略,默认为数值1。如果为本参数提供一个整数型变量,则命令执行后将自动修改该变量内容,将其索引值移动到下一个读入位置。如果移动后到达字节集的末尾,将修改该变量的内容为-1。”具体的使用代码如下:.版本 2.子程序 _按钮顺序读取_被单击.局部变量 某字节集, 字节集.局部变量 指针位置, 整数型某字节集 到字节集 (“”) 0 注意文本后面添加一个表示文本结束某字节集 某字节集 到字节集 (12345)某字节集 某字节集 到字节集 (2003年10月1日)指针位置 1输出调试文本 (取字节集数据 (某字节集, #文本型, 指针位置) 输出 输出调试文本 (取字节集数据 (某字节集, #整数型, 指针位置) 输出 12345输出调试文本 (取字节集数据 (某字节集, #日期时间型, 指针位置) 输出 2003年10月1日输出调试文本 (指针位置) 输出 -1 ,表示到字节集尾。2.1.3 指针到字节集易语言自身对此函数的附加说明是这样的:“本命令的最佳使用场合就是在易语言回调子程序和易语言DLL公开子程序用作获取外部数据。”,其实“指针到字节集”函数的实质是读取本进程内指定内存位置的指定长度的数据,因此,我们可以使用此函数遍历本进程的所有内存空间,当然,这需要你对Windows的内存管理机制有足够的了解才行。因为还没有涉及到回调函数和DLL编程,这里我们以一个小例子来试验一下“指针到字节集”的神奇之处。Windows在执行一个可执行文件的时候,首先会把该文件映射到该进程内存的0x400000地址处,这是一个十六进制的地址,转换成十进制是4194304,我们就从该地址读取10KB的数据看看是什么,具体代码如下:.版本 2.子程序 _按钮从内存读取本执行文件映像_被单击.局部变量 某字节集, 字节集某字节集 指针到字节集 (4194304, 10 1024) 从4194304处读取10KB的数据。写到文件 (“c:asdf.dat”, 某字节集) 将该数据写到一个临时文件中运行 (“notepad.exe c:asdf.dat”, 假, ) 用记事本打开查看删除文件 (“c:asdf.dat”)运行结果如图2.1.3-a所示:图2.1.3-a 指针到字节集试验运行结果如果你是一位有心人,你就会发现这些“乱码”很眼熟这不就是一个exe文件的内容吗?!正是,这个小试验就证实了“指针到字节集”实际上读取的是本进程的指定内存地址的数据。关于“指针到字节集”的更多用法,请参阅后续的DLL编程相关章节。2.2 例 十六进制查看器首先我们演示一下字节集的字节数组特性,我们来写一个小程序,把任何文件的内容以十六进制的形式显示出来。市面上早就有很多这样的软件了,而且已经非常成熟,比如WinHex,UltraEdit,HexWorkShop 等(图2.1-a是UltraEdit 显示的一个exe文件的部分数据),但有时候我们的软件也需要我们自己来实现这样的功能,比如我们要写串口通信程序、内存查看器等。先进行问题分析。为了便于代码重用,我们决定把这个功能写成一个函数,该函数接收三个参数:要转换的字节集。是否显示数据地址。是否显示文本。图2.2-a UltraEdit 中显示一个Exe文件的16进制数据函数返回转换后的文本,这样我们就可以很方便地显示到编辑框等控件中了。在转换的过程中,需要把字节集掰成一个个的字节,我们使用易语言自带的“取十六进制文本”把它转换成十六进制的形式。但这里有一个问题:如果字节的值小于或等于16,那么转换后就是一位,而大于16的转换后就是两位,要是文本整齐美观,对于小于一位的,就需要在前面补0;还有,如果要显示数据地址,则也需要补齐长度;我们决定每行显示16个字节,那么字节集的长度有可能不是16的整数倍数,那么也需要在文本的后面补上空白文本以对齐。显然,我们需要写一个这样的函数,该函数能够在指定的文本前后添加重复的字符,使该文本达到指定的长度。这个函数的代码如下:.版本 2.子程序 填充重复文本, 文本型, , 将文本的左侧或右侧添加字符到指定的长度.参数 参原始文本, 文本型.参数 参要达到的长度, 整数型.参数 参填充的字符, 文本型, 可空, 取第一个字符.参数 参在左侧, 逻辑型, 可空, 不在左侧就在右侧.局部变量 要增加的长度, 整数型.局部变量 字符, 文本型.局部变量 文本, 文本型.如果真 (是否为空 (参填充的字符) 参填充的字符 “ ” 默认使用空格填充.如果真结束.如果真 (是否为空 (参在左侧) 参在左侧 真 默认填充左侧.如果真结束.如果真 (参填充的字符 “”) 返回 (参原始文本).如果真结束要增加的长度 参要达到的长度 取文本长度 (参原始文本).如果真 (要增加的长度 0) 返回 (参原始文本).如果真结束字符 取文本左边 (参填充的字符, 1) 只要第一个字符文本 取重复文本 (要增加的长度, 字符).如果真 (参在左侧) 返回 (文本 参原始文本).如果真结束返回 (参原始文本 文本)我们的计划是每行显示16个字节,可以指定是否显示地址和文本。所以我们需要把一行文本分为三段:左侧的地址、中间的十六进制文本、右边的普通文本。左侧的地址我们可以直接通过循环的计数器得到;中间的我们把一个个字节转换成16进制文本后累加;右侧的文本则麻烦一些,因为有些控制字符是无法显示出来的(比如空字符、回车、换行、响铃、退格等),这些字符的ASCII码都小于32,这里我们按照惯例,统一把它们转换成“.”,其ASCII码是46。当计数器与16的余数是0的时候,就表示已完成一行,我们在其中插入换行符。具体的实现代码如下:.版本 2.子程序 字节集到十六进制文本, 文本型.参数 参数据, 字节集.参数 参是否显示地址, 逻辑型.参数 参是否显示文本, 逻辑型.局部变量 数据长度, 整数型.局部变量 i, 整数型.局部变量 结果文本, 文本型.局部变量 一行文本, 文本型.局部变量 某字节, 字节型.局部变量 右侧文本, 文本型数据长度 取字节集长度 (参数据).计次循环首 (数据长度, i) 某字节 参数据 i 一行文本 一行文本 “ ” 填充重复文本 (取十六进制文本 (某字节), 2, “0”) .如果真 (参是否显示文本) .如果真 (某字节 32) 某字节 46 原点符号 .如果真结束 右侧文本 右侧文本 字符 (某字节) .如果真结束 .如果真 (i 16 0) 假定每行16个字节 .如果真 (参是否显示地址) 一行文本 填充重复文本 (取十六进制文本 (i 16), 4, “0”, ) “ | ” 一行文本 .如果真结束 .如果真 (参是否显示文本) 一行文本 一行文本 “ | ” 右侧文本 .如果真结束 结果文本 结果文本 一行文本 #换行符 满一行就插入回车符并添加到结果文本中 一行文本 “” 右侧文本 “” .如果真结束.计次循环尾 () 最后一行有可能数据刚好不能被16整除,则不满足条件i%16=0,所以要补上最后的一段.如果真 (一行文本 “”) .如果真 (取文本长度 (一行文本) 48) 一行文本 填充重复文本 (一行文本, 48, “ ”, 假) .如果真结束 .如果真 (参是否显示地址) 一行文本 填充重复文本 (取十六进制文本 (i i 16), 4, “0”, 真) “ | ” 一行文本 .如果真结束 .如果真 (参是否显示文本) 一行文本 一行文本 “ | ” 右侧文本 .如果真结束 结果文本 结果文本 一行文本 #换行符.如果真结束返回 (结果文本)在窗体上添加一个按钮,标题为“打开文件”;添加两个选择框,分别命名为“显示地址”和“显示文本”,默认均选中;添加一个编辑框,“是否允许多行”属性为“真”,“滚动条”选择“纵向滚动条”;添加一个通用对话框用来打开文件;添加一个程序集变量“集字节集”,我们需要把读入的字节集保存到其中。为按钮、选择框添加以下的代码:.版本 2.子程序 _按钮打开文件_被单击.如果真 (通用对话框1.打开 () 集字节集 取字节集左边 (读入文件 (通用对话框1.文件名), 23 1024) 编辑框1.内容 字节集到十六进制文本 (集字节集, 选择框显示地址.选中, 选择框显示文本.选中).如果真结束=.子程序 _选择框显示地址_被单击编辑框1.内容 字节集到十六进制文本 (集字节集, 选择框显示地址.选中, 选择框显示文本.选中)=.子程序 _选择框显示文本_被单击编辑框1.内容 字节集到十六进制文本 (集字节集, 选择框显示地址.选中, 选择框显示文本.选中)在用户单击“打开文件”按钮后,我们使用“读入文件”函数读取文件,“读入函数”返回的是文件的字节集数据。这里我们考虑到速度的原因,只取其前面的23KB,如果不这样的话,超过100KB的文件就很慢了。而专业的软件如WinHex、UltraEdit等则可以读取多达几百兆甚至上G的文件,而且依然很快,显然是经过优化了的。其优化的方法是这样的:并不是一次将所有的文件读出,而是读取文件的一段数据,比如800个字节,按每行16个字节计,也就是50行。拖动滚动条的时候,将文件的读写指针以每16字节为单位进行偏移读取,转换后更新编辑框。这样每次只处理800个字节,速度当然就很快了(具体的读取方法,请参看下一例:文件分割机)。当然这样的话,就要去掉编辑框的滚动条,然后加上我们自己的滚动条进行额外的处理。这个优化的过程就留给你作为课外练习了,如果有疑问,请上本书的论坛(/)讨论。最后我们运行程序,随便打开一个文件,得到如图2.2-b的运行结果,跟UltraEdit显示的结果是一致的。图2.2-b 文件的16进制显示2.3 例 无需合并程序的文件分割机在软盘流行的时代,也广泛流传着一类软件:文件分割机。这些软件能够把指定的文件分割成指定大小的单个文件,这些单个文件的大小刚好小于或等于软盘的容量,因此可以把它们拷贝入多张软盘。再以软盘为媒介,拷贝到目标机器上,然后用软件自带的功能或仅仅使用DOS批处理文件将它们拼装起来,生成和原始文件一样的文件。虽然现在U盘和大容量移动硬盘的方便、廉价和稳定,早已把软驱和软盘推进了历史,但把写这种软件作为一个练习还是蛮不错的。今天我们就来写一个这样的软件,我们的目标是:可以由用户指定要分割的文件。可以有用户指定分割后的单个文件的大小。使用批处理文件来组装文件。其原因是一方面你不能指望目标机器也安装了你的分割机,另一方面批处理文件的尺寸非常小,便于软盘拷贝。为了方便生成最后的批处理文件,我们先来练习一个DOS命令:Copy。Copy 命令用来复制文件,其最简单的用法是:copy 源文件名目的文件名执行之后就将源文件名所指的文件复制到目标文件名所指的文件。Copy命令不光能复制文件,还能够将多个文件合并起来。请执行如下操作:点击开始菜单,点击“运行.”菜单项。在运行窗口中,输入“cmd”,如果是Windows98 或 WindowsME,则输入command,输入完毕按回车键。在弹出的命令提示符窗口中输copy /? ,然后按回车键,我们得到了关于Copy命令的详细帮助,如下:C:Documents and SettingsAdministratorcopy /?将一份或多份文件复制到另一个位置。COPY /D /V /N /Y | /-Y /Z /A | /B source /A | /B + source /A | /B + . destination /A | /B source 指定要复制的文件。 /A 表示一个 ASCII 文本文件。 /B 表示一个二进位文件。 /D 允许解密要创建的目标文件 destination 为新文件指定目录和/或文件名。 /V 验证新文件写入是否正确。 /N 复制带有非 8dot3 名称的文件时, 尽可能使用短文件名。 /Y 不使用确认是否要覆盖现有目标文件 的提示。 /-Y 使用确认是否要覆盖现有目标文件 的提示。 /Z 用可重新启动模式复制已联网的文件。命令行开关 /Y 可以在 COPYCMD 环境变量中预先设定。这可能会被命令行上的 /-Y 替代。除非 COPY命令是在一个批处理脚本中执行的,默认值应为在覆盖时进行提示。要附加文件,请为目标指定一个文件,为源指定数个文件(用通配符或 file1+file2+file3 格式)。在帮助文件的最后,我们看到一句话“要附加文件,请为目标指定一个文件,为源指定数个文件(用通配符或 file1+file2+file3 格式)。”也就是说,我们可以通过“”把多个文件合并拷贝为一个文件。在合并拷贝的时候,我们要采用二进制模式,所以还需要加上“/B”开关选项,所以,最后生成的批处理文件应该类似这样的样子:copy /b 文件名.扩展名.1+文件名.扩展名.2+.+文件名.扩展名.n 文件名.扩展名开工了,我们新建一个易语言程序,设计如下的窗体:图2.3-a 文件分割机窗体其中,长编辑框命名为“编辑框待分割的文件名”;短编辑框的名称为“编辑框块大小”,“输入方式”设为“整数文本输入”;组合框名称为“组合框单位”,有三项:字节、KB和MB,供用户选择;按钮的命名均为“按钮”+ 按钮的标题;“取消”按钮的“禁止”属性设为真。在写代码之前,先简要介绍一下文件读写方面的操作。在易语言中,读写文件最简单的是使用“读入文件”和“写出文件”函数,“读入文件”直接把文件内容读入到字节集变量,而“写出文件”则直接把字节集写出到指定的文本,很是方便,当然这两个函数不适合操纵大文件,否则太占内存,效率低。读写文件的另一种方式是标准的“打开文件读取数据关闭文件”三步曲。该方式需要先使用“打开文件”得到一个文件号,然后围绕该文件号进行“移动读写位置”和读写数据操作,在读写文件操作完毕后要记得关闭文件号,以免影响或许有其他进程对该文件的读写操作。关于读写文件的详细介绍,请参看本书的“文件格式编程”一章。此程序中还涉及到文件名、文件大小和目录等操作,这些函数都很简单,看看易语言编程环境提示窗口中的信息就能理解。其中经常需要得到去除路径的文件名,于是写了一个函数“去除文件名路径”(图2.3-b),如果文件名中有“”,则返回“”后的文本,否则返回原始文本。图2.3-b 去除文件名路径函数分割文件整个步骤是这样的: 创建保存分割后文件的目录。 打开要分割的文件。 从文件读取指定长度的数据块。 将数据块保存到按规则生成的文件名的文件中。 将新生成文件的文件名添加到批处理文本后面,更新进度条。 循环至直到文件读完。 关闭文件。 生成批处理文件并保存。下面我们看看整个程序的源代码。.版本 2.程序集 窗口程序集1.程序集变量 集已取消, 逻辑型=.子程序 _按钮开始分割_被单击.局部变量 文件大小, 整数型.局部变量 分块大小, 整数型.局部变量 分块文件名, 文本型.局部变量 批处理文本, 文本型.局部变量 目录, 文本型.局部变量 文件名, 文本型.局部变量 文件号, 整数型.局部变量 临时字节集, 字节集.局部变量 i, 整数型文件名 编辑框待分割的文件.内容.如果真 (文件名 “”) 信息框 (“请指定要分割的文件!”, #错误图标, ) 返回 ().如果真结束.如果真 (文件是否存在 (文件名) 假) 信息框 (“指定要分割的文件不存在!”, #错误图标, ) 返回 ().如果真结束文件大小 取文件尺寸 (文件名)分块大小 到数值 (编辑框块大小.内容).如果真 (分块大小 0) 信息框 (“请指定分块大小,并选择合适的单位。”, #信息图标, ) 返回 ().如果真结束.判断开始 (组合框单位.内容 “KB”) 分块大小 分块大小 1024.判断 (组合框单位.内容 “MB”) 分块大小 分块大小 1024 1024.默认.判断结束.如果真 (分块大小 文件大小) 信息框 (“文件本身比分块大小还小,不用分割。”, #信息图标, ) 返回 ().如果真结束集已取消 假按钮开始分割.禁止 真按钮取消.禁止 假目录 文件名 “.部分”创建目录 (目录) 创建一个目录用来保存分割后的文件们。文件号 打开文件 (文件名, #读入, #禁止写).如果真 (文件号 0) 信息框 (“打开文件失败!”, #错误图标, ) 返回 ().如果真结束文件名 去除文件名路径 (文件名).循环判断首 () 临时字节集 读入字节集 (文件号, 分块大小) 读入指定长度的数据,文件读写指针会自动移位 i i 1 分块文件名 目录 “” 文件名 “.” 到文本 (i) 生成分块文件名 写到文件 (分块文件名, 临时字节集) 将数据写到该分块文件 分块文件名 #引号 去除文件名路径 (分块文件名) #引号 去掉路径,绝对路径在别的机器上可能无法合并; 加上引号,防止文件名中有空格 .如果 (批处理文本 “”) 批处理文本 分块文件名 .否则 批处理文本 批处理文本 “+” 分块文件名 拼装批处理文本的中间部分 .如果结束 进度条1.位置 取读写位置 (文件号) 100 文件大小 计算进度条位置 处理事件 () 用户更新进度条和响应用户单击取消按钮。 .如果真 (集已取消) 关闭文件 (文件号) 按钮开始分割.禁止 假 按钮取消.禁止 真 返回 () .如果真结束.循环判断尾 (是否在文件尾 (文件号, ) 假) 组合批处理命令 在批处理命令中加上引号,防止文件名中有空格;最后加上pause暂停,让用户可以看清楚屏幕显示批处理文本 “copy /b ” 批处理文本 “ ” #引号 文件名 #引号 #换行符 “pause” #换行符写到文件 (目录 “合并文件.bat”, 到字节集 (批处理文本) 直接将字节集写入文件就可以了。信息框 (“分割文件完毕!”, #信息图标, )按钮开始分割.禁止 假按钮取消.禁止 真运行 (“explorer.exe ” 目录, 假, ) 用资源管理器浏览分割后的文件。=.子程序 去除文件名路径, 文本型, , 此函数用来去除文件名中的路径。.参数 参带路径文件名, 文本型返回 (取文本右边 (参带路径文件名, 取文本长度 (参带路径文件名) 倒找文本 (参带路径文件名, “”, , 假)=.子程序 _按钮浏览_被单击.如果真 (通用对话框1.打开 () 编辑框待分割的文件.内容 通用对话框1.文件名.如果真结束=.子程序 _按钮取消_被单击集已取消 真图2.3-c文件分割机的执行结果2.4 例 生成EXE文件并在其中写入配置信息假如你使用过木马软件,你一定对其这样的功能不会陌生:木马主程序会根据你填写的某些信息,比如服务器地址、端口号和密码等生成一个特定的EXE文件,该EXE文件能够根据填入的参数执行特定的操作。难道木马主程序有编译功能?! 非也,其实该EXE文件是事先就编译好了的,然后作为资源放入EXE文件中。木马主程序在生成木马的时候,先把该资源写到磁盘文件,然后修改该文件中的某些数据,将配置信息写入。木马程序运行的时候,先从自身把这些配置信息读取出来,然后执行相应的操作。一般来说,有两种方法把配置信息写入EXE文件中, 将配置信息写到EXE文件的末尾,很多木马采用的是这种方法。 改写EXE文件的中间部分。第一种方法实现起来比较简单,但不幸的是由于易语言独立编译后EXE文件的特殊性,在其后添加数据后就无法运行了,所以此法并不实用。非独立编译的EXE文件虽然可以把数据写在尾部,但运行时要带支持库,那还不如把配置信息存在一个独立的文件中,因此也没有什么意义。这里我们就讨论第二种方法,修改EXE文件的中间数据。但具体修改哪个位置呢?况且EXE文件可不是随便能修改的,修改不正确会导致程序无法运行,还真有点麻烦。这里我给大家介绍一个小技巧。还是先来做一个试验。新建一个易程序,在窗体上添加一个按钮。双击该按钮,添加如下代码:.版本 2.子程序 _按钮显示_被单击.局部变量 配置信息, 文本型配置信息 “”信息框 (配置信息, 0, )运行该程序,单击按钮,就显示出变量的值。将该程序编译成EXE文件,再用UltraEdit打开,我们可以看到变量的值就在EXE文件中(图2.4-a 如果没看到,请使用搜索功能)。图2.4-a 字符串被编译程序存在EXE文件中既然能找到它,那就好办了。我们把该字符串修改成别的字符串试试,比如:“易容大师”,注意字符串的长度不要超过原来变量的长度哦,否则程序就有可能不能运行了。如果字符串比原来的短,请在左侧的十六进制输入区用0补足。修改完毕如图2.4-b所示。在修改的过程中,有时中文被拆分成两半而显示成乱码,不去理会。修改完毕保存文件。再次运行程序,点击按钮,哈哈,一句代码不写,就在外部修改了变量的内容。图2.4-b 修改EXE文件中的字符串既然能够手动修改了,那么改成程序修改也不是难事,只要进行一个字节集替换就可以了。请按下面的步骤操作:1 新建一个易程序,添加一个按钮,为按钮写如下代码:.版本 2.子程序 _按钮显示_被单击.局部变量 配置信息, 文本型.局部变量 临时文本数组, 文本型, , 0配置信息“|4567|admin|goomoo | ” 注意在后面加些空格占用存储空间,以免替换的信息超过长度临时文本数组 分割文本 (配置信息, “|”, )信息框 (“主机:” 临时文本数组 1 #换行符 “端口:” 临时文本数组 2 #换行符 “用户名:” 临时文本数组 3 #换行符 “密码:” 临时文本数组 4, 0, )运行该程序,单击按钮,应该得到这样的运行结果:图2.4-c 程序被配置前的运行结果将该程序编译成“待修改程序.exe”。2 再新建一个易程序。点击菜单“插入资源图片或图片组.”,在资源表格中双击“内容”下的空白格,出现“图片或图片组资源属性”对话框,点击其中的 导入新图片 按钮,出现一个浏览文件对话框,在“文件类型”旁边选择“所有文件(*.*)”,选中刚才编译的“待修改程序.exe”文件,把它作为图片资源添加进来(见图2.4-c)。将该资源重命名为“待修改程序”。图2.4-c 把EXE文件作为图片资源加入3 在_启动窗口上添加4个标签控件、4个编辑框控件和1个通用对话框控件。布局如图2.4-d所示。图2.4-d 窗体布局其中编辑框的名字设置为“编辑框”前面标签的名字。通用对话框的“类型”设置为“保存文件”,“过滤器”设置为“EXE文件(*.exe)|*.exe”,“默认后缀”设为“exe”。双击按钮,添加以下代码:.版本 2.子程序 _按钮生成_被单击.局部变量 文件内容, 字节集.局部变量 要替换成的文本, 文本型.局部变量 原始文本, 文本型.局部变量 长度差, 整数型.如果真 (通用对话框1.打开 () 假) 返回 ().如果真结束原始文本 “|4567|admin|goomoo | ” !注意这里的文本要和被配置程序里的文本一样!要替换成的文本 编辑框主机.内容 “|” 编辑框端口.内容 “|” 编辑框用户名.内容 “|” 编辑框密码.内容 “|”长度差 取文本长度 (原始文本) 取文本长度 (要替换成的文本).判断开始 (长度差 0) 需要在右侧补空格 要替换成的文本 要替换成的文本 取重复文本 (长度差, “ ”).判断 (长度差 0) 信息框 (“配置信息太长,可能会导致生成的程序无法运行。”, #错误图标, ) 信息框 (“生成文件失败!”, #错误图标, ) 返回 ().默认.判断结束文件内容 子字节集替换 (#待修改程序, 到字节集 (原始文本), 到字节集 (要替换成的文本), , ) 进行字节集替换.如果 (写到文件 (通用对话框1.文件名, 文件内容) 信息框 (“生成文件成功!”, #信息图标, ).否则 信息框 (“生成文件出错!”, #错误图标, )很惊讶只有这么短的代码,是不是?其中最关键的只有一句“文件内容 子字节集替换 (#待修改程序, 到字节集 (原始文本), 到字节集 (要替换成的文本), , )” ,其他的只是一些辅助性的工作如打开对话框、错误提示等。这段代码中要注意的是“原始文本”变量的内容一定要和被配置程序中的一致,包括大小写!否则替换无法成功。运行生成的程序,点击按钮,我们看到我们在配置程序中填的参数显示出来了(图2.4-e)!一切就这么简单! 当魔术的秘密被揭穿的时候,魔术也就不再神奇了。图2.4-e 运行生成的EXE,我们看到了我们在配置程序中填入的信息2.5文件捆绑机不知大家有没有用过多媒体制作软件如Authorware、Director、Multimedia Builder 、Flash等,如果没有用过,也一定听说过或使用过EXE文件捆绑器、WinRAR之类的程序,以及其他的电子贺卡之类的小程序。这类程序都有一些共同的特定: 能够生成EXE文件; 能够把多个其他的文件捆绑在该EXE文件中,生成的EXE文件在运行时能够解读它们。用前面的思路来实现这个功能可就难了:你不知道用户要捆绑多少个文件,你也不知道这些文件有多大。所以用预留空间然后进行替换的方法显然是行不通了,我们得另辟蹊径。前面我们说过一个方案,就是把数据附加在EXE文件的尾部,但这个方法在易语言中对于独立编译的EXE文件完全行不通。你可以做一个这样的试验:新建一个空的易语言程序,然后独立编译成EXE文件,用UltraEdit打开,修改尾部的一个字节的数据,或者在尾部添加一个字节,保存,再运行,你就得到这样的信息提示然后程序退出:图2.5-a 修改独立编译的EXE尾部的数据后运行的错误提示根据日常使用易语言独立编译程序的观察,我们发现易语言独立编译的程序在执行的时候,会把相关的支持库文件释放到临时文件夹中。也就是说,易语言独立编译的EXE文件中既包含了非独立编译的EXE程序,也包含了支持库,同时支持库是压缩了之后捆绑到EXE程序的尾部的。所以一旦我们修改了其尾部的数据,程序就不能正确运行了。既然不能附加到尾部,那么我们能不能把文件插在它们之间呢? 如果插在它们之间程序仍能够运行,那我们就几乎成功了。这里关键是要找到它们之间的交界处在哪里,如果找到了,事情就成功了一半。有的朋友可能会说:“既然如此,那我们先编译一个非独立的exe文件,得到其大小,不就可以知道他们在何处交界的吗?”。我也曾这么想过,但通过观察发现事实并没有这么简单。我们可以随便编译一个易程序,一个非独立编译的,一个独立编译的,然后用UltraEdit打开看看,就会发现前面的开始处不远,就有不同的地方。既然如此,就不能简单地依据文件大小来判断了,差之毫厘,谬以千里啊!我猜想文件的头部虽然不同,但文件的尾部应该还是一样的,能够找到非独立编译程序的尾部也是一样的。嗯,不要吝惜你自己的想像力!大胆地猜想吧!“想像力比知识更重要。”,这是爱因斯坦说的。还有的朋友可能会说:“既然非独立编译文件的大小不能确定,为什么不找运行库的头呢?那样不是更简单?”事实上我们根本无法找到运行库的头。易语言的运行库是Krnln.fnr文件,该文件有0.97MB,而独立编译的exe文件一般为500KB左右,显然运行库只是部分放在了文件后面或是被压缩了然后放在后面的,那要找运行库的头是根本找不到的。 一旦我们找到了它们的交界处,我们就可以把独立编译后的EXE从此处一分为二,然后分别当作图片资源插入到主程序中。在写出的时候先写出非独立编译的那块,然后写一个分割符字符串,这个标志是为了便于生成的EXE程序在读取自身时方便分割自身。紧接着我们在后面写上要插入的文件的相关信息,比如文件名、文件起始位置、文件大小等,再写一个分割字符串,然后就是文件的内容,再写个分割串,最后再把易语言编译时附加的运行库数据加在后面就完事了。所以,最终生成的EXE文件应该具有如下的结构:非独立编译块一编译程序插入的分割符非独立编译块二文件信息描述文本文件一文件二。文件N压缩的支持库块表2.5-a 最终生成的文件结构,其中灰色块为分割字符串从表中我们可以看到,文件会被分隔字符串分成块连续的白色的部分。如果分隔的块数小于,则说明未插入任何文件。文件的描述信息在第三块,被插入的文件连续地存放在第块。最终捆绑的程序在运行的时候,先读取文件描述信息,并将该信息显示于列表框中,用户从列表框中选择要释放的文件,然后点击“释
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 仪表工业产品研发制度
- 游戏机维修规程
- 企业员工的玩比表现奖金
- 上海财经大学商学院:2024年气候行动报告
- 2025年眼底病症影像学解读模拟试题答案及解析
- 春季季付型租赁仓库合同含仓储管理培训服务
- 国际劳务合作合同通则与劳动权益保障协议
- 个人与个人私对私股权转让及企业债务承担协议
- 物业项目经理聘用合同及社区信息化建设合作协议
- 监理合同延期及施工合同备案及审批补充协议
- 医学细胞生物学第13章 细胞分裂与细胞周期
- 微生物学第九章 微生物生态
- 雪落在中国土地上
- GB/T 31155-2014太阳能资源等级总辐射
- 2023年蒸汽管路设计
- 耳部解剖及急慢性中耳炎课件
- 工程项目投资与融资讲义 课件
- 食品质量安全抽检数据分析模型优质资料
- 承插型盘扣式钢管进场验收记录表
- 军事训练教学法模板课件
- 物流设施与设备ppt课件(完整版)
评论
0/150
提交评论