[转载]Delphi之使用资源文件(Using Resource Files) – 瓢虫Monster – 博客园.
New Term
每个Windows应用程序都使用资源。资源(Resources)不是可执行代码,但它属于程序的一部分元素。
典型的Windows程序的资源有:
- 加速器(Accelerators)
- 位图(Bitmaps)
- 光标(Cursors)
- 对话框(Dialog boxes)
- 图标(Icons)
- 菜单(Menus)
- 数据表(Data tables)
- 字串表(String tables)
- 版本信息(Version information)
- 用户定义的专用资源(User-defined specialty resources)如声音和视频文件
Note
用Project Options对话框的Version Info页面可轻松地将版本信息加到Delphi工程中。如下图:
资源一般包括在扩展名为.rc的资源脚本文件中(resource script file),资源文件就是文本文件。资源文件用资源编译器编译,并在链接时加到应用程序的.exe文件中。
通常大家认为资源要加到可执行文件中,但是有些资源,如位图、字符串表、波形文件,既可以放到外部文件中(.bmp、.txt、.wav),也可加到.exe文件并包含到应用程序文件中。
把资源放到.exe文件中有两条主要优点:
- 存取资源的速度更快。因为在一个可执行文件中查找资源花的事件比从磁盘文件中装入资源花的时间要少。
- 程序和资源可一起包含到单个单元(即.exe文件)中,而不需要一大堆的支持文件。
它的不足之处是:会使.exe文件稍稍增大。其大小不会比外部资源文件加可执行文件大。但是增加大小会使加载该程序的时间加长。
是把资源存为外部资源文件,还是把资源放到.exe文件中,这得由编程人员自己定。但要记住的是:这两种方式用哪一种都行(甚至可以在同一个程序中使用两种方式)。
Delphi中的资源(Resources in Delphi)
传统的Windows程序几乎都至少包含一个对话框和一个图标。但是,Delphi应用程序有所不同。首先,Delphi应用程序中没有真正意义上的对话框,实质上也就是没有对话框资源(Delphi中存储的窗体是资源,但它们是RCDATA资源,而不是对话框资源)。
Delphi 应用程序有传统意义的图标资源。创建应用程序时,Delphi负责创建图标资源文件。类似地,在为Speedbutton、Image组件或BitBtn 组件选择位图时,Delphi将所选位图文件包含到窗体资源中(作为窗体资源的一部分)。在建立应用程序时,窗体和它的全部资源一起包括到程序文件中。这 些都是自动处理的。
有时需要在通常的Delphi处理中以外使用资源。例如,要制作动画,必须有一系列的位图,将它们装载进来作为可以最快速度执行的资源。在这种情况下,就需要知道如何把资源捆绑到Delphi应用程序中。
把 资源文件捆绑到可执行文件中是件非常容易的事,实际创建资源却要困难的多。如果有一个好的资源编辑器,创建诸如位图、图标和光标之类的基本资源并不困难, 但创建具有专业化品质的3D位图和图标却是一项艺术性的工作。我们肯定遇到过很多不错的程序,但它们的位图按钮实在难看。我们可以利用Delphi自带的 Image Editor创建位图、图标和光标。
如果要创建字符串资源、用户数据资源、波形文件资源或其他专用资源,则可能需要第三方资源编辑器。
Note
如 果手头有老版本的Borland Pascal,可使用其中的Resource Workshop编辑器编辑专用资源。创建好资源后,会形成一个.rc文件,Delphi中带有Borland Resource Compiler,用Borland Resource Compiler(BRCC32.EXE)将它编译成.res文件。从技术上讲,可以用任何一种文本编辑器创建.rc文件并用资源编译器编译它,但使用资 源编辑器创建资源要容易的多。
现在在推荐一个款比较不错的第三方资源编辑器Resource Builder,该软件界面简洁,使用简单,大家可以百度一下。
编译资源文件(Compiling Resource Files)
资源文件创建好后,要用资源编译器来编译它。编译资源文件的方法有两种:
- 从命令行手工编译资源文件。
- 添加一个批处理文件工程到工程组。
用其中任意一个方法,编译完后都得到一个.res文件,将它链接到应用程序中。
1、 从命令行编译(Compiling from the Command Line)
从命令行编译资源文件,只需打开Windows中的命令提示框,并输入一行与下面相似的命令:
brcc32 jjres.rc
当然,必须保证当前系统目录为Delphi安装目录的Bin目录下,如果不是,则必须输入BRCC32.EXE的完整路径。这个资源编译器速度非常快,甚至不等察觉,它就把资源脚本文件编译完成了。
2、 使用批处理文件工程(Using a Batch File Project)
添加一个批处理文件到工程组,与从命令行编译一样简单,并且还有一个好处:保证资源文件总是最新的。要搞清楚批处理文件如何工作,可执行下面的步骤:
(1)创建一个新的应用程序;
(2)选择【View | Project Manager】打开“Project Manager”工程管理器,如下图:
(3)点击“Project Manager”工具栏上的“Add New Project”按钮,显示Object Repository对象库,如下:
(4)双击Batch File图标来创建文件工程。将该批处理文件工程以Project2的名称添加到“Project Manager”中,如下:
(5)用鼠标右键点击批处理文件节点并选择Save,将文件保存为test.bat;
(6)用鼠标右键再次点击批处理文件节点并选择【Edit/Options】,会弹出“Batch File Options”对话框;
(7)在Command文本框内输入下列正文:
del myfile.res
brcc32 myfile.rc
(8)点击OK关闭“Batch File Options”对话框。
这 个练习锁做的就是创建一个批处理文件,当编译工程组时,它就会执行。第(7)步输入的批处理文件命令删除一个名为myfile.res文件,并调用 Delphi资源编译器编译myfile.rc文件。用资源编译器编译myfile.rc文件会生成一个名为myfile.res的文件。
工 程组中的下一个工程可能要使用myfile.res。呵呵,那为什么要先删除myfile.res文件呢?这是因为,删除该文件后,就能知道资源编译器是 重新建立了这个文件。如果资源编译器创建资源失败,则任何要使用这个资源文件的工程都将编译失败,并报告编译错误,提示编程人员,建立资源文件出错。
把资源文件链接到你的可执行文件中(Linking Resource Files to Your Executable)
编译好资源文件后,要把编译后的资源文件链接到程序的可执行文件中,可使用$R编译器指令。例如,要链接包含在myfile.res文件中的二进制资源,可在主窗体单元的开头处插入下面一行:
1
|
{$R myfile.res} |
就这么简单!只要指定的文件存在,Delphi就会在链接时把这个编译过的资源添加到可执行文件中。
使用资源的样本程序(A Sample Program Using Resources)
下面的清单中,列出了Jumping Jack的程序的主窗体单元。这个程序显示一个带声音效果的简单动画。主窗体上有两个按钮:一个Image组件和一个Label组件。Jumping Jack程序说说明了资源在Delphi应用程序中的使用。它特别说明了如何加载以资源文件形式存储的位图,如何加载并显示字符串资源,以及如何播放资源 文件中的波形音频。清单后面还列出了部分资源文件。先看看清单,然后再分析程序。
JJMain.pas清单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
unit JmpJackU; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, MMSystem; {$R JJRES.RES} const IDS_UP = 101 ; IDS_DOWN = 102 ; type TMainForm = class (TForm) Image: TImage; Label1: TLabel; Start: TButton; Stop: TButton; procedure FormCreate(Sender: TObject); procedure StartClick(Sender: TObject); procedure StopClick(Sender: TObject); private { Private declarations } Done: Boolean ; procedure DrawImage( var Name: string ); public { Public declarations } end ; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm . DrawImage( var Name: string ); begin { 从资源文件中读取位图,通过资源名称} Image . Picture . Bitmap . LoadFromResourceName(HInstance, Name); { 让Image有机会显示位图} Application . ProcessMessages; { 延缓,让动画变慢} Sleep( 20 ); end ; procedure TMainForm . FormCreate(Sender: TObject); begin Image . Picture . Bitmap . LoadFromResourceName(HInstance, 'ID_BITMAP1' ); end ; procedure TMainForm . StartClick(Sender: TObject); var s: string ; ResName: string ; i: Integer ; Buff: array [ 0..9 ] of Char ; begin s := 'ID_BITMAP' ; Done := False ; while not Done do begin for i := 1 to 5 do begin ResName := s + IntToStr(i); DrawImage(ResName); end ; LoadString(HInstance, IDS_UP, Buff, SizeOf(Buff)); Label1 . Caption := Buff; Label1 . Refresh; PlaySound( 'ID_WAVEUP' , HInstance, SND_ASYNC or SND_RESOURCE); Sleep( 200 ); for i := 5 downto 1 do begin ResName := s + IntToStr(i); DrawImage(ResName); end ; PlaySound( 'ID_WAVEDOWN' , HInstance, SND_ASYNC or SND_RESOURCE); LoadString(HInstance, IDS_DOWN, Buff, SizeOf(Buff)); label1 . Caption := Buff; Label1 . Refresh; Sleep( 200 ); end ; end ; procedure TMainForm . StopClick(Sender: TObject); begin { 当“Stop”按钮按下时,告诉循环停止} Done := True ; end ; end . |
JJRec.rc清单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
STRINGTABLE BEGIN 101, "Up" 102, "Down" END ID_WAVEUP WAVE "up.wav" ID_WAVEDOWN WAVE "down.wav" ID_BITMAP1 BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE LANGUAGE LANG_NEUTRAL, 0 BEGIN '42 4D 76 02 00 00 00 00 00 00 76 00 00 00 28 00 ' '00 00 20 00 00 00 20 00 00 00 01 00 04 00 00 00 ' '00 00 00 02 00 00 00 00 00 00 00 00 00 00 10 00 ' '00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80 ' '00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80 ' '00 00 C0 C0 C0 00 80 80 80 00 00 00 FF 00 00 FF ' '00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF ' '00 00 FF FF FF 00 BB BB BB BB BB BB BB BB BB BB ' 'BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB ' 'BB BB BB BB BB BB BB BB BB BB BB BB BB B0 B0 BB ' 'BB BB BB BB BB BB BB BB BB BB BB BB BB B0 B0 BB ' 此处省略了部分内容,详细内容请查看示例代码 |
分析:
主窗体类声明中声明了一个Done的布尔型字段,Done用于确定何时终止动画。DrawImage方法用于在Image组件中显示位图。
注意:上列代码中,使用了两个Windows API函数来加载字符串和波形文件资源。在StartClick方法中,LoadString函数加载一个字符串到到一个字符数组缓冲区内。然后将该字符数组分配给窗体上的Label组件的Caption属性。
PlaySound函数用于播放波形文件资源。PlaySound函数用SND_ASYNC标志通知Windows开始播放音频并立即将控制返回给 程序,这使得在播放音频的同时,动画能继续下去。SND_RESOURCE标志通知Windows,声音是一个资源,而不是磁盘上的一个文件。 LoadString和PlaySound函数都使用HInstance全局变量通知Windows到可执行文件中去查找资源。装入位图资源,使用VCL 方法LoadFromResourceName。
而资源文件JJRec.rc中的前5行说明字符串在资源脚本文件中的格式;使用文本编辑器创建字符串表非常容易。接下去是为两个波形文件创建各自的 WAVE资源,这两个波形文件已存在该工程目录中。资源编译器一看到WAVE声明,它就会读声音文件并将它们编译进二进制资源文件。
Note
从上面的清单中看出,使用文本编辑器来创建某些类型的资源是比较容易的事。如果位图和波形音频文件存为了外部文件,可像上面的清单那样将它们包含到.RC文件中,并用资源编译器将它们编译为二进制资源文件。其中二进制资源文件可链接到应用程序的可执行文件中。
上面的资源清单只是部分代码,用传统的资源编辑器创建的位图常常以数字数据的形式包含到资源文件中,位图的资源描述可以很长。Jumping Jack位图资源描述大约有200行,因此未在清单中全部列出。下图给出了Jumping Jack启动后的界面。
为应用程序创建附加资源不是什么高深的学问,但也不是很简单的事情。要搞清楚它们如何协调配合要花一定的事件。
以上代码均在Delphi7中测试通过,示例代码下载:JumpingJack.rar