文件合并

来源:互联网 发布:linux下安装qt5 编辑:程序博客网 时间:2024/04/29 16:28

 从想到做文件合并器到现在已经有一个多月了,但是一直没有静下心来想它的实现方法.   昨天看数学看烦了,   我终于忍不住扔开了书,开始着手"文件合并器"的编制.3小时后,终于有了眉目,今天又改了一下.现在终于能够和   各位分享我的喜悦了.我愿意将我的方法写给大家,也希望各位高手不吝赐教.     
          先看看我们的目的:编写一个程序A,它能够将两个可执行程序B和C合并在一起,形成     
  一个新的可执行程序D.   要让用户执行D的时候,相当于同时运行B和C两个程序.     
          我的开发工具:我现在能够用VB和DELPHI中的任何一个开发这个软件.这次我用的是     
  DELPHI.如果你需要,也可以用VC或BCB来完成.     
          下面我用这三个小时中我考虑的东西为线索来讲讲主要的原理.     
            一.   我的疑惑.     
                  将两个可以执行的程序合并在一起会变成什么东西?这是我的第一个疑惑.要解     
  决这个问题,首先要学会   如何将两个文件合并在一起.我想到了内存流(MemoryStream),它能极方便的完成这个步   骤.假设有两个可执行文件f1,   f2.现在要把他们合并在一起.下面给出原代码.     
                  var     
                      strmSource,strmDest:TMemoryStream;     
                  begin     
                      //先读f1     
                      strmSource:=TMemoryStream.Create;     
                      strmSource.loadfromfile(f1);     
                      //拷贝到strmdest     
                      strmDest:=TMemoryStream.Create;     
                      strmDest.copyfrom(strmSource,strmSource.size);     
                      strmSource.clear;     
                      //再读f1     
                      strmSource.loadfromfile(f2);     
                      //拷贝到strmdest     
                      strmDest.seek(strmDest.size,soFromBeginning);     
                      strmDest.copyfrom(strmSource,strmSource.size);     
                      strmSource.free;     
                      //这时strmDest里面便是两个文件合并后的内容了.将它保存为文件     
                      strmDest.SaveToFile('dest.exe');     
                      strmDest.free;     
                  end;     
                我惊讶的发现,执行dest.exe就相当于执行f1!!为了确认,我将原代码中f1和f2的     
  读入顺序对调,得到的新的dest.exe执行竟然相当于执行f2!!(此处省略了N个感叹号).我又用同样的方法在f1     
  的后面添加很多无意义的字节,得到的新的f1运行竟然很正常.现在我们知道了,将两个或者多个可执行文件合并在   一起,得到的新文件执行时只是执行第一个文件.这是非常关键的一步.       
        
  二.如何分离?     
                  合并没有问题了,如何分离呢?在知道原来的两个文件的大小的情况下,这很容易     
  作到.假设i1和i2是原来两个文件的大小(字节).合并后的文件是"dest.exe".     
                  var     
                      strmSource,strmDest:TMemoryStream;     
                  Begin     
                      //先读dest.exe     
                      strmSource:=TMemoryStream.Create;     
                      strmSource.loadfromfile('dest.exe');     
                      //拷贝f1到strmdest     
                      strmDest:=TMemoryStream.Create;     
                      strmDest.copyfrom(strmSource,i1);     
                      //保存f1     
                      strmDest.SaveToFile(f1);     
                      strmDest.clear;     
                      //拷贝f2到strmdest     
                      strmSource.seek(i1,soFromBeginning);     
    
                      strmDest.copyfrom(strmSource,i2);     
                      strmDest.SaveToFile(f2);     
                      strmDest.free;     
                      strmSource.free;     
                end;     
            
  三.总体思路.     
                  在解决了上述问题后,我的总体思路就出来了.假设我给用户的程序是A,它能把     
  B和C合并起来得到D.那么D具有什么特征呢?D应该至少由三个部分组成(请注意是"至少"):第一部分是一个可     
  执行的程序,我把它叫做   标准程序S,他能将D的第二部分和第三部分(就是原来的可执行文件B和C)读出来,保存在磁盘上,然后执行他们.但大家看了"如何分离"后应该知道,只有我们知道了B和C的长度时,才能方便的从D中读出他们.而为了使D可以在另一个用户的机子上也能够运行,我认为B和C的长度信息应该保存在D的最后.于是,D应该具有四个部分:     
          1:         S     
          2:         B     
          3:         C     
          4:         长度信息     
          那么,既然我给用户的程序是A,那么这里的标准程序S又从何而来呢?标准程序S又应该保存在哪里呢?     
          有两个办法.第一,给用户的程序包含两个文件,一个是A,一个是S.但我觉得这样不够   爽.于是我用了另一个方法:   将S连在A的后面,成为A'.     
          于是乎,当用户执行A'时,A'要求用户选择两个可执行文件B和C.当用户点击确定时,     
  A'将它自身所带的S与B和C合并起来,形成D.然后,用户便可以执行D了,这时的D并不依赖于A'.D执行时,实际上执行的是它的第一部分S,S首先从D的最后取得长度信息,然后根据这些长度信息读出B和C,保存于硬盘上的某个目     
  录.然后调用ShellExecute执行他们.这样就达到了我们的目的.     
          那么,长度信息如何定义呢?如何将S连在A的后面呢?S如何完成自身的功能呢?这就是     
  我下面要讲的.     
        
  四.保存长度信息.     
                我先讲一讲如何把一个字符串写入内存流.其实我自己也不知道如何直接将一个     
  字符串的内容读到内存流中,于是我采取了先将字符串内容写入一个临时文件中,然后用loadformfile将文件内容读入内存流中.     
                然而,我们必须知道连接在D后面的长度信息的具体长度,也就是说用几个字节保     
  存,才能让S读出长度信息.我考虑再三,决定用32个字节来表示每个文件的长度,虽然大多数情况下,文件大小不会超出100M.     
                看看这里的代码:     
                  var     
                      strmSource,strmDest:TMemoryStream;     
                      s1,s2:string;     
                      f:TextFile;     
                  begin     
                      //先用上面的方法将S和B与C的内容写入strmdest,现在要在strmDest里面添   加长度信息     
                      //假设s1,s2里放有B和C的大小,先把他们变为32个字节.     
                      while   length(s1)<32   do     
                          begin     
                              s1:='0'+s1;     
                          end;     
                      while   length(s2)<32   do     
                          begin     
                              s2:='0'+s1;     
                          end;     
                      //s1存入文件     
                      assignfile(f,'tmp');     
                      rewrite(f);     
                      try     
                          write(f,s1);     
                      finally     
                          closefile(f);     
                      end;     
                      //文件内容读入strmSource     
                      strmSource:=TMemoryStream.Create;     
                      strmSource.loadfromfile('tmp');     
                      //加到strmDest后面     
                      strmDest.copyfrom(strmSource,strmSource.size);     
                      strmSource.clear;     
    
                      deletefile('tmp');     
                      //s2存入文件     
                      assignfile(f,'tmp');     
                      rewrite(f);     
                      try     
                          write(f,s2);     
                      finally     
                          closefile(f);     
                      end;     
                      //文件内容读入strmSource     
                      strmSource:=TMemoryStream.Create;     
                      strmSource.loadfromfile('tmp');     
                      //加到strmDest后面     
                      strmDest.copyfrom(strmSource,strmSource.size);     
                      strmSource.free;     
                      deletefile('tmp');     
                  end;     
                    利用代码里的方法,便可将长度信息保存在D的最后了.     
            
  五.标准文件.     
                现在我想大家感到疑惑的就是标准文件S了,这到底是个什么玩意儿?怎么做它?     
                其实,我们在前面已经讲过了,"S首先从D的最后取得长度信息,然后根据这些长度     
  信息读出B和C,保存于硬盘上的某个目录.   然后调用ShellExecute执行他们".要注意这里的S和D是在一起的,S只不过是D的第一部分.他们的文件名是一样的了.于是就变成了S的功能是从它自身的后面取得长度信息,然后根据这些长度信息读出B和C,保存于硬盘上的某个目录.然后调用ShellExecute执行他们.我想,具体的方法我前面已经讲的很清楚了.只要记住长度信息是分别用32个     
  字节表示的就行了.     
        
  六.完整步骤     
                先编写S,然后编写A.再写一个程序E将S和A连接起来,S放在A的后面,成为A'.将A'发布给用户.     
  七.注意事项.     
                这个程序技巧性的确很强,但是我认为正常的人很少会用它.但对那些想散播病毒的人来说,却是一大利器.因此,我在这里要警告这部分人:制作或散发病毒是违反法律的,将受到法律允许范围内的     
  最高处罚.请好自为之。     

原创粉丝点击