How can i know if the user define component is created? - delphi

I create a memo inside a procedure, using this code:
Global_MemoIni := TMemo.Create(Conf);
Global_MemoIni.Parent := Conf;
Global_MemoIni.Visible := False;
Global_MemoIni.Align := alClient;
Global_MemoIni.WordWrap := False;
When I call the procedure again it creates the global_memoini again.
How can I know if the component is created so I don't need to call it again?
Update : Can I use the Global_MemoIni.Free above the creation code so the next time create
the Global_memoini once... But i want to know if this is created...
Thank you

You can check if Global_MemoIni is Nil and create the TMemo if it is. Otherwise it already exists, you can then free it using Free or FreeAndNil. If you use free be careful that you assign Nil to Global_MemoIni. If you don't, you can't use the Global_MemoIni <> Nil check.

I honestly don't understand the point of using a memo in stead of a TStringList which is more lightweight. just do
unit UnitName;
interface
uses SysUtils, Windows, Classes, ...;
var Global_INI: TStringList; // <-- it's defined in the interface section, therefore
// it can be accessed by any unit which uses this unit
implementation
initialization
Global_INI := TStringList.Create;
Global_INI.LoadFromFile( 'C:\config.ini' ); // <-- replace the file name with the
// one you want
finalization
FreeAndNil( Global_INI );
end;

Don't do this is an arbitrary function. Either create the component in the FormCreate or even the constructor of the form, or make it a read-only property of the form, and use lazy instantiation, i.e.
if not Assigned(Global_MemoIni) then
begin
Global_MemoIni := TMemo.Create(Self);
// rest of your code
end;
Result := Global_MemoIni;
But why is it global? If you make it a field and corresponding read-only property of the form, it is easily accessible and you can protect it in the way shown above.
FWIW, instead of Free-ing the component, let the Owner (the form) do that. That way, it is available as long as the form exists, and no nasty invalid pointer issues can take place.

If you do not know the creation state of object use:
if not Assigned(Global_MemoIni) then
begin
Global_MemoIni := TMemo.Create(Conf);
...
end
And don't forget to use FreeAndNil(Global_MemoIni) when destroying the object.

Related

Create a TDataModule only once

I'm trying to build a procedure that creates a TDataModule in Application as its parent.
The problem is, the second time I call the procedure, the dm parameter still nil. I expect something diferent of nil considering that it was created before.
Here is the code I'm trying:
procedure UseDataModule(dm : TDataModule; cClass:TcomponentClass);
begin
if dm = nil then
cClass.Create(Application);
end;
There are some requirements I want for this procedure:
The given TDataModule should be created once
It must to be created by procedure because I want to use It sometimes, that's why I don't put it in auto-create forms
Its parent will be always Application
Try changing your code to this:
procedure UseDataModule(var dm : TDataModule; cClass:TcomponentClass);
// the `var` qualifier is to allow the value of `dm` to be retained
// after `UseDataModule` exits, otherwise the Created instance will be discarded
// and you will have a memory leak
begin
if dm = nil then
dm := cClass.Create(Application);
end;
Imo, it would be better to code UseDataModule as a function, but that is largely a matter of taste. Note also that you could write if notAssigned(dm) instead of if dm = Nil.
I gather from your comment that you have decided to use the following code instead of my initial suggestion:
procedure UseDataModule(var dm : TDataModule; cClass:TcomponentClass);
begin
if dm = nil then begin
dm := cClass.Create(Application) as TDataModule;
end;
end;
which seems fine to me.
Another option is to use the same code used to autocreate forms with a check to see if it is already created.
// Create data module if it doesn't already exist
if DM = nil then Application.CreateForm(TDM, DM);

VCL component causing error when try to free after have class changed

I have an application where I create a Popup menu class (lets say TNewPopupMenu) that inherited from TPopupMenu (that is created in the vcl)
On Create changing the class using
Procedure T.ChangeClass;
type
PClass = ^TClass;
Begin
PClass(FEventPopup)^ := TNewPopupMenu;
TNewPopupMenu(FEventPopup).OnDismissed := CallbackDismissPopUpMenu;
End;
If I leave like this I get a error when the application closes and try to free FEventPopup, I try to do this so solve:
destructor T.Destroy;
type
PClass = ^TClass;
begin
TNewPopupMenu(FEventPopup).OnDismissed := nil;
PClass(FEventPopup)^ := TPopupMenu;
inherited;
end;
But I still getting an exception when the main form try to free FEventPopup, what else do I need to do?
The construction PClass(FEventPopup)^ := TNewPopupMenu; looks scary. It looks like you are tyring to change the class type at runtime and that is not possible.
Whar are you trying to do?. If you want to create a class of type TNewPopupMenu, you can use:
FEventPopup := TNewPopupMenu.Create(nil);
Next define FEventPopup as a TNewPopupMenu. And preferably add an owner, so you don't have to worry about destruction. Don't forget to assign the popup menu to the appropriate controls.
You won't need PClass(FEventPopup)^ := TPopupMenu;.
If T is a TForm or a TForm descendant, you can create dynamical components in the OnCreate (and destroy them in the OnDestroy.
And you better not use T as a classname. It gives you no clue on what it is doing.
Besides, if you really want to use custom components, why not put it in a package and register it so you can use it in the form editor.

Delphi: Correct way to store objects fetched from TObjectList

This example is of course simplified, but basically I have a main form that triggers another form (frmSettings) with
function Execute(var aSettings: TSettings):Boolean
TSettings is my own object created in main form for keeping track of the settings.
In this newly opened form (frmSettings) I fetch a TMyObjectList that is a descendant from TObjectList.
It's filled with TMyObj.
I then fill a TListBox with values from that TMyObjectList.
the code:
...
FMyObjectList : TMyObjectList;
property MyObjectList: TMyObjectList read getMyObjectList;
...
function TfrmSettings.getMyObjectList: TMyObjectList ;
begin
If not Assigned(FMyObjectList) then FMyObjectList := TMyObjectList.Create(True)
Result := FMyObjectList;
end;
function TfrmSettings.Execute(var aSettings: TSettings): Boolean;
begin
//Fill myObjectList
FetchObjs(myObjectList);
//Show list to user
FillList(ListBox1, myObjectList);
//Show form
ShowModal;
Result := self.ModalResult = mrOk;
if Result then
begin
// Save the selected object, but how??
// Store only pointer? Lost if list is destroyed.. no good
//Settings.selectedObj := myObjectList.Items[ListBox1.ItemIndex];
// Or store a new object? Have to check if exist already?
If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);
end;
end;
procedure TfrmSettings.FillList(listBox: TListBox; myObjectList: TMyObjectList);
var
i: Integer;
begin
listBox.Clear;
With myObjectList do
begin
for i := 0 to Count - 1 do
begin
//list names to user
listBox.Items.Add(Items[i].Name);
end;
end;
end;
procedure TfrmSettings.FormDestroy(Sender: TObject);
begin
FreeAndNil(FMyObjectList);
end;
Storing just the pointer doesn't seem as a good idea, as triggering the settings form again, recreates the list, and the original object would be lost even if user hits "cancel"
So storing a copy seems better, using assign to get all the properties correct. And first checking if I already have an object.
If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);
Should I move those two lines to a method instead like Settings.AssignSelectedObj(aMyObj:TMyObj)
Does this look correct or am I implementing this the wrong way?
Something more/less needed?
I need some guidelines so I feel more secure that I don't open up for memory leaks and other trouble.
Other than that reviewing the code a bit, the real question is: Is this the correct way to store my SelectedObject in the settings class?
Is this the correct way to store the selected object in the settings?
Probably not. Your settings class should not depend on the form in any way. What if you decide to create and destroy your form dynamically each time the user opens the settings? In this case your settings would hold an invalid object reference.
IMHO it is better to store the object list in the settings together with the index of the selected object. The form should just access the settings, fill the list box and modify the selected object index after the user confirmed with OK.
You are producing a memory leak in your code. You create a TObjectList as a local variable but you never free it. And if you free the local variable, the object references in the listbox will be invalid. You have two options:
Store the object list as a member variable of your form, create in the FromCreate event handler and destroy it in the FormDestroy event handler. You can then safely use object references in your list box.
Store the object list somewhere outside and pass it into the form as a parameter of the Execute method. In this scenario, you can also safely use object references.
I would rename myObjectList to GlobalObjectList, and move it out of the class. It can be declared in the form, but create/free in the initialization/finalization sections. During initialization, after you create the list, populate it from the ini file (or wherever you store it). Now you can access it from anywhere that has your unit in the Uses.
What about the serialization of TSettings? Put your settings in some published properties, then let the RTTI save its content:
type
TSettings = class(TPersistent)
public
function SaveAsText: UTF8String;
end;
function TSettings.SaveAsText: UTF8String;
begin
var
Stream1, Stream2: TMemoryStream;
begin
Stream1 := TMemoryStream.Create;
Stream2 := TMemoryStream.Create;
try
Stream1.WriteComponent(MyComponent);
ObjectBinaryToText(Stream1, Stream2);
SetString(result,PAnsiChar(Stream2.Memory),Stream2.Size);
finally
Stream1.Free;
Stream2.Free;
end;
end;
Then your settings can be stored in a text file or text string.
It's just one solution. But storing settings as text is very handy. We use such an approach in our framework, to store settings via a code-generated user interface. A settings tree is created, from a tree of TPersistent instances.

Delphi getting value of form components properties

I am implementing a Boilerplate feature - allow users to Change descriptions of some components - like TLabels - at run time.
e.g.
TFooClass = Class ( TBaseClass)
Label : Tlabel;
...
End;
Var FooClass : TFooClass;
...
At design time, the value Label's caption property is say - 'First Name', when the
application is run, there is a feature that allows the user to change the caption
value to say 'Other Name'. Once this is changed, the caption for the label for
the class instance of FooClass is updated immediately.
The problem now is if the user for whatever reason wants to revert back to the design
time value of say 'First Name' , it seems impossible.
I can use the RTTIContext methods and all that but I at the end of the day, it seems
to require the instance of the class for me to change the value and since this
has already being changed - I seem to to have hit a brick wall getting around it.
My question is this - is there a way using the old RTTI methods or the new RTTIContext
stuff to the property of a class' member without instantiating the class - i.e. getting
the property from the ClassType definition.
This is code snippet of my attempt at doing that :
c : TRttiContext;
z : TRttiInstanceType;
w : TRttiProperty;
Aform : Tform;
....
Begin
.....
Aform := Tform(FooClass);
for vCount := 0 to AForm.ComponentCount-1 do begin
vDummyComponent := AForm.Components[vCount];
if IsPublishedProp(vDummyComponent,'Caption') then begin
c := TRttiContext.Create;
try
z := (c.GetType(vDummyComponent.ClassInfo) as TRttiInstanceType);
w := z.GetProperty('Caption');
if w <> nil then
Values[vOffset, 1] := w.GetValue(vDummyComponent.ClassType).AsString
.....
.....
....
....
I am getting all sorts of errors and any help will be greatly appreciated.
The RTTI System does not provide what you are after. Type information is currently only determined at compile time. Initial form values are set at Runtime using the DFM resource. You can change values in a DFM Resource in a compiled application because it's evaluated at runtime.
Parse and use the DFM Resource where it is stored, or make a copy of the original value at runtime. Possibly at the point of initial change to reduce your memory footprint.
Masons Suggestion of using TDictionary<string, TValue> is what I would use. I would be careful of storing this information in a database as keeping it in sync could become a real maintenance nightmare.
Sounds like what you're trying to do is get the value of a certain property as defined in the DFM. This can't be done using RTTI, since RTTI is based on inspecting the structure of an object as specified by its class definition. The DFM isn't part of the class definition; it's a property list that gets applied to the objects after they've been created from the class definitions.
If you want to get the values of the properties of a form's controls, you'll probably have to cache them somewhere. Try putting something in the form's OnCreate that runs through all the controls and uses RTTI to populate a TDictionary<string, TValue> with the values of all the properties. Then you can look them up later on when you need them.
If what you are trying to achieve it to restore the value that was set at design-time (i.e. that one that is saved in the DFM), I'd use InitInheritedComponent as a starting point.
It's possible to get the content of the DFM at runtime. Could be a pain to parse though.
Check also InternalReadComponentRes.
Both routine can be found in the classes unit.
Well - I solved the problem. The trick is basically instantiating another instance of the form like so :
procedure ShowBoilerPlate(AForm : TForm; ASaveAllowed : Boolean);
var
vCount : Integer;
vDesignTimeForm : TForm;
vDesignTimeComp : TComponent;
vDesignTimeValue : String;
vCurrentValue : String;
begin
....
....
vDesignTimeForm := TFormClass(FindClass(AForm.ClassName)).Create(AForm.Owner);
try
// Now I have two instances of the form - I also need to have at least one
// overloaded constructor defined for the base class of the forms that will allow for
// boilerplating. If you call the default Constructor - no boilerplating
// is done. If you call the overloaded constructor, then, boilerplating is done.
// Bottom line, I can have two instances AForm - with boilerplated values and
// vDesignForm without boilerplated values.
for vCount := 0 to AForm.ComponentCount-1 do begin
vDummyComponent := AForm.Components[vCount];
if Supports (vDummyComponent,IdoGUIMetaData,iGetGUICaption) then begin
RecordCount := RecordCount + 1;
Values[vOffset, 0] := vDummyComponent.Name;
if IsPublishedProp(vDummyComponent,'Caption') then begin
vDesignTimeComp := vDesignTimeForm.FindComponent(vDummyComponent.Name);
if vDesignTimeComp <> nil then begin
// get Design time values here
vDesignTimeValue := GetPropValue(vDesignTimeComp,'Caption');
end;
// get current boilerplated value here
vCurrentValue := GetPropValue(vDummyComponent,'Caption');
end;
vOffset := RecordCount;;
end;
end;
finally
FreeAndNil(vDesignTimeForm);
end;
end;
Anyway - thank you all for all your advice.

How can I check whether an object reference is still valid?

I have an issues where I am trying to determine if a reference to an object is valid. But it seems to be returning strange results.
procedure TForm1.Button1Click(Sender: TObject);
var form1 : TForm;
ref2 : TControl;
begin
form1 := TForm.Create(nil);
form1.Name := 'CustomForm';
form1.Parent := self; //Main Form
form1.Show;
ref2 := form1;
showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
freeandnil(form1);
showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
end;
The first showmessage returns - "TForm - CustomForm - True" (Just like I would expect it to).
The second showmessage return - "TForm - - False". I was actually hoping for some kind of access violation that I could then trap and know that the reference isn't valid.
In my application I need to compile a list of random TForm descendants as they are created and then check later if they have gone away (or are not visible). Unfortunately it is a plugin based system so I can go change all of these Forms to post a "I'm done Message."
Would code like this be safe to use (assuming I actually am checking for access violations)? Does anybody have any ideas what is happening.
Thanks
The problem is that with a certain likelyhood the memory accessed is still reserved by the Delphi memory manager. In that case Windows does not generate any kind of access violation, because that memory belongs to you!
One possibility is to switch to a different Delphi memory manager which can detect the use of freed objects. FastMM4, for example, has several "memory hygiene" checks, which are very useful for debugging, but even then you won't catch all of these errors immediately.
You can download FastMM4 from SourceForge.
Any TComponent (e.g. a TForm descendant) can register for notifications when other components are destroyed.
In your form, call FreeNotification(form) for each form that you wish to be notified of the destruction of. Then on the same form override the Notification() method. When any form (or other component) for which you have called FreeNotification() is destroyed, your Notification() method will be called with a Component parameter referencing the form and an Operation of opRemove.
If I've understood what it is you are trying to achieve, I think this should be enough information to devise an approach to do what you need.
After
freeandnil(form1);
the Delphi memory manager just marks the memory allocated by form1 as free, but all form1 data is still there, and can be accessed via ref2 until the memory manager reuse the freed memory for some other object(s).
You can't check that way if ref2 references a valid object or not. Code like this can't be safe, it is actually a bug.
If you want to obtain a 100% access violation modify the code as follows (here ref2^ = nil if form1 is freed):
procedure TForm1.Button1Click(Sender: TObject);
var form1 : TForm;
ref2 : ^TControl;
begin
form1 := TForm.Create(nil);
form1.Name := 'CustomForm';
form1.Parent := self; //Main Form
form1.Show;
ref2 := #form1;
showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
freeandnil(form1);
showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
end;
There is no reliable way to do what you are trying to do using the technique you're attempting. Forms that have "gone away" may have their memory reused, possibly even for a new form.
At best, you could work some mechanism whereby you cache the results of iterating Screen.Forms, but you can still fall foul of accidental duplicates, where a form gets destroyed and another gets reallocated and gets the same object address. That scenario is less likely than the memory being reused for some other object, however.
In a similar case I am using a singleton object that keeps a list of all the created forms.
Each form has a field with a reference to this Object.
TMyForm = class(TForm)
private
//*** This is the reference to the singleton...
FFormHandler: TFormHandler;
public
...
//*** you might want to publish it as a property:
property FormHandler: TFormHandler read FFormHandler write FFormHandler;
end;
You can set this reference e.g. when calling the constructor:
TMyForm.Create(aFormHandler: TFormHandler; aOwner: TComponent)
begin
FFormHandler := aFormHandler;
inherited Create(aOwner);
end;
(Or you could set the field from outside directly after creating the form if you don't want to change the parameters of the constructor).
When the form ist destroyed it notifies the handler and tells him to remove the form from the list - something like that:
TMyForm.Destroy(Sender: TObject);
begin
FFormHandler.RemoveFromFormList(Self);
inherited;
end;
(The details of the track-keeping are not included in the expample - e.g. a method "AddToFomList" or something alike would be needed)
There is one very interesting memory manager. It is called SafeMM: http://blogs.embarcadero.com/medington/2009/10/16/24839 But still it is for debugging only.
Given that you cannot modify the code that is out there in the plugins, all the good solutions about how to write safer code are not applicable to your case.
You have 1 way of doing it by
checking if an Object reference is
still what it's supposed to be by
looking up the VMT. This idea was
first published by Ray Lischner (who advocated for FreeAndNil for that very reason) and
later by Hallvard Vassbotn: see
this SO answer.
Another, better but introducing major slowdown, is to use FastMM4 in FullDebugmode to have it to replace all the freed objects by a TFreeObject instance instead of simply releasing the memory to the available pool.
Note that both methods do not prevent a false positive if another instance of the same class happens to be created at the same memory address. You get a valid object of the right type, just not the original one. (Unlikely in your case, but possible)
it is as simple as comparing against NIL:
// object declaration
Type object;
object = new Type();
...
// here you want to be sure of the existance of the object:
if (object <> nil )
object.free;
If you cannot test in another manner, you can use this as a last resort±
function IsValidClass( Cls: TClass ): Boolean;
var
i: Integer;
begin
for i := 0 to 99 do begin
Result := ( Cls = TObject ); // note that other modules may have a different root TObject!
if Result then Exit;
if IsBadReadPtr( Cls, sizeof( Pointer ) ) then Break;
if IsBadReadPtr( Pointer( Integer( Cls ) + vmtParent ), sizeof( Pointer ) ) then Break;
Cls := Cls.ClassParent;
end;
Result := False;
end;
function IsValidObject( Obj: TObject ): Boolean;
begin
Result := not IsBadReadPtr( Obj, sizeof( Pointer ) ) and IsValidClass( Obj.ClassType ) and not IsBadReadPtr( Obj, Obj.InstanceSize );
end;
IsBadReadPtr comes from Windows.

Resources