Embedding files as resources in a Delphi executable

You can learn how to create your own software for any situation with the excellent Software Requirements Unleashed.  If you need to know how to embed icons, cursors, animated cursors, bitmaps, jpeg images, wave files, text files and any other file inside a Delphi executable, just keep reading!

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

You can find the full source code of this article in the archive that accompanies the Pascal Newsletter #25.

 

Share this article!

Follow us!

Find more helpful articles:

Comments

Feb
5

Another way to store any binary data into executable is to append binary data at last part of the executable. To read the binary data you put an ID and size of binary data at last part of executable. When application starts, to load embedded binary data, application read itself and seek to last part and read the ID. If ID matched, it seeks backward to look for binary data size. After size of embedded binary data is found, it seeks backward as many as binary data size. Now it points to start of embedded binary data and can read binary data to extract data.

By Zamrony P Juhara