Overview
One of the most widely used practices for generating a small executable is to modularize the project into several EXEs, DLLs, BPLs, and keep separate auxiliary text, image, or sound files.
This practice is helpful for decreasing file traffic during system updates by performing the update only on those files that have actually changed.
However, if it is necessary to keep a single file for the whole system, Delphi offers this possibility through its IDE.
In this article, we will have a look at how to insert external files inside the executable and access them later when it is necessary.
Adding files to the Delphi project
Although this Delphi “ Add resource file to project” feature has existed since Delphi version 3, the way to incorporate external files into the executable has been significantly improved since version 2009. Now you can use a resource manager that can be accessed via the menu Project > Resources and Images. In Screenshot 1 you can see several files added through the resource manager screen.
Once files are added, they appear in Delphi’s Project Manager, where you can select them and change their properties in the Object Inspector as shown in Screenshot 2.
Once the files have been added, when the project is compiled, a text file with extension *.rc is generated, which contains on each line an “alias” to reference the file, its physical path and the resource data type. The content of tOnce the files are added and the project is compiled, a text file with the extension *.rc is generated. On each line, it contains an “alias” to reference the Delphi resource file, its physical path, and the resource data type. The content of this file referring to Figure 2 can be seen in the following image.
After the *.rc file has been generated, Delphi compiles it into an intermediate version with extension *.dres, which will be used during the linking process, and later generates a new file with extension *.res, which is finally eAfter a *.rc file is generated, Delphi compiles it into an intermediate version with the extension *.dres, which will be used during the linking process, and it will generate a new file with the extension *.res, which will be finally encapsulated inside the executable.
Reusing text files
Files that do not fit into the predefined standards for resources (bitmaps, icons, and font files), such as text files, are encapsulated as a binary type called RCData. To access them, it is necessary to use a class called TResourceStream, which allows you to load the file into any component that has a property of the TStrings type, using a single code line. For example, we can easily load the contents of the file and display them in a ListBox with the following command:
12345678910 | var
LStream : TResourceStream;
begin
LStream := TResourceStream.Create(HInstance, 'TXTuf', RT_RCDATA);
try
ListBox1.Items.LoadFromStream(LStream);
finally
LStream.free;
end;
end; |
When you are instantiating the TResourceStream class while calling the LoadFromStream method of the TStrings class, it is necessary to use at least three parameters: a pointer variable called HInstance, the “alias” that references the resource, and a constant that represents the data type in which the resource is stored in the executable.
Reusing image files
Image files can be reused in two ways, depending on their type. If they are icon images, *.bmp files, or cursors, there is the LoadFromResourceName method, which allows you to load them with just one code line:
Note that since this is an image with the *.bmp extension, the method does not contain additional conversion routines, unlike in the cases with images of other formats, which are compiled as binaries.
The second method is used for dealing with PNG or JPEG images. In such cases, it is necessary to use classes that properly handle the resource. We can see how it can be done in the example below.
123456789101112 | procedure TFormExample.BtnImageClick(Sender: TObject);
var
PngImage: TPngImage;
begin
PngImage := TPngImage.Create;
try
PngImage.LoadFromResourceName(HInstance, 'IMGDevmediaLogo');
Image1.Picture.Graphic := PngImage;
finally
PngImage.Free;
end;
end; |
Line 05: We instantiate an object of the TPngImage type;
Line 07: The LoadFromResourceName method gets the copy of the image of the *.png type that was compiled as RCData;
Line 08: We assign the png instance to the Graphic property, as a result, component make a copy of our png and the image is displayed in the component;Line 10: We free our local object of TPngImage to prevent memory leaks.
Reusing audio files, executables, DLLs, and others
Files of the types other than those that we have considered so far are also compiled as RCData, but to use them you have to extract them using the combination of classes TResourceStream and TFileStream, as shown below. In this example, we used an MP3 file, but the same code can work for other types as well, such as executables or DLLs.
12345678910111213141516171819 | procedure TFormExample.btnExecutarMP3(Sender: TObject);
var
ResourceStream: TResourceStream;
FileName: string;
MediaPlayer: TMediaPlayer;
begin
ResourceStream := TResourceStream.Create(hInstance, 'WAVClock', RT_RCDATA);
try
FileName := ExtractFilePath(Application.ExeName) + 'file.mp3';
ResourceStream.SaveToFile(FileName);
finally
ResourceStream.Free;
end;
MediaPlayer.Close;
MediaPlayer.FileName := FileName;
MediaPlayer.Open;
MediaPlayer.Play;
end; |
Line 07: We instantiate a TResourceStream, passing the alias of the MP3 file and the data type (RCData) as a parameter;
Line 09: We assign the path of a directory to extract the file to the string variable;
Lines 10 and 11: We save all the contents of the resource loaded in line 7 into the file;
Lines 15 to 18: We execute the MP3 file extracted from the executable.
Conclusion
Although Delphi resources can be very useful for transporting files in the executable itself and reusing them within the system, you should be careful with them. Since there is no effective file compression, its size usually increases more than expected.
At Softacom, we have solid expertise in working with Delphi, which allows us to find the most optimal solutions to tasks of different complexity. If you need professional assistance for your Delphi project realization, our team is always open to cooperation.