Is there a conditional define for library projects in Delphi? - delphi

I have a utility unit with code shared between a few applications and DLLs. I'd like to selectively compile portions of this unit based upon the current project type: Application, Package, or Library. I couldn't find any conditional definitions for a library or package in the System unit, and Google didn't find anything relevant. So, are there any conditional defines such as IsLibrary or IsPackage set by the compiler? I'm aware of the IsLibrary variable, by the way.

There is no such pre-defined conditional, and there could not be such a conditional. That's because at compilation time it is impossible to know whether the unit will, ultimately, be linked into an executable, a library or a package.
In fact, the same compiled unit could be linked into any or all of the above project types. And indeed you can see this yourself when you link the RTL into your projects. You link the same System unit, the same compiled .dcu file, into all your projects, irrespective of the project type.

IMHO there is absolutly no need for such conditionals because of existing conventions.
Compiling an Application or Library (the same on this compiling aspect) or a Package differs like so:
Application/Library will compile only the used parts from the unit
Package will compile all parts from the unit referenced by the interface part of the unit
Example Unit
unit foo;
interface
procedure foo1;
procedure foo2;
implementation
procedure foo3;
begin
// used by foo2, compile depends on foo2 compilation
end;
procedure foo4;
begin
// will never be compiled, because is never used
end;
procedure foo1;
begin
// Package: will always be compiled
// Application/Library: will be compiled if used
end;
procedure foo2;
begin
// Package: will always be compiled
// Application/Library: will be compiled if used
foo3;
end;
end.
That is also a reason, why using packages may result in bigger exe files, because it can contain unused code parts from precompiled dcu files.

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);

The initialization part is not called

I'm maintaining the VirtualTreeView component for Delphi and C++Builder. With Delphi everything is okay but when I compile the packages with the C++Builder the code in the initialization part in the Delphi units is not called. Any ideas?
When a Delphi unit's initialization/finalization sections are not being called in a C++Builder project, it usually means the Delphi unit is not being linked into the final executable, typically because the C++ code is not directly referencing any code in the unit, so it gets optimized out. C++Builder is a bit more aggressive about removing unused code than Delphi is. In Delphi, simply adding a unit to a uses clause forces that unit to be linked to. That is not the case in C++. #includeing a Delphi unit's .hpp file in C++ code is not enough to guarantee the unit is linked to, if the C++ code does not use anything from the .hpp file.
Indy ran into this problem in several of its units, most notably IdAllAuthentications, IdAllFTPListParsers, and IdAllHeaderCoders. These units all contain only initialization/finalization code, no interface code, so their generated .hpp files were basically empty. To force linkage, I had to add {$HPPEMIT} statements to the interface section to output #pragma link statements in the generated .hpp files. For example:
unit IdAllAuthentications;
interface
{
Note that this unit is simply for listing ALL Authentications in Indy.
The user could then add this unit to a uses clause in their program and
have all Authentications linked into their program.
ABSOLUTELY NO CODE is permitted in this unit.
}
{$I IdCompilerDefines.inc}
// RLebeau 2/14/09: this forces C++Builder to link to this unit so
// the units can register themselves correctly at program startup...
{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT}
{$HPPEMIT LINKUNIT}
{$ELSE}
{$HPPEMIT '#pragma link "IdAllAuthentications"'}
{$ENDIF}
implementation
// uses units that self-register in their initialization sections ...
end.
{$HPPEMIT LINKUNIT} was introduced in XE5 Update 2, to help with linking units that use unit-scope names:
New: You can now use HPPEMIT Delphi compiler directives for linking and generating C++ namespace declarations.
...
{$HPPEMIT LINKUNIT} replaces #pragma link for the iOS device target platform.
For more information, see HPPEMIT.
For C++ applications, {$HPPEMIT LINKUNIT} replaces #pragma link on mobile platforms.
The Delphi run time has units that must be linked in order to enable some functionality. In C++, auto-linking was previously achieved using the following directive:
{$HPPEMIT '#pragma link "<unitname>"'}
Now you should use the following directive instead:
{$HPPEMIT LINKUNIT}
LINKUNIT generates a #pragma link statement that references the calling unit using the correct decorated/namespaced unit name.

SetEnvironmentVariable not working

I need to set two environmental variables (when my application runs) for included 3rd party libraries.
The problem is that 'that way' it is not working, however
when I run console application, set these two variables and then run the application, everything is okay ...
how to configure these two vars correctly?
i use the procedure:
function SetEnvVarValue(const VarName,
VarValue: string): Integer;
begin
// Simply call API function
if SetEnvironmentVariable(PChar(VarName),
PChar(VarValue)) then
Result := 0
else
Result := GetLastError;
end;
It returns 0
Maybe the thing is, that i have the libraries being loaded on application startup.
When my application stars I set then the variables and I do it too late ...?
Further information
I have included two units in dpr:
ImageMagick in 'C:\Program Files (x86)\Borland\Delphi7\Lib\Magick\magick\ImageMagick.pas',
magick_wand in 'C:\Program Files (x86)\Borland\Delphi7\Lib\Magick\wand\magick_wand.pas';
And the Unit:
unit DoItFirst;
interface
uses
Windows, Sysutils;
var
s: string;
error: Integer;
function _putenv_s(const lpName, lpValue: PChar): BOOL; cdecl; external 'msvcrt.dll';
implementation
function GetEnvVarValue(const VarName: string): string;
var
BufSize: Integer; // buffer size required for value
begin
// Get required buffer size (inc. terminal #0)
BufSize := GetEnvironmentVariable(PChar(VarName), nil, 0);
if BufSize > 0 then
begin
// Read env var value into result string
SetLength(Result, BufSize - 1);
GetEnvironmentVariable(PChar(VarName),
PChar(Result), BufSize);
end
else
// No such environment variable
Result := '';
end;
initialization
_putenv_s(PChar('DYLD_LIBRARY_PATH'), PChar('g:\_projekty\ZBar Test\'));
_putenv_s(PChar('MAGICK_CODER_MODULE_PATH'), PChar('g:\_projekty\ZBar Test\modules\coders\'));
s := GetEnvVarValue('DYLD_LIBRARY_PATH');
s := GetEnvVarValue('MAGICK_CODER_MODULE_PATH');
end.
This unit is at the beginning of dpr file.
From what I can discern from your update, the third party library in question is ImageMagick. And the .pas wrappers for that library use load-time linking to the ImageMagick DLL.
When you modify the environment variables from a command interpreter, and then start your process, the ImageMagick DLL can see those environment variables. When you modify the environment variables in your process startup code, the the ImageMagick DLL cannot see those environment variables. Presumably because it has read the variables before your code modifies them.
What I would conclude from the above is that the ImageMagick DLL is reading the environment variables in its initialization.
Because you are using load-time linking, the DLL initialization happens before you have any opportunity to execute your code. I can think of the following ways to work around the issue:
Switch from load-time linking to run-time linking for the ImageMagick DLL. This will require you to modify the ImageMagick wrappers that you use. If you are unfamiliar with how to do this, then you might consult the JEDI source code for inspiration. Note that if you are using a modern Delphi, then you can simply modify the wrapper DLL to delay load the ImageMagick DLL. Add the delayed directive to the function declarations. This results in run time linking.
Move some of your code into a DLL so that you can load it with run-time linking. I'm imagining that you move any code that uses the ImageMagick wrapper into a DLL. That would allow you to keep on using the same wrapper, but still have the ImageMagick DLL loaded at process run-time rather than process load-time. You might even move your entire code into a DLL and then have an executable that did nothing more than load that DLL, and then call a single exported main function.
Use a separate launcher process. The launcher process prepares the environment, and then launches the real application.
Of these options, the first is by far the most preferable, in my opinion.
You may be changing the environment variables after they've been read by the third party libraries.
First up, you should set the environment variable as the first thing tour program does.
Even then, it may be that the third-party libraries could be reading that information in initialisation functions, possibly before your code even starts running.
If that's the case, I think the order of initialisation is deterministic (see here), depending on the order of your units in the dpr (project file).
If you want those variables set before the third-party libs look at them, you can create a DoMeFirst unit and do it in that unit's init code. Then make sure that's the first one in the project file.
If that doesn't work, another option may be to write a program which changes the environment and then calls your current program as a child.

Add resource file by code with Delphi

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.

BPL File needs Run-Time Packages !

I have created a Package and i want to use the BPL File of my Package ...
My Package have VCL.dcp and RTL.dcp as Required libraries , i load this Package in my application without any errors but when i want to unload it , an Access Violation shown !
If i Build my Application with Run-Time Packages ( "vcl" and "rtl" ) , Access Violation not shown !
What is this mean ?! My Application need VCL and RTL Libraries to Load BPLs ?! I want to Load my Package like a DLL File , is there any solution ?
I`m using Delphi 2010
thanks a lot ...
Your BPL requires the RTL and VCL packages. If your Application doesn't require them, then that means the RTL and VCL units are compiled into your EXE file. When your EXE loads your BPL, you now have two copies of the RTL and VCL units — one set of copies comes from within the EXE, and the second copies come from the RTL and VCL packages that your package implicitly causes to be loaded.
Delphi isn't intended to accommodate that situation. It's possible that you have memory that was allocated by one RTL and attempted to get freed by the other RTL. Or there might be function pointers in the EXE that refer to functions that were in the VCL package.
I see three options for you:
Compile your EXE to use packages. Specifically, it should require the same RTL and VCL packages that your BPL requires.
Make your BPL not require any other packages. If it doesn't require RTL and VCL, then any RTL and VCL units that your package uses will get compiled into your BPL. You'll end up with two separate copies again, but it should work better since neither copy will think it's supposed to be shared.
Load your package like a real DLL instead of like a package. You said you wanted to use it like a DLL, so do that. Use LoadLibrary, and then use GetProcAddress to get whatever functions you want to call. If you go this route, it's probably better to not make your code be a package at all. Make it a DLL, and export functions that only use parameter types that you'd expect to find in other DLLs, like integers, character pointers, and record pointers, not strings or objects.
It should be clear that the first option is the easiest. The second could probably work, and it sounds like that's the way you'd prefer, but I expect it will generate more headaches before it finally works. The third option is best if you'll ever have to use other development environments during the lifetime of this project.
What have your package inside?
What work do you do with it?
How do you charge and discharge? What's in it?
What do you do with the package before unload it?
When you Unload it, all the objects/forms/components/... that yo've used is released?
ADDED: I Think that you are using anything of the package when you try to Onload. This is the reason of AV.
In an EXE compiled without runtime package, I load the package:
OutputDebugString(PChar('Loading the package'));
hand := LoadPackage('r:\rrrrrrr\Package1.bpl');
I Unload the package with this code:
OutputDebugString(PChar('Ready to Unload Package'));
UnloadPackage(hand);
OutputDebugString(PChar('Unloaded'));
The package has a unit with a form (form1) and a unit Init.pas, for initialization like this:
unit Init;
interface
// prototipos
procedure Start_P;
procedure Finish_P;
implementation
uses
Unit1, Windows;
procedure Finish_P();
begin
OutputDebugString(PChar('Finish_P form free'));
Form1.Free;
end;
procedure Start_P();
begin
OutputDebugString(PChar('Start_P Creating form'));
Form1 := TForm1.Create(nil);
Form1.Show;
end;
Initialization;
Start_P();
Finalization;
Finish_P();
end.
The package is loaded and the form visualized without problems, and the same with the operation of Close and Unload. The project is compiled with "Build with rutime packages" unchecked.
Can you post any code.
The result of OutputDebugString is this (no AV error):
[2644] Loading the package
[2644] Start_P Creating form
[2644] Ready to Unload Package
[2644] Finish_P form free
[2644] Unloaded
Regards.
Thanks for your helps ...
I put an example of my package and my Application here to Find what is the problem !
We have a package without requiring to Run-Time Packages like VCL and RTL , in other words i removed all libraries from the Requires section in my package :
my package contains a form with code below :
unit MyUnit;
interface
uses
Windows, Forms, StdCtrls, Buttons, Controls, Classes, Dialogs;
type
TMyForm = class(TForm)
MyLabel: TLabel;
MyEdit: TEdit;
PostBtn: TBitBtn;
procedure PostBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MyForm: TMyForm;
implementation
{$R *.dfm}
function ShowForm(FCaption, LCaption : String) : String;
var
F : TMyForm;
begin
F := TMyForm.Create(nil);
try
F.Caption := FCaption;
F.MyLabel.Caption := LCaption;
F.ShowModal;
finally
Result := F.MyEdit.Text;
F.Free;
end;
end;
procedure TMyForm.PostBtnClick(Sender: TObject);
begin
if MyEdit.Text <> '' then
Close
else
ShowMessage('Please Enter Value !');
end;
exports
ShowForm;
end.
I Load this Package and Call ShowForm Function and then Unload package :
var
ShowF : function(FCaption, LCaption : String) : String;
MyPkg : HMODULE;
FC, LC : String;
begin
MyPkg := LoadPackage(ExtractFilePath(Application.ExeName)+'MyPackage.bpl');
FC := 'Enter Value ... ';
LC := 'Value : ';
if MyPkg <> 0 then
begin
try
#ShowF := GetProcAddress(MyPkg, 'ShowForm');
if Assigned(ShowF) then
Edit1.Text := ShowF(FC, LC)
else
ShowMessage('Function not found !');
finally
UnloadPackage(MyPkg);
end;
end;
end;
After the Procedure above done , the AV Shows !
#Neftalí : If I just do loading and unloading the Package , no AV Shows , but i think that is because i don`t call some routines or objects or ... that they need VCL or RTL Libraries , if i use objects and functions and ... of this package , after using them i will get an AV ...
is it true ?!
If I Build my application with Run-Time package ( VCL and RTL ) no AV will shown !
I`m confusing !! , I want to use an BPL package without any Run-Time package needed ...
thanks a lot ...
Yes, if you want to use runtime packages in your application you have to build it with runtime packages, and then it requires them (links statically with them).
The solution to your problem depends on what the problem actually is (which is unclear at the moment).
Ohhhhh, great oversight/neglect (mine).
With the code that you have posted, made a simple change a test it (use PChar).
function ShowForm(FCaption, LCaption : String) : PChar;
...
Result := PChar(F.MyEdit.Text);
...
The same when you define the sitaxis of the function:
ShowF : function(FCaption, LCaption : String):PChar;
Test it and say the result.
Regards.

Resources