Having already Googled for a solution to this, none of the suggestions work for me so either I've missed something or I've done something wrong.
I have a number of packages within my Delphi XE2 application, of which a couple of these packages contain RCDATA resources which are ultimately PNG images.
The control(s) which uses the resource(s) works absolutely fine in the Delphi XE2 designer - the resource is loaded and drawn as expected. When I come to run the main application in which the package/control is used, I'm presented with an EResNotFound exception when the control attempts to load the resource item:
handle := FindClassHInstance(Self.ClassType);
fPlaneImage := TPngImage.Create;
fPlaneImage.LoadFromResourceName(handle, 'IDC_IMG_PLANE');
And when the application runs, I get the following error:
Project PinPoint.exe raised exception class EResNotFound with message IDC_IMG_PLANE not found'.
And if I hit continue in the debugger, I then get:
Project PinPoint.exe raise exception class EPNGCouldNotLoadResource with message 'The png image could not be loaded from the resource ID.'.
I've tried passing all variations of handle into the LoadFromResource function:
fPlaneImage.LoadFromResourceName(HInstance, 'IDC_IMG_PLANE');
fPlaneImage.LoadFromResourceName(GetModuleHandle('AppCore.bpl'), 'IDC_IMG_PLANE');
fPlaneImage.LoadFromResourceName(FindClassHInstance(Self.ClassType), 'IDC_IMG_PLANE');
Yet they all yield the same exception.
I was considering whether it would be easier to create a standalone package which contained all the resources and then refer to this from each of the other packages within the project, rather than each package having their own resources however I'm unsure as to whether this would actually work.
Please could anyone offer any advice.
Try to use the below FindResModule function to get the resource module.
// use fPlaneImage.LoadFromResourceName(FindResModule('IDC_IMG_PLANE'), 'IDC_IMG_PLANE');
function FindResModule(const AResourceName: string): HINST;
var
LModule: PLibModule;
begin
LModule := LibModuleList;
while Assigned(LModule) do
begin
Result := LModule.Instance;
if (Result <> 0) and (FindResource(Result, PChar(AResourceName), RT_RCDATA) <> 0) then
Exit;
LModule := LModule^.Next;
end;
Result := 0;
end;
In case it fails most likely the resource is "eliminated by the linker". It may happen for example when you create your own packages and you miss to add the unit with the resource to your package or application file. Just add the unit to the uses clause of your application.
I see no harm if you create a separate package for resources. It should work fine but it is not actually required. Just one small hint, BPL package is not required, you may pack your resources into a DLL
Related
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);
In my project, i'm trying to connect one more UNIT, named Lang_Unit.pas with some classes and procedures, but, while compiling the Project, Delphi gives unknown error called "[dcc32 Fatal Error] Lang_Unit.pas(5): F2084 Internal Error: AV07953449-R26D7474C-0".
And the point is that, what if i will close my project, or remove this connected UNIT, error is not getting away.
And if i will create clear default VCL Application, its still gives this error.
And only when i'm restarting my Delphi 2010, error is getting away.
But, if i will try to edit something in the code, this error is comes again...
What is problem ? Everything was works fine, im not touched nothing.
I've just turned off my PC, then after some time turned it ON and opened my Project and edited my code, then i see this error...
If Its will help, here is my Lang_Unit.pas code :
unit Languages_UNIT;
interface
Uses
System.Generics.Collections, IniFiles;
Type
TLanguages_List = Class
private
LangType:string;
LangDescription:string;
LangFile:TIniFile;
public
Constructor Create(LType,LDes:string; LFile:TiniFile);
Function GetLangType:string;
Function GetDescription:string;
Function GetStructure:TIniFile;
End;
TLanguages_Controller = Class
public
Function GetStructureByType(RequestedType:string; LangList:TObjectList<TLanguages_List>):TIniFile;
Function TypeExists(RequestedType:string; LangList:TObjectList<TLanguages_List>):Boolean;
Procedure LoadLanguage(RequestedType:string; LangList:TObjectList<TLanguages_List>);
End;
implementation
uses Unit1;
Constructor TLanguages_List.Create(LType,LDes:string; LFile:TiniFile);
Begin
LangType:=LType;
LangDescription:=LDes;
LangFile:=LFile;
End;
Function TLanguages_List.GetLangType:string;
Begin
Result:=LangType;
End;
Function TLanguages_List.GetDescription:string;
Begin
Result:=LangDescription;
End;
Function TLanguages_List.GetStructure:TIniFile;
Begin
Result:=LangFile;
End;
Function TLanguages_Controller.GetStructureByType(RequestedType:string; LangList:TObjectList<TLanguages_List>):TIniFile;
var
i:integer;
Begin
For i := 0 to LangList.Count-1 Do
Begin
IF(LangList[i].GetLangType=RequestedType) Then
Begin
Result:=LangList[i].GetStructure;
Break;
End;
End;
End;
Function TLanguages_Controller.TypeExists(RequestedType:string; LangList:TObjectList<TLanguages_List>):Boolean;
var
i:integer;
GOTYA:Boolean;
Begin
GOTYA:=False;
For i := 0 to LangList.Count-1 Do
Begin
IF(LangList[i].GetLangType=RequestedType) Then
Begin
GOTYA:=True;
Break;
End;
End;
IF(GOTYA) Then
Result:=True
Else
Result:=False;
End;
Procedure TLanguages_Controller.LoadLanguage(RequestedType:string; LangList:TObjectList<TLanguages_List>);
var
i:integer;
SLS:TIniFile;//SELECTED LANGUAGE STRUCTURE
CS:string;//CURRENT SECTION
Begin
//GET SELECTED LANGUAGE STRUCTURE
For i := 0 to LangList.Count-1 Do
Begin
IF(LangList[i].GetLangType=RequestedType) Then
Begin
SLS:=LangList[i].GetStructure;
Break;
End;
End;
//START LOADING SELECTED LANGUAGE
//TABS SECTION LOAD
CS:='TABS';
SD_DEFNAME:=SLS.ReadString(CS,'Speed_Dials','Speed_Dials');
Form1.goleft.Hint:=SLS.ReadString(CS,'Back','Back');
Form1.goright.Hint:=SLS.ReadString(CS,'Forward','Forward');
REFLESHBTN_TEXT:=SLS.ReadString(CS,'Reflesh','Reflesh');
STOPBTN_TEXT:=SLS.ReadString(CS,'Stop','Stop');
//PAGE_POPUP SECTION LOAD
CS:='PAGE_POPUP';
Form1.ChromiumPopup.Items[0].Caption:=SLS.ReadString(CS,'Forward','Forward');
Form1.ChromiumPopup.Items[1].Caption:=SLS.ReadString(CS,'Back','Back');
Form1.ChromiumPopup.Items[2].Caption:=SLS.ReadString(CS,'Reflesh','Reflesh');
Form1.ChromiumPopup.Items[3].Caption:=SLS.ReadString(CS,'Copy_Link','Copy Link');
Form1.ChromiumPopup.Items[4].Caption:=SLS.ReadString(CS,'Save','Save');
Form1.ChromiumPopup.Items[5].Caption:=SLS.ReadString(CS,'Print','Print');
Form1.ChromiumPopup.Items[6].Caption:=SLS.ReadString(CS,'view_source','View Source');
Form1.ChromiumPopup.Items[7].Caption:=SLS.ReadString(CS,'code_debug','Code Debug');
End;
end.
Internal error means that the compiler itself is in a 'confused' state.
The way to get out of this is to:
Save your code in a safe location for later reference.
Restart Delphi
Revert the source code to the last known good state by undoing your last edits, or by loading a temp save file.
You can find the previous files in the _backup folder.
Make sure to set file type to any file.
In order to have Delphi generate a save file upon compilation you need to enable autosave
It's a good idea to have Delphi keep more than the default 10 saves. I like to set it to the max: 90.
Just keep restarting Delphi, and compile a previous version, until the internal error goes away.
Then you just recreate the code in a slightly different manner.
(You did save the original code right?)
I also had this problem (in Delphi 10 Berlin). It started shortly after I changed the name of a component in a frame. It also seemed very persistent. However I found the by right clicking the project and selecting 'Clean' followed by 'Build' solved the problem.
I had this issue with my system drive's memory less than 300mb left. It was especially choking in the cache folder. Prior to this error I had a sensible error (fatal error DBG) when I was attempting to recurse already async functions into a larger async function in a big multi threaded application. The compiler just gave up!(perhaps for circular references and too many subfunctions of a function) The two errors may not be related. But after freeing the system drive to about 2 Gigs upon restart and correcting the above mistake, I did a clean then it compiled just fine.
In my case, the solution for the F2084 error code was to change the encoding from ANSI to UTF8.
In my case, I made several packages that related one to another, and all were built as design and run time packages. But one day, I changed some of them to run-time only packages. After that I experienced this kind of error. It took me hours to realize that I should rebuilt all other related packages. After doing that, the error was away eventually.
I've been looking hours over the net, using Google, trying for PDF's and still unable
to load an Image resource in Delphi 7..
My test.rc file goes like this:
1 RT_BITMAP "1.bmp"
I've added the test.rc file to my project using Project->Add to Project.. which compiled a test.res file upon build and seems to have automatically included the .res file into my project (because using the {$R test.res} would say I already use that resource). I also tried removing the test.res from the project using Project->Remove from Project.. and manually adding the {$R test.res} to my project.
However no matter how I include the test.res file..
I get the
Project Project2.exe raised exception
class EAccessViolation with message
'Access violation at address 00408D0C
in module 'Project2.exe'. Read of
address 00000001'. Process stopped.
Use Step or Run to continue.
at First I used
Image1.Picture.Bitmap.LoadFromResourceID(hInstance,1);
Because this is what I found using Google. And I got this error.
Later I tried
procedure TForm1.Image1Click(Sender: TObject);
var bBitmap : TBitmap;
begin
bBitmap := TBitmap.Create;
try
bBitmap.Handle := LoadBitmap(hInstance, '1');
Image1.Width := bBitmap.Width;
Image1.Height := bBitmap.Height;
Image1.Canvas.Draw(0,0,bBitmap);
finally
bBitmap.Free;
end;
end;
This didn't get me any errors, and nither did it show the image so the problem remains unsolved..
I'm a newbie to the use of Resources but I must load some images into resources before I release my project so the .BMP files won't be tempered with...
any help would be highly appreciated!
I reproduced exactly your same problem in a test program.
I then changed RT_BITMAP to BITMAP, recompiled RC and tested.
It works.
I have a Delphi form inside a DLL (I know that this restricts the use of the DLL to Delphi but this is not a problem in this case).
The DLL exports a function ShowForm that looks roughly like this:
procedure ShowForm (App : TApplication);
begin
OldApp := Application;
try
Application := App;
MyForm := TMyForm.Create (nil);
try
MyForm.ShowModal;
finally
FreeAndNil (MyForm);
end;
finally
Application := OldApp;
end;
end;
Now on the form I use a TAdvOfficeHint (from the TMS component pack). Unfortunately the hints do not show up.
Am I missing something here? How can I make the form behave exactly as it would if I showed it from the main application?
Thanks!
I don't know TAdvOfficeHint but I guess it hooks Application.OnShowHint to set its own THintWindowClass, and even if both the main executable and the DLL are linking in the TMS unit, they each have their own copy of the class which is where things go wrong.
Assigning Application is not enough: there are other global variables, like Screen, Mouse, etc. Others are even hidden in the implementation so I'd say your chances to make the form behave exactly as from the main application are slim.
Just found the reason why it does not work. As TOndrej states, TAdvOfficeHinthooks Application.OnShowHint and internally executes the following line of code:
FHintInfo.Assign (AHintInfo);
Assign internally uses a dynamic type check
if (Source is TAddvHintInfo) then ...
which fails due to the separate type registries of the DLL and the main application.
I have run into this problem a few times now and maybe I really have to switch to runtime packages to avoid all this stuff.
Anyway, if there's anything I can do to prevent this, please comment.
Wrong setting of Application.
Try this and see if it solves your problem:
procedure ShowForm (AppHandle : THandle);
begin
OldAppHandle := Application.Handle;
try
Application.Handle := AppHandle;
........
finally
Application.Handle := OldAppHandle;
end;
end;
I guess in Delphi 2006 and later versions you can call System.ShareMemoryManager method in the EXE code, so that its memory manager is shared with other modules loaded in the process memory space.
I am going through most of my applications and porting them to D2009 and I have one application that makes use of dynamic packages. For the life of me I cannot get my host application to recognize classes registered in a package. I traced through and the initialization section in the package being loaded was called and RegisterClasses was called but when I do a GetClass() call the classes are not available. Is there someone out there who can enlighten me as to what might be going on? I have researched and looked to see if there are any issues with the D2009 release and dynamic packages and so far I have found nothing. I'm beginning to wonder if I have a corrupted installation of Delphi or some other problem.
TIA
If you are using a 3rd party memory manager then make sure it is proven to work with D2009 (actually 2007 and up).
With FastMM (which is the default MM since 2007) you would have to set the UseRuntimePackages define in FastMM4Options.inc
make sure that the following steps are done:
Create a new package in Delphi;
Insert a form in this package;
Insert a "inicialization" section in the form and uses the RegisterClass method. (registerClass(TForm1)); Don't forget the "T".
Save and compile the package;
Close all;
Copy the .bpl file (c:\Users\Public\Documents\RAD Studio\5.0\Bpl) to the application folder;
Create a new aplication in Delphi;
Go in Project > Options > Packages, and check the box "Build with runtime packages";
Leave only "vcl;rtl" in the text field and click OK button;
Insert a button;
In the source of the button, insert the code:
procedure TForm1.Button1Click(Sender: TObject);
var
PackageModule: HModule;
AClass: TPersistentClass;
begin
PackageModule := LoadPackage('Package1.bpl');
if PackageModule <> 0 then
begin
AClass := GetClass('TForm2');
if AClass <> nil then
with TComponentClass(AClass).Create(Application)
as TCustomForm do
begin
ShowModal;
Free;
end;
UnloadPackage(PackageModule);
end;
end;
Compile the application. =)