How to retrieve a resource from within a tWebModule - delphi

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.

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 Package Resources - EResNotFound

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

How to resolve "Sevenzip: Error result (00000001) Incorrect function" using JCLcompression unit

I'm using JCL version 2.4.1.4571 with Delphi XE3 and have had no luck decompressing archives. I've downloaded the dll's from JEDI's site and also tried using the 7z.dll (32bit) from 7-zip.org but either way I get the same error when I try to "ExtractAll"
See function below:
function TForm1.Decompress(FileName, DestDir: String): Boolean;
var
archiveclass: TJclDecompressArchiveClass;
Myarchive: TJclDecompressArchive;
begin
Decompress := False;
// Filename = name.7z or name.zip (simple test zips nothing fancy)
// DestDir = fully qualified path to an existing directory
archiveclass := GetArchiveFormats.FindDecompressFormat(FileName);
Try
if Assigned(archiveclass) then
Begin
Myarchive := archiveclass.Create(FileName);
if (Myarchive is TJclSevenZipDecompressArchive) then
Begin
try
Myarchive.ListFiles; { Fails without doing this first }
{ ExtractAll (AutocreateSubDir) must be set true if arc has directories or it will crash }
Myarchive.ExtractAll(DestDir, True);
Decompress := True;
except on E: EJclCompressionError do
Begin
ShowMessage(e.Message);
End;
end;
End
Else
ShowMessage('Not supported by 7z.dll');
End;
Finally
MyArchive.Free;
End;
end;
When I execute the MyArchive.ExtractAll line, I get an exception Sevenzip: Error result (00000001) Incorrect function. I based my code on code from others here on StackOverflow. Am I missing something I need to do first or is this a bug? I've replaced the extract line with MyArchive.ListFiles and get the same error (I saw that in an example here; however, I've yet to divine the purpose of ListFiles.
Compiling to 32bit target.
Edit: Created a series of different types of archives using 7-zip and tried to decompress each with my program. The first thing I discovered is that if the archive contains directories of files, ExtractAll will crash if you don't set the second parameter to True. I then tested archives with different compression methods.
.7z archive using LZMA2 Ultra compression gives the Hresult = 1 error
.zip archive using LZMA Ultra compression gives the Hresult = 1 error
.zip archives using flavors of Deflate or deflate64 all work fine.
It appears that the library doesn't handle LZMA compression at all. Since it makes no sense that the 7z.dll can't handle it, I'm guessing the problem is with the JEDI JCL code. I need to be able to compress/decompress .7z and .zip's using LZMA with this library or I could have just used the built in zip stuff to begin with. Any further suggestions would be appreciated.
I think that is a JCL implementation bug. 7z use COM interfaces, and returns HRESULT codes. JCL attemptes to translate them into error messages using SysErrorMessase(), but AFAIK it works only for Win32 error codes, not HRESULT. That way the return code S_FALSE gets mapped to ERROR_INVALID_FUNCTION (see http://issuetracker.delphi-jedi.org/view.php?id=6348).
My guess is that a 7z call is returning S_FALSE for some reason, because it encounters some issue when decompressing - and the error is deceiving.
See also Error Handling in COM.
Further Googling on this problem turned up http://sourceforge.net/p/jcl/mailman/message/21371812/
It appears "FindDecompressFormat doesn't find archive format if file name is not in lower case."
I tried changing the string I was passing to lowercase and I successfully decompressed an LZMA archive.
archiveclass := GetArchiveFormats.FindDecompressFormat(lowercase(FileName));
JEDI JCL would be a cool library if it had any documentation whatsoever - sad.
If you call TJclZipDecompressArchive with a filename that does not exist you will get the same not very helpful error message on the ListFiles function.
Moral of the story check if the file exists yourself before calling the api.

Delphi Firemonkey - loading style at runtime

I have loaded a couple of the sample styles from ......\RAD Studio\9.0\Styles as resources into my project and am 'simply' trying to load one of them at runtime.
I'm using the following code to try and do this:
var
vResourceStream : TResourceStream;
begin
vResourceStream := TResourceStream.Create( HInstance, 'DARKSTYLE', RT_RCDATA );
try
StyleBook1.LoadFromStream( vResourceStream );
finally
vResourceStream.Free;
end;
It compiles ok but when I run it I get a bunch of errors, the first being 'Property Align does not exist' then 'Error reading TStyleBook.Align: Property Align does not exist' and it seems to do this for a bunch more atributes, Height etc.
Can someone give me some pointers as to how to solve it please?
Not that I know a bit about FMX, but AFAIU the .style files are resource definition files. Instead of reading the stylebook object from the stream, you should read its resource:
StyleBook1.Resource.LoadFromStream( vResourceStream );
For reference, there are LoadFromStream and LoadFromFile methods at TStyleBook, but there is also TStyleStreaming class with an extra LoadFromResource utility method (apart from its own LoadFromStream and LoadFromFile). Probably they should add a LoadFromStream to TStyleBook class too (guess it would call into the respective TStyleStreaming one)

Delphi - Form in DLL - Hints not showing

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.

Resources