某个bbs站上有文章:(想来作者不会介意我装载吧!)
==============================================================================
文件合并器的制作方法
作者:王昊
从想到做文件合并器到现在已经有一个多月了,但是一直没有静下心来想它的实现方法. 昨天看数学看烦了, 我终于忍不住扔开了书,开始着手"文件合并器"的编制.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,那么这里的标准程序又从何而来呢?标准程序又应该保存在哪里呢?
有两个办法.第一,给用户的程序包含两个文件,一个是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
'发布给用户.
七.注意事项.
这个程序技巧性的确很强,但是我认为正常的人很少会用它.但对那些想散播病毒的人来说,却是一大利器.因此,我在这里要警告这部分人:制作或散发病毒是违反法律的,将受到法律允许范围内的
最高处罚.请好自为之。
而该程序的思路则有很巧妙的应用。你可以将DLL或其他需要的文件连接在你的程序后面.让你的程序运行时先解出这些文件。这样就能发布只有一个执行文件的程序了,比较方便,可以帮助VB程序员发布伪“绿色软件”
好了,就写到这里。有空我再写点经验出来。
谢谢大家赏脸看我的文章。