大侠帮帮我啊,怎么把图片封装在exe文件中? (50分)

  • 主题发起人 主题发起人 xxbt
  • 开始时间 开始时间
X

xxbt

Unregistered / Unconfirmed
GUEST, unregistred user!
比如image要显示的图片,不用loadfromfile
谢了
 
看我的大事软件吧:www.to-happy.com
 
转贴一个例子吧(只有如何加入,应用自己想吧)
=========================================================
信息标题:我知道了如何动态在EXE中加入任何资源(.EXE,.BMP,.SWF,.DOC..)大家分享一下吧!(1460字)
信息作者:crisee
发表时间:2001-8-18 11:06:00
阅读次数:363
信息内容:

前些日子,我发表了类似的帖子,是求援的。
然后,参考了些源码。经过“处心积虑”的琢磨,终于弄懂了。
本人是编程初学者,脑子也笨,高手见了不要见笑!
我发现哦,在EXE资源区内写入其它资源,虽然EXE还可运行,但图标会变成Windows默认的样儿,也就是说EXE中的ICON资源遭破坏了。然后,我尝试在EXE尾部写入资源,太棒了!虽然文件长度增加,但丝毫没有影响EXE的运行。我们假定要把 sir32.exe 加入 程序(project1.exe)中。
首先,创建新的Project,定义 private 变量 fStream:TFileStream,然后写个过程
Procedure TForm1.EXEStrore;
var FS: TFileStream;
Begin
//首先将程序自身复制为~tmp.exe
CopyFile(Application.ExeName, '~tmp.exe', false);
//为打~tmp.exe创建 fStream,模式为 只读。
fStream.Create('~tmp.exe',fmOpenRead);
//为打开sir32.exe创建 FS,模式为 只读。
FS.Create('Sir32.exe',fmOpenRead);
try
//将fStream定位至 ~tmp.exe文件尾部。
fStream.position := fStream.Seek(0, SoFromEnd);
//将 FS (sir32.exe) 定位至文件首部,准备读入内存。
FS.Postion := 0;
//将 FS(Sir32.exe) 写入 fStream(编译后的程序project1.exe)
//OK..
fStream.CopyFrom(FS, FS.Size);
finally
//释放资源,要不会出错的。
fStream.free;
FS.free;
end;
end;
以上简短的代码,就可以把任何类型的文件写入~tmp.exe中。当然,~tmp.exe只是中转站。然后在写一段用.bat文件删除程序本身,并把~tmp.exe改为程序名的代码。
如 定义 var pfile: TEXTFILE ;
begin
AssignFile(pfile,'~tmp.bat');
reWrite(pfile);
WriteLn(pfile, Pchar('Del '+Application.ExeName));
WriteLn(pfile, Pchar('ren ~tmp.exe '+Application.ExeName));
WriteLn(pfile, 'Del %0');
WriteLn(pfile, 'Exit');
CloseFile(pfile);
WinExec('~tmp.bat',SW_Hide);
Halt;
end;
最后,你会发现project1.exe文件长度增加了,具体增加的长度刚是sir32.exe的大小(一个字节也不差)。
但是,如何再把写入的文件再读出呢?自己想想,或是我下回就说 ^:^
 
收集的可能是陈惊涛的
二、实际应用之一:利用流制作EXE文件加密器、捆绑、自解压文件及安装程序
我们先来说一下如何制作一个EXE文件加密器吧。
EXE文件加密器的原理:建立两个文件,一个用来添加资源到另外一个EXE文件里面,称为添加程序。另外一个被添加的EXE文件称为头文件。该程序的功能是把添加到自己里面的文件读出来。Windows下的EXE文件结构比较复杂,有的程序还有校验和,当发现自己被改变后会认为自己被病毒感染而拒绝执行。所以我们把文件添加到自己的程序里面,这样就不会改变原来的文件结构了。我们先写一个添加函数,该函数的功能是把一个文件当作一个流添加到另外一个文件的尾部。函数如下:
Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Target,Source:TFileStream;
MyFileSize:integer;
begin
try
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);
try
Target.Seek(0,soFromEnd);//往尾部添加资源
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//计算资源大小,并写入辅程尾部
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
finally
Target.Free;
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
有了上面的基础,我们应该很容易看得懂这个函数。其中参数SourceFile是要添加的文件,参数TargetFile是被添加到的目标文件。比如说把a.exe添加到b.exe里面可以:Cjt_AddtoFile('a.exe',b.exe');如果添加成功就返回True否则返回假。
根据上面的函数我们可以写出相反的读出函数:
Function Cjt_LoadFromFile(SourceFile,TargetFile :string):Boolean;
var
Source:TFileStream;
Target:TMemoryStream;
MyFileSize:integer;
begin
try
Target:=TMemoryStream.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//读出资源大小
Source.Seek(-MyFileSize,soFromEnd);//定位到资源位置
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//取出资源
Target.SaveToFile(TargetFile);//存放到文件
finally
Target.Free;
Source.Free;
end;
except
Result:=false;
Exit;
end;
Result:=true;
end;
其中参数SourceFile是已经添加了文件的文件名称,参数TargetFile是取出文件后保存的目标文件名。比如说Cjt_LoadFromFile('b.exe','a.txt');在b.exe中取出文件保存为a.txt。如果取出成功就返回True否则返回假。
打开Delphi,新建一个工程,在窗口上放上一个Edit控件Edit1和两个Button:Button1和Button2。Button的Caption属性分别设置为"确定"和"取消"。在Button1的Click事件中写代码:
var S:string;
begin
S:=ChangeFileExt(Application.ExeName,'.Cjt');
if Edit1.Text='790617' then
begin
Cjt_LoadFromFile(Application.ExeName,S);
{取出文件保存在当前路径下并命名"原文件.Cjt"}
Winexec(pchar(S),SW_Show);{运行"原文件.Cjt"}
Application.Terminate;{退出程序}
end
else
Application.MessageBox('密码不对,请重新输入!','密码错误',MB_ICONERROR+MB_OK);
编译这个程序,并把EXE文件改名为head.exe。新建一个文本文件head.rc,内容为: head exefile head.exe,然后把它们拷贝到Delphi的BIN目录下,执行Dos命令Brcc32.exe head.rc,将产生一个head.res的文件,这个文件就是我们要的资源文件,先留着。
我们的头文件已经建立了,下面我们来建立添加程序。
新建一个工程,放上以下控件:一个Edit,一个Opendialog,两个Button1的Caption属性分别设置为"选择文件"和"加密"。在源程序中添加一句:{$R head.res}并把head.res文件拷贝到程序当前目录下。这样一来就把刚才的head.exe跟程序一起编译了。
在Button1的Cilck事件里面写下代码:
if OpenDialog1.Execute then Edit1.Text:=OpenDialog1.FileName;
在Button2的Cilck事件里面写下代码:
var S:String;
begin
S:=ExtractFilePath(Edit1.Text);
if ExtractRes('exefile','head',S+'head.exe') then
if Cjt_AddtoFile(Edit1.Text,S+'head.exe') then
if DeleteFile(Edit1.Text) then
if RenameFile(S+'head.exe',Edit1.Text) then
Application.MessageBox('文件加密成功!','信息',MB_ICONINFORMATION+MB_OK)
else
begin
if FileExists(S+'head.exe') then DeleteFile(S+'head.exe');
Application.MessageBox('文件加密失败!','信息',MB_ICONINFORMATION+MB_OK)
end;
end;
其中ExtractRes为自定义函数,它的作用是把head.exe从资源文件中取出来。
Function ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
begin
try
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
try
Res.SavetoFile(ResNewName);
Result:=true;
finally
Res.Free;
end;
except
Result:=false;
end;
end;
注意:我们上面的函数只不过是简单的把一个文件添加到另一个文件的尾部。实际应用中可以改成可以添加多个文件,只要根据实际大小和个数定义好偏移地址就可以了。比如说文件捆绑机就是把两个或者多个程序添加到一个头文件里面。那些自解压程序和安装程序的原理也是一样的,不过多了压缩而已。比如说我们可以引用一个LAH单元,把流压缩后再添加,这样文件就会变的很小。读出来时先解压就可以了。另外,文中EXE加密器的例子还有很多不完善的地方,比如说密码固定为"790617",取出EXE运行后应该等它运行完毕后删除等等,读者可以自行修改。
---------------------------------------------------------------------
三、实际应用之二:利用流制作可执行电子贺卡
我们经常看到一些电子贺卡之类的制作软件,可以让你自己选择图片,然后它会生成一个EXE可执行文件给你。打开贺卡时就会一边放音乐一边显示出图片来。现在学了流操作之后,我们也可以做一个了。
添加图片过程我们可以直接用前面的Cjt_AddtoFile,而现在要做的是如何把图像读出并显示。我们用前面的Cjt_LoadFromFile先把图片读出来保存为文件再调入也是可以的,但是还有更简单的方法,就是直接把文件流读出来显示,有了流这个利器,一切都变的简单了。
现在的图片比较流行的是BMP格式和JPG格式。我们现在就针对这两种图片写出读取并显示函数。
Function Cjt_BmpLoad(ImgBmp:TImage;SourceFile:String):Boolean;
var
Source:TFileStream;
MyFileSize:integer;
begin
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//读出资源
Source.Seek(-MyFileSize,soFromEnd);//定位到资源开始位置
ImgBmp.Picture.Bitmap.LoadFromStream(Source);
finally
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
上面是读出BMP图片的,下面的是读出JPG图片的函数,因为要用到JPG单元,所以要在程序中添加一句:uses jpeg。
Function Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;
var
Source:TFileStream;
MyFileSize:integer;
Myjpg: TJpegImage;
begin
try
Myjpg:= TJpegImage.Create;
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareDenyNone);
try
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));
Source.Seek(-MyFileSize,soFromEnd);
Myjpg.LoadFromStream(Source);
JpgImg.Picture.Bitmap.Assign(Myjpg);
finally
Source.Free;
Myjpg.free;
end;
except
Result:=false;
Exit;
end;
Result:=true;
end;
有了这两个函数,我们就可以制作读出程序了。下面我们以BMP图片为例:
运行Delphi,新建一个工程,放上一个显示图像控件Image1。在窗口的Create事件中写上一句就可以了:
Cjt_BmpLoad(Image1,Application.ExeName);
这个就是头文件了,然后我们用前面的方法生成一个head.res资源文件。
下面就可以开始制作我们的添加程序了。全部代码如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, ExtDlgs;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Button2: TButton;
OpenPictureDialog1: TOpenPictureDialog;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
Function ExtractRes(ResType, ResName, ResNewName : String):boolean;
Function Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
Function TForm1.ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
begin
try
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
try
Res.SavetoFile(ResNewName);
Result:=true;
finally
Res.Free;
end;
except
Result:=false;
end;
end;
Function TForm1.Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Target,Source:TFileStream;
MyFileSize:integer;
begin
try
Source:=TFileStream.Create(SourceFile,fmOpenRead or fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite or fmShareExclusive);
try
Target.Seek(0,soFromEnd);//往尾部添加资源
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//计算资源大小,并写入辅程尾部
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
finally
Target.Free;
Source.Free;
end;
except
Result:=False;
Exit;
end;
Result:=True;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Caption:='Bmp2Exe演示程序.作者:陈经韬';
Edit1.Text:='';
OpenPictureDialog1.DefaultExt := GraphicExtension(TBitmap);
OpenPictureDialog1.Filter := GraphicFilter(TBitmap);
Button1.Caption:='选择BMP图片';
Button2.Caption:='生成EXE';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if OpenPictureDialog1.Execute then
Edit1.Text:=OpenPictureDialog1.FileName;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
HeadTemp:String;
begin
if Not FileExists(Edit1.Text) then
begin
Application.MessageBox('BMP图片文件不存在,请重新选择!','信息',MB_ICONINFORMATION+MB_OK)
Exit;
end;
HeadTemp:=ChangeFileExt(Edit1.Text,'.exe');
if ExtractRes('exefile','head',HeadTemp) then
if Cjt_AddtoFile(Edit1.Text,HeadTemp) then
Application.MessageBox('EXE文件生成成功!','信息',MB_ICONINFORMATION+MB_OK)
else
begin
if FileExists(HeadTemp) then DeleteFile(HeadTemp);
Application.MessageBox('EXE文件生成失败!','信息',MB_ICONINFORMATION+MB_OK)
end;
end;
end.
怎么样?很神奇吧:)把程序界面弄的漂亮点,再添加一些功能,你会发现比起那些要注册的软件来也不会逊多少吧。
-----------------------------------------------------------------------
实际应用之三:利用流制作自己的OICQ
OICQ是深圳腾讯公司的一个网络实时通讯软件,在国内拥有大量的用户群。但OICQ必须连接上互联网登陆到腾讯的服务器才能使用。所以我们可以自己写一个在局部网里面使用。
OICQ使用的是UDP协议,这是一种无连接协议,即通信双方不用建立连接就可以发送信息,所以效率比较高。Delphi本身自带的FastNEt公司的NMUDP控件就是一个UDP协议的用户数据报控件。不过要注意的是如果你使用了这个控件必须退出程序才能关闭计算机,因为TNMXXX控件有BUG。所有nm控件的基础 PowerSocket用到的ThreadTimer,用到一个隐藏的窗口(类为TmrWindowClass)处理有硬伤。
出问题的地方:
Psock::TThreadTimer::WndProc(var msg:TMessage)
if msg.message=WM_TIMER then
他自己处理
msg.result:=0
else
msg.result:=DefWindowProc(0,....)
end
问题就出在调用 DefWindowProc时,传输的HWND参数居然是常数0,这样实际上DefWindowProc是不能工作的,对任何输入的消息的调用均返回0,包括WM_QUERYENDSESSION,所以不能退出windows。由于DefWindowProc的不正常调用,实际上除WM_TIMER,其他消息由DefWindowProc处理都是无效的。
解决的办法是在 PSock.pas
在 TThreadTimer.Wndproc 内
Result := DefWindowProc( 0, Msg, WPARAM, LPARAM );
改为:
Result := DefWindowProc( FWindowHandle, Msg, WPARAM, LPARAM );
早期低版本的OICQ也有这个问题,如果不关闭OICQ的话,关闭计算机时屏幕闪了一下又返回了。
 
楼上的动作很快,呵呵
 
我说一个最简单的,比上面的要简单十倍.
同样也是保存在资源文件中,因为delphi的dfm就是以资源文件存放在exe中,所以我们只要将
图片保存到dfm中就可以了.
创建一个新窗体,放一个TImage,装入你的图片,ok.
要用这个图片,创建这个窗体类的实例,访问他的Picture就行了,是不是很简单?
 
楼上的不具有通用性
 
创建资源文件
首先创建一个.Rc的纯文本文件。格式如下:
资源标识符 + 关键字 + 资源文件名
① 资源标识符:程序中调用资源时的特殊标号;
② 关键字:标识资源文件类型;
MIDIFILE midi '文件名'
Wave: 资源文件是声音文件;
RCDATA: JPEG文件;
AVI: AVI动画;
ICON: 图标文件;
BITMAP: 位图文件;
CURSOR: 光标文件;
资源文件名:资源文件名;
③ 资源文件名:编译的资源文件,比如动画、位图、光标等;
④ 如:MyWav WAVE "FileName.Wav"
使用Borland资源编译器(BRCC32.EXE)转化.Rc文件成.Res文件。在DOS命令行下输入下列命令:
Brcc32 FileName.Rc // Brcc32.Exe在Delphi5Bin文件下。
3.在工程中引用资源文件
为了存取我们的资源文件,必须告诉Delphi链接我们的资源文件到应用程序中。因此我们可以在源代码中加入一条编译指令完成上述功能。这条指令必须紧跟在窗口指令后,如下形式:
{$R *.DFM} //Delphi自带编译指令
{$R FileName.Res} //新加入的编译资源文件的指令。
不要删除{$R *.DFM}指令,因为这行代码告诉Delphi链接下面的资源到窗口的资源中。
4.调用资源文件
(1)存取资源文件中的位图(Bitmap)
程序中如果想存取资源,你必须调用一些Windows API函数。保存在资源文件中的位图、光标和图标可以通过调用LoadBitmap、LoadCursor和LoadIcon函数存取。
本例举例说明如何存取资源文件中位图并显示在Timage控件中。
procedure TfrMain.btnCanvasPic(Sender: TObject);
Image1.Picture.Bitmap.Handle :=LoadBitmap(hInstance, ''资源标识符'');
end;
注:如果位图没有装载成功,程序仍旧执行,但是Image将不再显示图片。你可以根据LoadBitmap函数的返回值判断是否装载成功,如果装载成功返回值是非0,如果装载失败返回值是0。
另外一个存取显示位图的方法如下
procedure TfrMain.btnLoadPicClick(Sender: TObject);
begin
Image1.Picture.Bitmap.LoadFromResourceName(hInstance, ''资源标识符'');
end;
(2)存取资源文件中的光标
Screen.Cursors[]是一个光标数组,使用光标文件我们可以将定制的光标加入到这个属性中。因为默认的光标在数组中索引值是0,所以除非想取代默认光标,最好将定制的光标索引值设为1。
procedure TfrMain.btnUseCursorClick(Sender: TObject);
begin
Screen.Cursors[1] :=LoadCursor(hInstance,''资源标识符'');
Image1.Cursor :=1;
end;
(3)存取资源文件中的图标
将图标放在资源文件中,可以实现动态改变应用程序图标。
procedure TfrMain.LoadIconClick(Sender: TObject);
begin
Application.Icon.Handle := LoadIcon(hInstance, ''资源标识符'');
End;
(4)存取资源文件中的AVI
procedure TfrMain.LoadIconClick(Sender: TObject);
begin
Animate1.ResName :=''MyAvi'' ; //资源标识
Animate1.Active :=True ;
End ;
(5)存取资源文件中的JPEG
把jpeg单元加入到Interface的uses单元中。
procedure TForm1.LoadJPGClick(Sender: TObject);
var
Fjpg : TJpegImage ;
FStream :TResourceStream ;
begin
Fjpg :=TJpegImage.Create ;
FStream := TResourceStream.Create (Hinstance,''资源标识符'',RT_RCDATA) ;
FJpg.LoadFromStream (FStream) ;
Image1.Picture.Bitmap.Assign (FJpg);
end;
(6)存取资源文件中的Wave
把MMSystem加入到Interface的uses单元中
procedure TForm1.LoadWaveClick(Sender: TObject);
begin
PlaySound(''MyWav'', HInstance, Snd_ASync or Snd_Memory or snd_Resource) ;
end;
5.结束语
  位图、光标和图标、AVI动画、JPEG和Wave文件等可以放到资源文件中,上面我们介绍了如何将资源加入到资源文件中,并介绍了在Delphi中动态存取资源文件的方法。Delphi编译工程会自动创建一个和工程名相同的资源文件(如果没有其他的资源,主窗口的图标会放在这个资源文件中)。但是建议最好不要改变这个资源文件。
 
楼上各位写的都很好,谢谢大家热情帮助!
 

Similar threads

回复
0
查看
825
不得闲
D
回复
0
查看
762
DelphiTeacher的专栏
D
D
回复
0
查看
736
DelphiTeacher的专栏
D
D
回复
0
查看
690
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部