I'm trying Delphi 10.4. Localizing Windows applications was working like a charm in the past, but now when I dynamically load the RC DLL file, it only changes RCDATA, and not the "String Table" any more.
I was using this code (as quick resume)
NewInst := LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE);
....
CurModule.ResInstance := NewInstance;
FileName is the DLL file that has the resources (RCDATA and "String Table") that I could edit with "Resource Hacker" software, and can see that it contains "String table" Inside as expected.
It works fine for RCDATA (all forms are getting translated) but not "String table" anymore that is contained into Resourcestring section of any .pas file, and all string remains in the original language.
It was working fine in previous Delphi version (like 10.2) and I do not know why it fails with this version.
There is another solution. Disable the new caching by assigning LoadResStringFunc to nil. A good place to do that is in the beginning of the program.
begin
>>> ADD THIS to disable the caching
LoadResStringFunc := nil;
>>>
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Core problem
The problem is that resourcestrings are cached now, and you do not have access to clear the cache after you loaded new resource data :(
See https://quality.embarcadero.com/browse/RSP-30853
(and drop a vote if you think it has to be fixed)
Work-around
I "Cloned" the resourcestring caching mechanism into a separate unit and re-routed LoadResStringFunc to my cloned unit. Here I allow access to the resstringcache, and thus can clear it after loading the new resource.
Related
I have the following issue.
I'm using Glut openGL for drawing certain elements in my application written in Delphi. The library file I'm using is called glut32.dll (placed where the EXE file is standing)
Now I decided to compile my app in 64-bit as other libraries it is using are going to be updated only for 64-bit. All fine, but the one thing that stops me is the glut32.dll (it is 32-bit). As the Glut project is not supported for many years already I found the freeglut alternative which claim to be replacement for the original Glut: https://freeglut.sourceforge.net/, and downloaded the freeglut.dll from here: www.transmissionzero.co.uk/software/freeglut-devel/ for MSVC.
As I looked at the .h header files it seems more or less the same as my code translation is in the *.pas files.
So I tried to just load the freeglut.dll instead of glut32.dll (dll is set to the correct dll name in the function below)
procedure LoadGlut(const dll: String);
begin
FreeGlut;
hDLL := LoadLibrary(PChar(dll));
if hDLL = 0 then raise Exception.Create('Could not load Glut from ' + dll);
#glutInit := GetProcAddress(hDLL, 'glutInit');
#glutInitDisplayMode := GetProcAddress(hDLL, 'glutInitDisplayMode');
#glutCreateWindow := GetProcAddress(hDLL, 'glutCreateWindow');
#glutCreateSubWindow := GetProcAddress(hDLL, 'glutCreateSubWindow');
#glutDestroyWindow := GetProcAddress(hDLL, 'glutDestroyWindow');
#glutPostRedisplay := GetProcAddress(hDLL, 'glutPostRedisplay');
...
It is loading with no errors and so on, also the procedures in it, but when the application start and reach a point to use one of these functions it just stop the debugger with no error message (like pressing Ctrl+F2).
procedure DrawArrow(P1, P2: TRPoint; Color: TRGBAColor);
var
R : Real;
begin
DrawLine(P1, P2, Color);
glColor4fv(#Color[0]);
glPushMatrix;
SetZAxis(P2, P1);
R := VectorModulus(VectorDifference(P1,P2));
glTranslated(0, 0, R);
SetSymbolScale;
glTranslated(0, 0, -ARROW_L);
glutSolidCone(ARROW_W/2, ARROW_L, SOLID_SLICES, SOLID_STACKS);
glPopMatrix;
glPopMatrix;
end;
The debugger shut itself at glutSolidCone(ARROW_W/2, ARROW_L, SOLID_SLICES, SOLID_STACKS); This function is defined in freeglut as well.
EDIT: If I call other simple function like glutInitDisplayMode it doesn't stop, so it seems that the library is correctly loaded. But it still keeps shutting down at glutSolidCone or other drawing functions.
I don't have much experience of using these header files and dll that comes with it, but my Delphi code should be a good translation in this case? Or not? I don't know how to debug this.
What is the way to adapt my code in order to fit freeglut in it. It should be something small I think as most of the things should be the same.
Thank you for the help
It seems that just replacing glut32.dll with freeglut.dll won't do the job properly as I guess functions inside differs a bit (or simply renaming freeglut.dll --> glut32.dll also fails). However I found a workaround published by NVIDIA: NVIDIA Cg Toolkit
It is not supported since 2013, but when installed it brings the dll version of glut32 both for 32-bit and 64-bit development. Quickly try it and it seems to work and cover what I have as definition in Glut.pas.
So if you need 64-bit version and replacement of glut32.dll it can be downloaded from there.
Meanwhile if somebody convert the freeglut headers (.h) to Delphi code it would be great as at the end the Freeglut projecy is still maintained, up-to-date and add some more useful functions in addition to the original Glut. This conversion is still beyond my knowledge.
Our program is something like this:
C:\Main
C:\Main\Utils
C:\Main\App.exe
For C:\Main and all subfolders, we have all privileges for Everyone set to allow Full Control.
In our code, we create a .bmp file:
BmpSt := TMemoryStream.Create;
BmpSt.SetSize(Length(dta));
BmpSt.WriteBuffer(dta[1],Length(dta));
BmpSt.Seek(0,soFromBeginning);
Bmp := '\Main\Util\' + FormatDateTime('YYYYMMDDHHNNSS',Now) + '.bmp';
BmpSt.SaveToFile(Bmp);
if not DeleteFile(Bmp) then begin
ShowMessage('No');
end;
If I don't run App.exe as an administrator, it will never delete the file. However, when I do run it as an administrator, it deletes the file just fine.
Privileges are set to Full Control for Everyone.
I know this might not exactly be a Delphi question since its heavily dealing with Windows UAC, but this code worked fine when our program was in Delphi 5, now we are building it in Delphi 10.
We are also getting similiar issues using a TPrinter object to insert the above .bmp onto a print page, but I'm confident that the issues with DeleteFile will solve those.
Right after using DeleteFile() you can use GetLastError() to get what went wrong and trigger an exception for it with CheckOSError(). Once you know why it is failing you can troubleshoot further.
if not deletefile(bmp) then begin
CheckOSError(GetLastError);
end;
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);
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. =)
I am trying to extract a gif image embedded as a resource within my ISAPI dll using WebBroker technology. The resource has been added to the DLL using the following RC code:
LOGO_GIF RCDATA logo.gif
Using resource explorer I verified it is in the DLL properly.
using the following code always throws an exception, "resource not found" (using Delphi 2009)
var
rc : tResourceStream;
begin
rc := tResourceStream.Create(hInstance,'LOGO_GIF','RCDATA');
end;
RCDATA is a pre-defined resource type with an integer ID of RT_RCDATA (declared in Types unit).
Try accessing it this way:
rc := tResourceStream.Create(hInstance,'LOGO_GIF', MakeIntResource(RT_RCDATA));
If I remember correctly you are actually dealing with an instance of the web server, not the dll. I don't remember the work around though, but that is the explanation for why that doesn't work. Hopefully someone else can build off of this.
Either use your own arbitrary resource type like GIF:
LOGO_GIF GIF logo.gif
then use
rc := tResourceStream.Create(hInstance,'LOGO_GIF','GIF');
or simply use
rc := tResourceStream.Create(hInstance,'LOGO_GIF', RT_RCDATA);
or simply use
rc := tResourceStream.Create(hInstance,'LOGO_GIF', RT__RCDATA);
This. Works like a charm.
D2009 here, too, had the same issue, but was trying to get TStringsList out of the DLL.
Thanks.