Reputation: 2612
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.
Upvotes: 6
Views: 7211
Reputation: 69114
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);
Upvotes: 2
Reputation: 613572
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.
Upvotes: 3
Reputation: 598299
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.
Upvotes: 8