Embedding files as resources in a Delphi executable
How to embed icons, cursors, animated cursors, bitmaps, jpeg images,
wave files, text files and any other file inside a Delphi executable.
Product:
Delphi 3.x (or higher)
Uploader: Ernesto D'Spirito
Component Download: p0025.zip
Question/Problem/Abstract:
This article attempts to explain how to include files inside a Delphi
application as different kinds of resources, and how to manage them.
Answer:
Embedding files as resources in a Delphi executable
It is possible to embed any kind of file in an executable using resource
files (*.RES). Certain kinds of resources are recognized by the API and
can be used directly. Others are simply taken as binary data and its up
to you to use them. In this article we will see examples of both kinds.
To create the resource file we start with the source file (*.RC), for
example named RESOURCES.RC, which is a simple text file that contains
the resource entries (name, class and file):
sample_bmp BITMAP sample.bmp
sample_ico ICON sample.ico
sample_cur CURSOR sample.cur
sample_ani ANICURSOR sample.ani
sample_jpg JPEG sample.jpg
sample_wav WAVE sample.wav
sample_txt TEXT sample.txt
The names of the resources (sample_bmp, sample_ico, etc.) are arbitrary.
The kind of resource may be one recognized by the APIs (BITMAP, ICON, CURSOR)
or arbitrary (JPEG, WAVE, TEXT). The file names specify the files that will
be included in the .RES file (and later in the .EXE).
Now we have to compile the .RC file to produce the .RES file. For that we
can use the Borland Resource Compiler (brcc32.exe) that you can probably
find in Delphi's BIN folder. It's a simple command-line utility that expects
the name of the source file as parameter:
C:/DELPHI/P0025>brcc32 resources
Borland Resource Compiler Version 5.40
Copyright (c) 1990, 1999 Inprise Corporation. All rights reserved.
C:/DELPHI/P0025>_
To instruct the linker to embed the resource file in the executable,
we use the resource file directive ($R or $RESOURCE) in our Pascal ource code:
{$R resources.res}
Loading the resources in your application is easy for the "recongnized"
resources like BITMAP, ICON and CURSOR since the Windows API provides
functions (LoadBitmap, LoadIcon and LoadCursor respectively) to get
handles for these elements, that for example we can assign to the
Handle property of the corresponding object:
Image1.Picture.Bitmap.Handle :=
LoadBitmap(hInstance, 'sample_bmp');
Icon.Handle := LoadIcon(hInstance, 'sample_ico');
Screen.Cursors[1] := LoadCursor(hInstance, 'sample_cur');
For more alternatives when loading image resources, see the API LoadImage.
Other resources are little bit more difficult to manage. Let's start
with JPEG images. The following function uses TResourceStream to load
the resource as a stream that will be loaded into a TJPEGImage object:
function GetResourceAsJpeg(const resname: string): TJPEGImage;
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(hInstance, ResName, 'JPEG');
try
Result := TJPEGImage.Create;
Result.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
Example:
var
Jpg: TJPEGImage;
begin
// ...
Jpg := GetResourceAsJpeg('sample_jpg');
Image2.Picture.Bitmap.Assign(Jpg);
Jpg.Free;
// ...
end;
For WAV files we need a pointer to the resource loaded in memory,
and for a text file we need to load a resource in a string. We can
do it using TResourceStream, but let's see an example using the API:
function GetResourceAsPointer(ResName: pchar; ResType: pchar;
out Size: longword): pointer;
var
InfoBlock: HRSRC;
GlobalMemoryBlock: HGLOBAL;
begin
InfoBlock := FindResource(hInstance, resname, restype);
if InfoBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
size := SizeofResource(hInstance, InfoBlock);
if size = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
GlobalMemoryBlock := LoadResource(hInstance, InfoBlock);
if GlobalMemoryBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
Result := LockResource(GlobalMemoryBlock);
if Result = nil then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
function GetResourceAsString(ResName: pchar; ResType: pchar): string;
var
ResData: PChar;
ResSize: Longword;
begin
ResData := GetResourceAsPointer(resname, restype, ResSize);
SetString(Result, ResData, ResSize);
end;
Sample calls:
var
sample_wav: pointer;
procedure TForm1.FormCreate(Sender: TObject);
var
size: longword;
begin
...
sample_wav := GetResourceAsPointer('sample_wav', 'wave', size);
Memo1.Lines.Text := GetResourceAsString('sample_txt', 'text');
end;
Once we have the wave resource loaded into memory we can play it
as many times as we want by using the API sndPlaySound declared
in the MMSystem unit:
procedure TForm1.Button1Click(Sender: TObject);
begin
sndPlaySound(sample_wav, SND_MEMORY or SND_NODEFAULT or SND_ASYNC);
end;
There are some resources (like fonts and animated cursors) that can't
be used from memory. We necessarily have to save these resources to a
temporary disk file and load them from there. The following function
saves a resource to a file:
procedure SaveResourceAsFile(const ResName: string; ResType: pchar;
const FileName: string);
begin
with TResourceStream.Create(hInstance, ResName, ResType) do
try
SaveToFile(FileName);
finally
Free;
end;
end;
The following function makes use of the previous one to save a resource in a temporary file:
function SaveResourceAsTempFile(const ResName: string;
ResType: pchar): string;
begin
Result := CreateTempFile;
SaveResourceAsFile(ResName, ResType, Result);
end;
The discussion of the function CreateTempFile falls beyond the scope of
this article and its implementation can be seen in the example attached
to the newsletter.
The following function makes use of SaveResourceAsTempFile to save an
animated-cursor resource to a temporary file, then it loads the cursor
from the file with LoadImage and finally deletes the temporary file.
The function returns the handle returned by LoadImage:
function GetResourceAsAniCursor(const ResName: string): HCursor;
var
CursorFile: string;
begin
CursorFile := SaveResourceAsTempFile(ResName, 'ANICURSOR');
Result := LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0,
0, LR_DEFAULTSIZE or LR_LOADFROMFILE);
DeleteFile(CursorFile);
if Result = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
Sample call:
Screen.Cursors[1] := GetResourceAsAniCursor('sample_ani');
Form1.Cursor := 1;
Well, that's it. I hope you find it useful. You can find more information
about resource files in the MSDN Library:
http://msdn.microsoft.com/library/en-us/winui/hh/winui/rc_6cs3.asp