Get class by its name in Delphi - class

I would like to write a function that accepts a classname and results the corresponding TClass. I've noticed that, System.Classes.GetClass function doesn't works if the classname isn't registered.
Example:
if(GetClass('TButton') = nil)
then ShowMessage('TButton not found!')
else ShowMessage('TButton found!');
The previous code always shows:
TButton not found!
Is there something missing?

You can get unregistered class used in Delphi application via extended RTTI. But you have to use fully qualified class name to find the class. TButton will not be enough, you have to search for Vcl.StdCtrls.TButton
uses
System.Classes,
System.RTTI;
var
c: TClass;
ctx: TRttiContext;
typ: TRttiType;
begin
ctx := TRttiContext.Create;
typ := ctx.FindType('Vcl.StdCtrls.TButton');
if (typ <> nil) and (typ.IsInstance) then c := typ.AsInstance.MetaClassType;
ctx.Free;
end;
Registering class ensures that class will be compiled into Delphi application. If class is not used anywhere in code and is not registered, it will not be present in application and extended RTTI will be of any use in that case.
Additional function that will return any class (registered or unregistered) without using fully qualified class name:
uses
System.StrUtils,
System.Classes,
System.RTTI;
function FindAnyClass(const Name: string): TClass;
var
ctx: TRttiContext;
typ: TRttiType;
list: TArray<TRttiType>;
begin
Result := nil;
ctx := TRttiContext.Create;
list := ctx.GetTypes;
for typ in list do
begin
if typ.IsInstance and (EndsText(Name, typ.Name)) then
begin
Result := typ.AsInstance.MetaClassType;
break;
end;
end;
ctx.Free;
end;

Related

Add a form before Mainform

I would like to program a small advertising form, which let's me easily implement it to any of my other projects.
The ad form should appear before my real project/app starts.
Some requirements have to be met:
Easy to implement to any project without any hassle - add form/unit and almost everything takes care of the rest.
My app's MainForm (Application.MainForm) shall be created during runtime AFTER my ad form has been successfully closed (or requirements have been met)
Adding only 1 unit/form should be enough to implement
My progress so far is:
I create an empty main form which is hidden (Application.MainForm)
Then I create a modalform, which is the actual Ad-Form - on the right modalresult, free my welcomescreen and proceed to the "main app"
I need to remove all auto-create forms from my project
I open a procedure in my project's source file with some parameters, including the app's main form (see source)
Unfortunately I have to add all units/forms to the project instead of just one (recursive path problem?)
That's what I have so far:
Project Source:
program MyTestProgram;
uses
Vcl.Forms,
Windows,
uMainWindow in 'uMainWindow.pas' {Form1},
uEmptyForm in '..\AdProject\uEmptyForm.pas' {AdEmptyMainForm},
uWelcomeScreen in '..\AdProject\uWelcomeScreen.pas' {WelcomeScreen}; // shouldn't be here
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Run;
LoadAdWindow('Title of my app', uMainWindow.TForm1, uMainWindow.Form1);
end.
EmptyForm Unit:
unit uEmptyForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TAdEmptyMainForm = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
AdEmptyMainForm: TAdEmptyMainForm;
procedure LoadAdWindow (Appname: string; InstanceClass: TComponentClass; var Reference);
implementation
{$R *.dfm}
uses
uWelcomeScreen; // Has to be added to the project, otherwise it doesn't detect the unit in the same path as this unit
procedure LoadAdWindow (Appname: string; InstanceClass: TComponentClass; var Reference);
begin
Application.MainFormOnTaskbar := True;
Application.ShowMainForm := False;
Application.Title := Appname;
Application.CreateForm(TAdEmptyMainForm, AdEmptyMainForm);
with uWelcomeScreen.TWelcomeScreen.Create(Application.MainForm) do
begin
Caption := Appname;
if ShowModal <> 1337 then ExitProcess(0);
Free;
end;
Application.CreateForm(InstanceClass, Reference);
end;
end.
After the welcome screen succeeds, the application closes.
Is this even the right way to it?
Any help is appreciated!
The Application.MainForm is established by the first call to Application.CreateForm() for a TForm-derived class. Application.Run() exits immediately if Application.MainForm is not assigned.
To do what you are attempting, you should do it more like the following instead. You don't need a blank MainForm, simply create and show the ad window before creating the real MainForm. Only the project main source needs to be changed:
program MyTestProgram;
uses
Vcl.Forms,
Windows,
uMainWindow in 'uMainWindow.pas' {Form1},
uWelcomeScreen in '..\AdProject\uWelcomeScreen.pas' {WelcomeScreen};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.Title := 'Title of my app';
with TWelcomeScreen.Create(Application) do
try
Caption := Application.Title;
if ShowModal <> 1337 then
Exit;
finally
Free;
end;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
You can easily create and show the Ad Form and after it create and show the main form, like this :
var
FAdFrm : TAdFrm;
begin
FAdFrm := TAdFrm.Create(nil);
if FAdFrm.ShowModal = mrOK then
begin
FAdFrm.Free;
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainFrm, MainFrm);
Application.Run;
end
else
FAdFrm.Free;
Be careful that the Main Form of your project is the first form that created with Application.CreateForm method
An alternative is to create the advertising form on MainForm's create event. I do this on my own projects.
procedure TMainForm.FormCreate(Sender: TObject);
begin
AdvForm := TAdvForm.Create(Self);
Try
AdvForm.ShowModal;
finally
AdvForm.Free;
end;
end;

TObjectList<> Get item error

I'm trying to create TObjectList class in Delphi XE8, but i get errors when i try to get the value.
compiler error message: "[dcc32 Error] : can't access to private symbol {System.Generics.Collections}TList.GetItem"
Here is my code:
unit Unit2;
interface
uses
Classes, System.SysUtils, System.Types, REST.Types, System.JSON, Data.Bind.Components,
System.RegularExpressions, System.Variants,
Generics.Collections;
type
TTruc = class
public
libelle : string;
constructor Create(pLibelle : string);
end;
TListeDeTrucs = class(TObjectList<TTruc>)
private
function GetItem(Index: Integer): TTruc;
procedure SetItem(Index: Integer; const Value: TTruc);
public
function Add(AObject: TTruc): Integer;
procedure Insert(Index: Integer; AObject: TTruc);
procedure Delete(Index: Integer);
property Items[Index: Integer]: TTruc read GetItem write SetItem; default;
end;
implementation
{ TTruc }
constructor TTruc.Create(pLibelle: string);
begin
libelle := pLibelle;
end;
{ TListeDeTrucs }
function TListeDeTrucs.Add(AObject: TTruc): Integer;
begin
result := inherited Add(AObject);
end;
procedure TListeDeTrucs.Insert(Index: Integer; AObject: TTruc);
begin
inherited Insert(index, AObject);
end;
procedure TListeDeTrucs.Delete(Index: Integer);
begin
inherited delete(index);
end;
function TListeDeTrucs.GetItem(Index: Integer): TTruc;
begin
result := inherited GetItem(index);
end;
procedure TListeDeTrucs.SetItem(Index: Integer; const Value: TTruc);
begin
inherited setItem(index, value);
end;
end.
the testing code is :
procedure TForm1.Button1Click(Sender: TObject);
var
l : TListeDeTrucs;
i : integer;
Obj : TTruc;
begin
l := TListeDeTrucs.Create(true);
l.Add(TTruc.Create('one'));
l.Add(TTruc.Create('two'));
Obj := TTruc.Create('three');
l.Add(Obj);
for i := 0 to l.count - 1 do
begin
showMessage(l[i].libelle);
end;
L.Delete(0);
l.extract(Obj);
l.Free;
end;
How can i make it work ?
Well, GetItem, and indeed SetItem are private. Your code cannot see them. Private members can be seen only in the unit in which they are declared. You need to use members that are at least protected.
This compiles:
function TListeDeTrucs.GetItem(Index: Integer): TTruc;
begin
Result := inherited Items[Index];
end;
procedure TListeDeTrucs.SetItem(Index: Integer; const Value: TTruc);
begin
inherited Items[Index] := Value;
end;
In this case your class is a little pointless because none of the methods in your class vary behaviour from the base class. But peut-ĂȘtre your real class does more.

Delphi: How to allow setting a TClass-property of a TCollectionItem at design time

I'm developing a component that works on several classes.
In order to allow adding the list of managed classes, I've written a TCollection's inherited class in which each item (inherited from TCollectionItem) defines a published "TargetClassName" property.
The "TargetClassName" property's setter function, calls the following function in order to find the corrisponding TClass:
function FindAnyClass(const Name: string): TClass;
var
ctx: TRttiContext;
typ: TRttiType;
list: TArray<TRttiType>;
begin
Result := nil;
ctx := TRttiContext.Create;
list := ctx.GetTypes;
for typ in list do
begin
if typ.IsInstance and (EndsText(Name, typ.Name)) then
begin
Result := typ.AsInstance.MetaClassType;
break;
end;
end;
ctx.Free;
end;
(Thanks to Dalija Prasnikar for writing the function Get class by its name in Delphi).
Now, I'm wondering if there's a better way to allow adding classes to a TCollectionItem at design time.. What do you think about it?
Hope to read interesting solutions!
Thanks to all.
in creation on TCollection You need To introduce Collation Class
it's Posible in two way
1 : hard coded in create time X := TMycollation.Create(TMyCollationClass)
2 : your solution X := TMycollation.Create(FindAnyClass('TMyCollationClass'));

How to copy data from one class into another class

how to copy data from one class into a second class using operator overloading with DELPHI ?
my dummy app goes like this :
type
TClass_A = class
a: String;
end;
TClass_B = class(TClass_A)
b: String;
end;
implementation
procedure TForm1.Button1Click(Sender: TObject);
var
a: TClass_A;
b: TClass_B;
begin
a := TClass_A.Create;
b := TClass_B.create;
b := a; // <<-- What code should be here? Can I overload := operator?
end;
The assignment operator cannot be overloaded in Delphi.
You will need to introduce a method to perform the copying. An example of how this might be done is TPersistent.Assign. It would be perfectly reasonable for you to derive from TPersistent and override the virtual Assign method to implement the desired functionality.
Here is a small example of how to do this with new style RTTI (D2010 and higher).
Please note that this is a very basic example which only copies fields (not properties) and works best with basic classes (do not use this with TComponent), the fields in the destination object must be the same type. FYI, there are better examples out there :)
uses
Rtti,...
function CopyObject(const FromObj, ToObj: TObject): Boolean;
var
Ctx : TRTTIContext;
FromObjType : TRttiType;
ToObjType : TRttiType;
FromField : TRttiField;
ToField : TRttiField;
begin
Result := False;
FromObjType := Ctx.GetType(FromObj.ClassInfo);
ToObjType := Ctx.GetType(ToObj.ClassInfo);
for FromField in FromObjType.GetFields do
begin
ToField := ToObjType.GetField(FromField.Name);
if Assigned(ToField) then
begin
if ToField.FieldType = FromField.FieldType then
ToField.SetValue(ToObj, FromField.GetValue(FromObj));
Result := True;
end;
end;
end;

Scan all classes for a given custom attribute

I'm looking for a way of scanning all loaded classes for classes which contain a custom attribute, if possible, without using RegisterClass().
at first you have to create TRttiContext, then get all loaded classes using getTypes. after that you can filter types by TypeKind = tkClass;
next step is to enumerate attributes and check if it has your attribute;
attribute and test-class delcaration:
unit Unit3;
interface
type
TMyAttribute = class(TCustomAttribute)
end;
[TMyAttribute]
TTest = class(TObject)
end;
implementation
initialization
TTest.Create().Free(); //if class is not actually used it will not be compiled
end.
and then find it:
program Project3;
{$APPTYPE CONSOLE}
uses
SysUtils, rtti, typinfo, unit3;
type TMyAttribute = class(TCustomAttribute)
end;
var ctx : TRttiContext;
t : TRttiType;
attr : TCustomAttribute;
begin
ctx := TRttiContext.Create();
try
for t in ctx.GetTypes() do begin
if t.TypeKind <> tkClass then continue;
for attr in t.GetAttributes() do begin
if attr is TMyAttribute then begin
writeln(t.QualifiedName);
break;
end;
end;
end;
finally
ctx.Free();
readln;
end;
end.
output is Unit3.TTest
Call RegisterClass to register a class with the streaming system.... Once classes are registered, they can be loaded or saved by the component streaming system.
so if you don't need component streaming (just find classes with some attribute), there is no need to RegisterClass
You can use the new RTTI functionality exposed by the Rtti unit.
var
context: TRttiContext;
typ: TRttiType;
attr: TCustomAttribute;
method: TRttiMethod;
prop: TRttiProperty;
field: TRttiField;
begin
for typ in context.GetTypes do begin
for attr in typ.GetAttributes do begin
Writeln(attr.ToString);
end;
for method in typ.GetMethods do begin
for attr in method.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
for prop in typ.GetProperties do begin
for attr in prop.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
for field in typ.GetFields do begin
for attr in field.GetAttributes do begin
Writeln(attr.ToString);
end;
end;
end;
end;
This code enumerates attributes associated with methods, properties and fields, as well as with types. Naturally you will want to do more than Writeln(attr.ToString), but this should give you an idea for how to proceed. You can test for your specific attribute in the normal way
if attr is TMyAttribute then
....