'Property Align does not exist' when inheriting from TCustomControl - delphi

I have created a custom control inherited from TCustomControl and published the property Align of TControl. But, when I used this custom control in a C++Builder project, it raised the exception
Project Launcher.exe raised exception class EReadError with message 'Property Align does not exist'.
This is the code for the custom control.
unit GameListCtrl;
interface
uses
SysUtils, Classes, Controls;
type
TGameList = class(TCustomControl)
private
protected
procedure Paint; override;
public
{ Public declarations }
published
property Align default alLeft;
end;
implementation
{ TGameList }
procedure TGameList.Paint;
begin
inherited;
end;
end.

Often this kind of error occurs if the package was not properly rebuilt. Then you need to open the package project that includes the unit "GameListCtrl" an rebuild the package. Make sure to activate the option to let RAD Studio create the C++ Builder files.
If that doesn't help the linker maybe picks a wrong / old DCU or obj file. Search all your drives and delete all GameListCtrl.dcu and GameListCtrl.obj files that you can find. I use UltraSearch from JAM Software to quickly search my local drives, it is much faster than Windows Search as it works directly on the NTFS structures.
You may also try to switch to static linking for your project in the project options.

Related

Can't load package %s error while installing a package

I'm testing on Delphi 2007 and my groupproject is composed by 2 packages.
PackageRun.bpl
It's marked as "runtime only" and contains a unit named "uMyTestRun.pas" in which is defined an empty TFrame descendant:
unit uMyTestRun;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TMyTest = class(TFrame)
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
end.
PackageDes.bpl
It requires PackageRun.bpl, it's marked as "designtime only" and contains a unit named "uMyTestDes.pas" in which I wrote the following code:
unit uMyTestDes;
interface
uses
Classes,
uMyTestRun;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TMyTest]);
end;
end.
Output directories of both packages are in Library paths (Inside there are bpl, dcp and dcu).
Trying to install PackageDes.bpl (Component, Install Packages..., Add...), I'm getting the following error:
Can't load package C:\<...>\PackageDes.bpl. Impossibile trovare il
modulo specificato.
The last part of the message is in my OS's language, in english it should be something like "Can't find specified module". (My OS is Windows 10 Pro 64bit).
PackageDes.bpl is exactly in the same path shown in the error message (C:\<...>\PackageDes.bpl).
After some tests, I found that the error disappear by removing the following line from uMyTestDes.pas unit:
RegisterComponents('MyComponents', [TMyTest]);
Is there something wrong in my code/projects/environment?
Run Process Monitor from http://SysInternals.com and set the filters to intercept only file operations ( toolbar rightmost buttons ) of your Delphi IDE process (check the process name in TaskManager or shortcut properties (it is bds.exe for Delphi XE2), then add the filter similar to Include / Process Name / Ends With / bds.exe ).
Then clear the log in PM, switch to Delphi and try to load the package, then as soon as error pops up switch back to PM and stop capturing events. Try to do it fast as you can, for example do not waste your time closing error box.
Now you would get a trace of file I/O activity of Delphi loading the package of yours (and some other background activity noise - the faster you do the less noise there'd be). In that trace look for all the errors and see where and which package Delphi tries to find.
You can also try Microsoft Dependency Walker or similar tools to se if your Design-Time BPL has all the DLL-dependency tree resolvable. Personally I usually use Unreal/Total commander with FileInfo plugin or ntCore CFF Explorer.
Easy way to solve this issue is to add a post build action to your run time project:
copy "$(OUTPUTDIR)\$(OUTPUTFILENAME)" "$(BDSCOMMONDIR)\Bpl"
The command above copies your run time file to the default IDE Bpl location.
I had a similar issue. In my case I had the same library name in a different Delphi version BPL path. I found out the solution for my issue looking at the comments above, so this is only a reminder for basic things to check:
BPL path have to be included in your OS path variable;
Search for a BPL module with the same name in other OS path before the right one (mutiple Delphi version installations).
Try to change the register procedure to uMyTestRun unit.
unit UMyTestRun;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;
type
TMyTest = class(TFrame)
private
{ Private declarations }
public
{ Public declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TMyTest]);
end;
end.
Now, the package will install correctly.
Regards.

How can I create an event handler for a non published but public event in delphi?

In RAD Studio 10.1 Berlin quite a few things has changed from previous version. In FMX there are a few previously published events that has now been changed to only be public.
I have a Multi Platform Project that uses a TStringGrid component and the OnDblClick event. When opening this project in Studio 10.1 I get warned that the Property OnDblClick does not exist.
The question is now how I can use the no longer published event?
(I must say that It's hard to understand why they haven't set mouse events to Published anymore. As far as I know most regular PCs and OSX machines doesn't have touch. A true Multi Target Project should be able to target these systems without hassle as they did in Studio 10 Seattle)
In case the event handlers already exist (which I imply by the error message), you can assign these handlers to their events in FormCreate.
procedure TForm1.FormCreate;
begin
StringGrid1.OnDblClick := StringGrid1DblClick;
end;
One solution is to make your own component where you extend the FMX.TStringGrid to have published event handlers again.
See here how to create a new FMX component: creating a firemonkey component
Here's the code to re-publish the mouse events.
unit MyStringGrid;
interface
uses FMX.Grids;
type
TMyStringGrid = class(TStringGrid)
published
property OnDblClick;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseEnter;
property OnMouseLeave;
end;
procedure Register;
implementation
uses FMX.Types;
procedure Register;
begin
RegisterComponents('NewPage', [TMyStringGrid]);
end;
initialization
RegisterFmxClasses([TMyStringGrid]);
end.
This has been reported as a bug here.
Looking at the source code in Delphi 10.1 berlin the public OnDblClick event is actually inherited from TControl class.
Similar the OnDblClick event is also inherited from TControl class with the exception that it is made public, like many other events that are inherited from TControl˙ class.
Any way it seems that guys at Embarcadero have been doing some refactoring by cleaning the parent property redeclarations˙(not sure if this is the right term) like:
type
TParentClass = clas(Tobject)
public
property ParentPropery: Integer read GetParentProperty write SetParentProperty;
TExampleClass = class(TParentClass)
public
property ParentPropery;
end;
Redeclaring ParentProperty in the above case is not needed as it will be available in all child classes unless you want to change its visibility from public to published for instance.
If you look at Delphi 10 Seattle source code you see that property OnDblClick is redeclared in several TStringGrid parent classes being published in TCustomScrollBox for the first time.

Don´t save my published property in component frame

I have a Component inherited of the TFrame... But my published properties don't save the values in .dfm... The error occurs when closing Delphi and open again, reloading the project.
This error is: "Error reading MyComponent1.Obs: property Obs does not exists..."
TMyComponent = class(TFrame)
FObs: string;
procedure SetObs(const Value: string);
published
property Obs: string read FObs write SetObs;
end;
procedure register;
implementation
procedure register;
begin
RegisterComponents('My Components', [TMyComponent]);
end;
procedure TMyComponent .SetObs(const Value: string);
begin
if FObs <> Value then
FObs := Value;
end;
I've managed to reproduce the error and then fix it, but can't understand exactly why it happens so, has to do with visual inheritance which works in rather complex way (hope David or Remy could explain what happens here).
First of all, this error indicates that property actually was saved to dfm file. Point is, it's perfectly normal situation when some properties are absent in dfm, it just means that property must have default value (or value which ancestor had), so IDE never raises error because it didn't find some property in file.
Here the opposite happens: property 'obs' was found in dfm, but wasn't found in class itself. The same happened when I created new project and then added frame unit to project itself. It seems, in that case visual inheritance took place instead of normal inheritance, which is: IDE found, that class TMyComponent is described in 'visual' unit belonging to project, found that line:
TMyComponent = class (TFrame)
and made TMyComponent merely alias for TFrame. That's why error occurred: TFrame really doesn't have 'obs' property.
And here is workaround: Don't add this frame to project itself. Instead, add frame from component palette. This way it works as expected. If IDE can't find files of this component, add folder containing them to search path.

Component property not considered on runtime in Delphi XE2

I am developing a component but I can't make it consider a property set at design-time.
The following is an excerpt of the component:
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor:TCustomRichView;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
protected
function GetRVEditor:TCustomRichView;
procedure SetRVEditor(Editor:TCustomRichView);
published
property RVEditor:TCustomRichView read GetRVEditor write SetRVEditor;
end;
...
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
SetRVEditor(FRVEditor);
...
end;
function TRVEditFrame.GetRVEditor:TCustomRichView;
begin
Result:=FRVEditor;
end;
procedure TRVEditFrame.SetRVEditor(Editor:TCustomRichView);
begin
if Assigned(Editor) then begin
FRVEditor:=Editor;
end;
end;
I can register the component, place it in ther form and set FRVEditor on design-time.
Problem is when I run the application the code inside SetRVEditor() is not executed because Editor=nil.
If I was able to set FRVEditor on design-time, how come that it is=nil on run time ? How can I fix this ?
I add here my further comments because the explanation is too long
#Kenneth, thank you for your reply
TCustomRichView is part of a third part component set that manages
hypertext documents and has 4 more specialized descendents and you
are right, TCustomRichView shouldn't be used in a real application.
TRVEditFrame is the component I am developing.
The idea behind my component is to create one single frame (hence the choice of the component TFrame) with menus, shortcuts, popup menus etc to manage each of the 4 TCustomRichView descendents.
This is exactly the reason why I use TCustomRichView: I can "slot" any of the 4 descendents into my component-frame. This is the same principle of TDatasource that can be connected with TTAble and TQuery (they have the same ancestor).
I suppose the reason why the VCL doesn't link RVEditor to the TCustomRichView descendent I set on design-time is because TFrame has no OnCreate event, like TForm for instance.
So far I managed to solve the issue by calling TRVEditFrame.SetRVEditor manually in the TForm.OnCreate that hosts TRVEditFrame but I was wondering if there are better methods to do so and that is why I have asked advice here.
I know you can create a OnCreate event for TFrames as well, maybe I can place TRVEditFrame.SetRVEditor in there but, again, I was wondering if there was a better method.
Regarding the last part of your comment, I am aware of the register procedure but take into account the component is under development. When I develope components I never install them in the IDE because I prefer to keep the test stuff outside the "official" one.
I use this method and as soon as the component is ready then I register it with the procedure you mention. If I want to implement other features to the same component I can work on the test ones and keep on using the "official" one I have in the IDE at the same time.
My suggestion is to change the design in order to get the frame component referencing the custom editor by using the Notification method, instead of manually setting a property. So, change your frame class to
TRVEditFrame = class(TFrame)
...
private
{ Private declarations }
FRVEditor: TCustomRichView;
protected
procedure Notification(aComponent: TComponent; aOperation: TOperation); override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
public
property RVEditor:TCustomRichView read FRVEditor;
end;
The implementation of the Notification method does the magic of connecting/disconnecting the frame to the custom editor
procedure TRVEditFrame.Notification(aComponent: TComponent;
aOperation: TOperation);
begin
inherited;
if aComponent is TCustomRichView then
if aOperation=opRemove then begin
if aComponent=FRVEditor then
FRVEditor := nil;
end else
FRVEditor := TCustomRichView(aComponent);
end;
I don´t know if you need any special handling when an editor is set/reset to the frame, so this code does nothing in special, just assigns the component or nil to the FRVEditor data member at the proper moment.
You should create child components.
constructor TRVEditFrame.Create(AOwner: TComponent);
begin
inherited; // Create(AOwner);
FRVEditor := TRichView.Create(self);
end;
DFM streaming engine may load properties of already created class, but it cannot create the class for you for two reasons:
1) DFM can not know of any special tuning done on created component, like what should be constructor parameters (if any), what constructor to use (of many), which niotifications and event handlers to attach and so on.
2) DFM can not know which type the property should be. For example i published TStrings oroperty - object of which class should be created to feel in ? TStrings? TStringList? TRichEditStringList? THashedStringList ? they all were inherited from TStrings and thus any of them is fine choice for DFM - but not for the ocmponent you write.
Thus DFM streaming subsystem is responsible for saving and loading properties of objects, but creating those object is purely your responsibility.
I believe you also may cut the corners by learning how IDE Designer creates your forms and making RVEdit a variable rather than property:
TRVEditFrame = class(TFrame)
...
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
var RVEditor:TCustomRichView; // just like users' forms are created by IDE
end;
Then hopefully TFrame constructor would create the content for this variable for you. But this design is fragile because any outer code would be able by any stupid mistake to make something like MyEditFrame.RVEditor := ... and cause a memory leak and unexpected failures of all the attached connections between the editor and the frame.

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