Add resource file by code with Delphi - delphi

I have a custom res file "myres.res".
In this moment I use it on my application, I have add {$R myres.res} under {$R *.res} line inside my DPR project file and it work very well.
Now I'd like creare a VCL component with a boolean property "UseCustomRes". If I set UseCustomRes=True I'd like add the res file when I compile my project but if I set UseCustomRes=False I don't want use res file when I compile my project.
Is this possible? I don't know if it possible and how it is possible.

Picking a resource at runtime
If you want to use a resource (or not) based on a runtime variable you'll have to compile it in always, otherwise you'll lose the option of using it at runtime.
Whilst running you can access a resource using TResourceStream.
Here's an example:
procedure ExtractResource;
var
rs: TResourceStream;
begin
rs:= TResourceStream.Create(HInstance, 'NameOfResource', RT_RCDATA);
try
rs.Position:= 0;
rs.DoSomethingWithTheResource...
finally
rs.Free;
end;
end;
Here's the online help: http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TResourceStream
Note that the help entry for TResourceStream is a bit broken; it does not show all methods.
The missing methods are here: http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TStream_Methods
Picking a resource at compile time
Note that the {$R *.res} line includes any .res file in the current directory.
If you want to select a specific .res file, you'll have to exclude this line.
Conditional compilation is done using defines, like so:
implementation
{.R *.res} //disable the *.res inclusion.
{$IFDEF GetMyResource}
{$R MyResource.res} //will only be compiled if `GetMyResource` is defined
{$ENDIF}
{$R AlwaysIncludeThisResource.res} //will always be included.
You then define the symbol GetMyResource in the Conditional defines under project options, see here: https://stackoverflow.com/a/4530320/650492

Resources are included by the linker based on the existence of special $RESOURCE directives. These directives cannot be switched based on the value of an object instance's property.
So, with the built in tooling there is now way to achieve what you desire. What you will need to do is to add a post-build step that modifies the output file by adding the resource if needed. A good example of a tool which does exactly this is madExcept.

Related

Extracting Resources(i.e .MP4 file) from .BPL packages [duplicate]

I want to make a component that uses some resources compiled in my package's project. My component will try to use this resource (a PNG image) during runtime (at its constructor).
I want to use it in my application's project, but as soon as the component gets created, I get the following exception:
First chance exception at $7579B9BC. Exception class EResNotFound with message 'Resource xxx not found'. Process yyy.exe (6060)
What am I missing here?
EDIT
The calling code for the resource in the package's project is like this:
Png.LoadFromResourceName(HInstance, 'png_resource_name');
EDIT 2
As suggested by David, I tried using the function GetModuleHandle, but it will always return 0 if I call it from the package's project or the application's project. The code being called in the package's project is like this:
PackageModuleHandle := GetModuleHandle(PChar('my_package.bpl'));
Png := TPngImage.Create;
Png .LoadFromResourceName(PackageModuleHandle, 'png_resource_name');
Absolute paths to the bpl file won't work either.
EDIT 3
New attempt based on new answer:
PackageModuleHandle := FindClassHInstance(TMyComponent);
Png := TPngImage.Create;
Png .LoadFromResourceName(PackageModuleHandle, 'png_resource_name');
Fails with the same exception.
EDIT 4
Using ResourceHacker, and if I used it right, the resources doesn't seem to be in my bpl file. What could I be doing wrong about this? Seems such a complicated matter to such a simple feature.
CONCLUSION
I had to add the .res file of my package to the package's .dpr just after the {$R *.res} line. Like this:
{$R *.res}
{$R 'my_pacakge.res'}
Also, I had to include the my_package.rc file to my project, so the resources would get compiled to the .res after each build. That did the trick, I guess. Thanks for all the answers.
You need to use FindClassHInstance(), specifying your component's class type, instead of using the global HInstance variable or GetModuleHandle(). That way, you obtain the correct module handle regardless of whether the package gets statically linked or dynamically linked into the main executable.
You are passing HInstance, the handle of the executable module to the resource loading function. That fails because the resource lives in the package module. Therefore you need to pass the module handle for the package. You can obtain the module handle of the package like this:
PackageModuleHandle := GetModuleHandle(PChar('MyPackage.bpl'));
If you are loading your package dynamically then the call to LoadPackage returned the module handle.
Update: Remy's suggestion of using FindClassHInstance is clearly a better way to obtain the module handle.
A component that uses a resource that is implemented in a unit called MyUnit1.pas should include the following line inside:
{$R MyUnitRc.res MyUnitRc.rc}
Note that the above syntax won't work on some old delphi versions (Delphi 7). The above works on 2010,XE,XE2 and so on, and it compiles the .rc into a .res when the project builds that unit. Alternatively you would add the {$R} declaration to each statically linked application and also to your BPL's .dpr file.
That .RC file mYUnitRc.res will contain one or more lines declaring your resources. I use RCDATA declarations to load PNGs, like this:
MYIMAGENAME RCDATA "MyFile.png"
If you do it this way, instead of using an RC file added to the .dproj/.dpr only, then it will work in two important cases:
When the BPL is loading at designtime.
When the component is loading at runtime from within an application that is compiled with Runtime Packages turned off.
To handle the third case, the one that the other answers (Remy and David) deal with, you do need to call FindClassHinstance as Remy stated. However I believe you should also look at how you are including the resource file and having it compiled and linked, both within your package, and within applications that use the component.
Here is some sample code showing loading a resource at runtime into a TPngImage, that I use in my own components:
procedure TSampleControl.LoadImageFromResource( button:TSubControl);
var
png:TPngImage;
begin
if button.DefaultResourceId='' then exit;
png := TPngImage.Create;
try
try
png.LoadFromResourceName(HInstance,button.DefaultResourceId);
button.Glyph.Assign(png);
except
on E:Exception do begin
OutputDebugString( PChar(button.DefaultResourceId+' load error:'+E.Message) );
end;
end;
finally
png.Free;
end;
end;
Note that I catch resource load exceptions, which leave some elements in my control without their glyphs but at least does not crash Delphi. HInstance may need to be changed as David and Remy Point out to use LoadFromResourceName to handle the case where you need to load from the .BPL but I don't think that you can assume that a person who uses your components is always going to redistribute your BPL file, and thus FindClassHinstance is preferable to GetModuleHandle..
Update: I should have used what Remy suggested:
png.LoadFromResourceName( FindClassHInstance(TSampleControl),
button.DefaultResourceId);

Delphi OpenTools API - Editing the project requires clause

I have written an OpenTools wizard for creating a skeleton for a custom project type. It does work, and the project and units are properly created. But, how do I edit the .dpk or .dpk file's requires clause?
A call to ModuleServices.CreateModule(MyIOTAProjectCreatorInterface) gives me the .dproj file only.
In my VCL Component Installer (since XE, this is part of the Delphi IDE), I do it this way:
procedure TCompInstallWizard.AddReferenceFiles(InstallProject: IOTAProject;
const FileNames: array of string);
var
ReferenceFile: string;
begin
WriteDebugMessage('AddReferenceFiles');
for ReferenceFile in FileNames do
if not ContainsFile(InstallProject, ReferenceFile) then
InstallProject.AddFile(ReferenceFile, False);
end;
with the help of function IOTAProject.AddFile(FileName, IsUnitOrForm). Note that I call it like this:
if FPersonality = ppCppBuilder then
AddReferenceFiles(InstallProject,
['rtl.bpi', 'designide.bpi', 'vcl.bpi', 'vclactnband.bpi',
'vclx.bpi', 'xmlrtl.bpi'])
else
AddReferenceFiles(InstallProject,
['rtl.dcp', 'designide.dcp', 'vcl.dcp', 'vclactnband.dcp',
'vclx.dcp', 'xmlrtl.dcp']);
Note that the docs say:
{ Call this function to add an arbitrary file to the project. NOTE: some
files have special meaning to different projects. For example: adding
VCL60.DCP will cause a new entry in a package project's "requires" list
while it will be a raw file to any other project type. Set IsUnitOrForm
to true for files that are considered items that the project would
process directly or indirectly (ie. .pas, .cpp, .rc, etc..) or can be
opened in the code editor. For all others, including binary files
(.res, .bpi, .dcp, etc..) set this to False. }
procedure AddFile(const AFileName: string; IsUnitOrForm: Boolean);
This means that if you add a 'bla.dcp' it will automatically land in the requires section, and if you add a 'bla.pas' file, it will land in the contains section. It took me a while to find out.

Programmatically get all units used in a dpr in Delphi

I am new in Delphi and I am trying to make an application in which I will give as an input a .dpr file and the application will create a list with all the .pas files used by this .dpr... I still cannot find a function in Delphi or a way to read the uses of the .dpr in order to navigate through the file system to these pas files and read their uses, and so on... Does anyone has any idea on how to achieve this?
It's not exactly straightforward: You don't just need to read the .dpr file, but you also need to parse the .dproj and registry to get Search Paths. If you're trying to do this right, you also have to parse the .dpr and .pas files as code files so you can find the uses statements, handle {$I '...'} includes, {$IFDEF} blocks, interface vs implementation sections, and so on.
All that said, you might want to look to the open source CnPack and GExperts projects for inspiration. Both of them have solved this problem, and you may be able to leverage their work towards whatever problem you're trying to solve.
If you let Delphi create a .map file (linker option), it will contain a list of all source and dcu files used in that project. GExperts does that, using a simple parser for a map file which is taken from my dzlib https://sourceforge.net/p/dzlib/code/HEAD/tree/dzlib/trunk/src/u_dzMapFileReader.pas
I would like to update this question for possible answer for future reference.
Create a separate unit file (PhonyObject.pas)
unit PhonyObject;
interface
uses
System.Classes, FMX.Forms, FMX.Dialogs;
type
TPhonyObject = class(TObject)
end;
TPhonyClass = class of TPhonyObject;
procedure FindUnitName(anObject: TObject);
var
PhonyName: string;
PhonyClass: TPhonyClass;
PhonyInstance: TObject;
PhonyClassName: procedure(anObject: TObject) = FindUnitName; //function: String = FindUnitName;
implementation
uses System.TypInfo;
procedure FindUnitName(anObject: TObject);
begin
if anObject <> nil then PhonyName := anObject.UnitName
else if not (TObject.UnitName <> 'System') then
begin
if TypInfo.GetTypeData(anObject.ClassInfo) <> nil then PhonyName := String(GetTypeData(anObject.ClassInfo)^.UnitName);
end else PhonyName := TObject.UnitName;
//FreeAndNilProperties
end;
initialization
PhonyClass := TPhonyObject;
PhonyInstance := PhonyClass.Create;
ShowMessage('Unit Name =' + PhonyInstance.UnitName);
PhonyInstance.Free;
finalization
PhonyClass := nil; //PhonyClass.Free;
end.
And in order to use this inside another (multiple) units, this is the code I have used so far, but I hope to update it later on. I have this showing up inside a hand made "console" with black background and white text in a TMemo. If anyone wants the code for the TMemo (its not commonly known), or how to show all these inside basically a debug window, all you just have to do let me know. This is the best I have gotten it so far, but I need a better understanding of the child/parent object/classes
unit AnotherUnit;
interface
uses
System.Classes, PhonyObject;
type
TPhonyObj = class(TPhonyObject)
end;
//var
implementation
{$R *.fmx}
uses ...;
initialization
PhonyClass := TPhonyObj;
PhonyInstance := PhonyClass.Create;
ShowMessage('UnitName= ' + PhonyInstance.UnitName + ' (AnotherUnit)'); // PhonyClass.UnitName // PhonyClassName(PhonyInstance);
PhonyInstance.Free;
finalization
PhonyClass := nil;
end;
I used as unique of Unit Names and class names, as I could and I realize I don't actually use any objects till the end, none the less it should work with out any problems. Please comment if there are some better ideas, but I think this is a powerful feature for Delphi programming when you can predict when certain unit names are going to suddenly show up. And how to predict for them too.

Is there a conditional define for library projects in Delphi?

I have a utility unit with code shared between a few applications and DLLs. I'd like to selectively compile portions of this unit based upon the current project type: Application, Package, or Library. I couldn't find any conditional definitions for a library or package in the System unit, and Google didn't find anything relevant. So, are there any conditional defines such as IsLibrary or IsPackage set by the compiler? I'm aware of the IsLibrary variable, by the way.
There is no such pre-defined conditional, and there could not be such a conditional. That's because at compilation time it is impossible to know whether the unit will, ultimately, be linked into an executable, a library or a package.
In fact, the same compiled unit could be linked into any or all of the above project types. And indeed you can see this yourself when you link the RTL into your projects. You link the same System unit, the same compiled .dcu file, into all your projects, irrespective of the project type.
IMHO there is absolutly no need for such conditionals because of existing conventions.
Compiling an Application or Library (the same on this compiling aspect) or a Package differs like so:
Application/Library will compile only the used parts from the unit
Package will compile all parts from the unit referenced by the interface part of the unit
Example Unit
unit foo;
interface
procedure foo1;
procedure foo2;
implementation
procedure foo3;
begin
// used by foo2, compile depends on foo2 compilation
end;
procedure foo4;
begin
// will never be compiled, because is never used
end;
procedure foo1;
begin
// Package: will always be compiled
// Application/Library: will be compiled if used
end;
procedure foo2;
begin
// Package: will always be compiled
// Application/Library: will be compiled if used
foo3;
end;
end.
That is also a reason, why using packages may result in bigger exe files, because it can contain unused code parts from precompiled dcu files.

Delphi 7 make complains about files not found

I've got a BPG file that I've modified to use as a make file for our company's automated build server. In order to get it to work I had to change
Uses * Uses
unit1 in 'unit1.pas' * unit1
unit2 in 'unit2.pas' * unit2
... * ...
in the DPR file to get it to work without the compiler giving me some guff about unit1.pas not found.
This is annoying because I want to use a BPG file to actually see the stuff in my project and every time I add a new unit, it auto-jacks that in 'unitx.pas' into my DPR file.
I'm running make -f [then some options], the DPR's that I'm compiling are not in the same directory as the make file, but I'm not certain that this matters. Everything compiles fine as long as the in 'unit1.pas is removed.
It could come from the fact, that the search path in the IDE and the search path of the command line compiler are not the same. If you change the serach path of the command line compiler you might be able to use the exactely same source code as within the IDE.
One possibility to configure the search path for the command-line compiler is to do it in a file called dcc32.cfg. Take a look at the help, there is a short description of dcc32.cfg in the IDE-help.
Well this work-around worked for me.
//{$define PACKAGE}
{$ifdef PACKAGE}
uses
unit1 in 'unit1.pas'
unit2 in 'unit2.pas'
...
{$else}
uses
unit1
unit2
...
{$endif}
The only problem is whenever you add a new unit, delphi erases your ifdef package at the top.
Every time I have to put conditionals into a project file I do this:
program a;
uses
ACondUnits;
...
unit ACondUnits;
interface
uses
{$IFDEF UseD7MM}
Delphi7MM;
{$ELSE}
FastMM4;
{$ENDIF}
implementation
end.
Maybe this trick works in packages too. Never tried.

Resources