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.
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);
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.
Need some help in creating function which can create folders recursively with giving path:
C:\TestFolder\Another\AndAnother
Delphi function MkDir returning IOerror = 3.
MkDir('C:\TestFolder\Another\AndAnother');
Use
ForceDirectories('C:\TestFolder\Another\AndAnother');
(This is a standard RTL function, found in SysUtils.pas. Hence you do not need to create your own function, even though that wouldn't have been difficult.)
SysUtils is obsolete now and ForceDirectories is not UNC aware!
There is a new library in since Delphi XE7 (or even earlyer?) called IOUtils.
IOUtils is cross-platform compatible and UNC aware.
function ForceDirectories(FullPath: string): Boolean; // Works with UNC paths
begin
TDirectory.CreateDirectory(FullPath);
Result:= DirectoryExists(FullPath);
end;
Note: The function is from Delphi LightSaber library. There are several other similar I/O functions there (like ListFilesOf(Folder)).
i want to embedded a file (any kind of type) to my exe application and be able to extract in the remote to use it, i know how do it by embedded into resource,but i don't want to place the files in the app directory, i want to store all files (like .rec) into my exe, in c# it is possible to store as text file and then read it by FileStream but in Delphi the resource files is separate from the exe file.
is there any solution to do this ? Thanks a lot!
You should make an .rc file and add that to your project. The content of the RC file is like:
FIXED48 IMAGE ..\Resources\Fixed48x48.png
MENU16 IMAGE ..\Resources\Menu16x16.png
TICK SOUND ..\Resources\Tick.wav
PING SOUND ..\Resources\Ping.wav
Now after you do a build you can load one of these fikles using a TResourceStream:
procedure TdmReportGeneral.InsertLogo(Memo: TStringList; View: TfrView);
var
S: TResourceStream;
begin
if (View is TfrPictureView) and (View.Name = 'Logo') then begin
S := TResourceStream.Create( 0, 'FIXED48', 'IMAGE' );
try
// do something useful... TfrPictureView(View).Picture.MetaFile.LoadFromStream( S );
finally
S.Free();
end;
end;
end;
You should be able to get the Delphi compiler to link your resource into your EXE by adding it as a {$R myresource.res} pragma in a unit in your project. You can then get a handle to it via a call to FindResource when you need to read it.
This article takes you through the appropriate steps.
DelphiDabbler has a great article on this very topic. They even include 2 example projects for download that show how to embed a file as a resource, and how to read it back.
You can download a worked example that
demonstrates what we've described here
– it uses the above code. The zip file
contains a pair of projects. The first
is a program that embeds a supplied
rich text file in a resource file. The
second program includes the resource
file and displays the rich text in a
rich edit component as above.
Running a project and was getting an error saying
"Not enough actual parameters"
The error is in "C:\Program Files\PBear\HTMLComponents\package\GDIPL2A.pas".
The mistake pointed three times to "inherited Create;" lines 260,270 and 278 . In the file "GDIPL2A.pas".the Code is:-
var
err: integer;
begin
inherited Create;
err := GdipCreateBitmapFromScan0(W, H, 0, PixelFormat32bppARGB, nil, fHandle);
if err <> 0 then
raise EGDIPlus.Create('Can''t create bitmap');
end;
I was wondering why it would show an error in "THTML" files, WHICH ARE NOTHING BUT FILES FROM THE INSTALLATION of THTML.I did not even touch THTML files.
Kindly help
Thanks and Regards
Vas
A "Not enough actual parameters" error on "inherited Create;" means that you're trying to call an inherited constructor but it not supplying any parameters. Check the class you're inheriting from and you'll see a Create that requires some parameters. (If the base class doesn't have one, check its parent, and its parent and so on. You'll find one eventually.) It should be pretty obvious once you find the constructor declaration what you need to pass to it.
Your call needs to look something like:
inherited Create(param1, param2);
I have THTML, and it indeed includes GDIPL2A.pas, which is a wrapper around GDIPlus; apparently THTML uses GDIPlus to display embedded images or something.
A quick look at the declaration of TGPImage and TGpBitmap shows the constructor declarations of each:
// TGpImage
public
constructor Create(FileName: string; TmpFile: boolean = False); overload;
constructor Create(IStr: IStream); overload;
// TGpBitmap
public
constructor Create(W, H: Integer); overload;
constructor Create(IStr: IStream); overload;
You'll see that all of the constructors takes at least one or two parameters; your call to inherited Create passes none. However, since the call to inherited is in another constructor it should work (and indeed does on my machine; I just tried rebuilding one of the demos from THTML and it recompiled GDIPL2A.pas fine), You've got something else going on, like a different version of GDIPL2A in your path that the compiler is using instead of the one you're seeing in the IDE's editor.
As to your question, I answered it in the first paragraph above. It's reporting the error in THTML because that's the copy of GDIPL2A that the compiler is using, which may not be the one your code is expecting it to use.
You can fix that by either:
Reordering units in the uses clause of your code so that all calls that cause GDIPL2A to compile are using the same one;
Copy the GDIPL2A your code thinks it's using into your project's source folder, so it will be compiled from there. This will probably break THTML if you're using it in that same project, so be ready for that;
Find and resolve the competing copies of GDIPL2A so that there's only one copy available on the compiler's search path;
Remove the THTML path from your project's search and library paths, if you're not using it in your problem project. You can also, using Project|Options|Packages, prevent THTML from even being loaded when you open the project if you'd like, to make your project load faster.
I don't know if anybody read this anymore but my problem occurred during installing of ThtmlViewer. My solution was to edit the GDIPL2A.pas file. I just added an emty string and a false boolean, so the 3 create statements looked like this:
inherited Create('', False);
And then everything worked fine (at least so far)
(I have an old Delphi 4 on an old Windows Xp on an old PC, not connected to internet)
Kindly
Erling