How to check if an object reference is valid?

I have problems when I try to determine if an object reference is valid. But this seems to return 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" (as expected).

The second output of showmessage is "TForm - - False". I really hoped for some kind of access violation, which I could then catch and find out that the link is invalid.

In my application, I need to compile a list of random TForm descendants as they are created, and then check back later if they are gone (or not visible). Unfortunately, this is a plugin system, so I can modify all of these forms to post the message "I made a message."

Could such a code be safe to use (provided that I really check for access violations)? Does anyone have any idea what is going on.

thanks

+4
source share
9 answers

The problem is that with some probability, memory access is still retained by the Delphi memory manager. In this case, Windows does not create any access violation, because this memory belongs to you!

One possibility is to switch to another Delphi memory manager, which can determine the use of freed objects. For example, FastMM4 has several "memory" checks that are very useful for debugging, but even then you will not immediately understand all these errors.

You can download FastMM4 from SourceForge .

+6
source

Any TComponent (for example, a TForm descendant) can register for notifications when other components are destroyed.

In your form, call FreeNotification (form) for each form that you want to receive a notification of destruction about. Then, in the same form, override the Notification () method. If any form (or other component) for which you called FreeNotification () is destroyed, your Notification () method will be called using Component >, referring to the form and opRemove Operation .

If I understand what exactly you are trying to achieve, I think that there is enough information for this to develop an approach to what you need.

+6
source

After

 freeandnil(form1); 

The Delphi memory manager simply marks the memory allocated by form1 as free, but all form1 data still exists and can be accessed through ref2 until the memory manager reuses the freed memory for some other objects.

You cannot check this path if ref2 refers to a valid object or not. This code cannot be safe; in fact, this is a mistake. If you want to get a 100% access violation, change 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; 
+2
source

In a similar case, I use a Singleton object that stores a list of all created forms. Each form has a field with a link 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 link, for example. when calling the constructor:

 TMyForm.Create(aFormHandler: TFormHandler; aOwner: TComponent) begin FFormHandler := aFormHandler; inherited Create(aOwner); end; 

(Or you can set the field from the outside immediately after creating the form, if you do not want to change the constructor parameters).

When the ist form is destroyed, it notifies the handler and tells it to remove the form from the list - something like this:

 TMyForm.Destroy(Sender: TObject); begin FFormHandler.RemoveFromFormList(Self); inherited; end; 

(Tracking details are not included in the decryption - for example, the "AddToFomList" method or something similar will be needed)

+1
source

There is no reliable way to do what you are trying to do using the technique you are trying to do. Forms that are "gone" can reuse their memory, perhaps even for a new form.

In the best case, you can work with some mechanism in which you cache the results of the Screen.Forms iteration, but you can still make a mistake in random duplicates, where the form will be destroyed and the other will be redistributed and get the same address of the object. However, this scenario is less likely than reusing memory for some other object.

+1
source

There is one very interesting memory manager. It is called SafeMM: http://blogs.embarcadero.com/medington/2009/10/16/24839 But still it is intended only for debugging.

+1
source

Given that you cannot change the code that plugins have, all the good decisions on how to write safe code are not applicable to your case.

  • You have 1 way to do this, checking the reference to the object is still what it should have been looking at VMT. This idea was first published by Ray Lisschner (who, for this reason, advocated FreeAndNil ) and later by Hallvard Vassbotn : see this SO answer .

  • Another, better, but major slowdown is the use of FastMM4 in FullDebugmode so that it replaces all freed objects with a TFreeObject instance instead of just freeing memory into an available pool.

Note that both methods do not prevent false positives if another instance of the same class is created with the same memory address. You get a valid object of the desired type, not the original one. (Unlikely in your case, but possible)

0
source

it's as simple as comparing with 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; 
0
source

If you cannot test in another way, 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.

-one
source

Source: https://habr.com/ru/post/1299659/


All Articles