?
Solved

Form.Free doesn't free all system resources?

Posted on 2005-05-11
20
Medium Priority
?
527 Views
Last Modified: 2010-04-05
I am debbuging a strange case: A program that silently open and close some TForms without user input is giving me strange behaviours: After some TForm.create and the corresponding Form.Free, I get a "Canvas does not allow drawing" error on the Form.Create line.

Some clues:

A) Only one TForm is created at each moment, it is not nested or similar, so in theory, only one Form is alive.

B) I put this line inside a try except, and in the case it fails, it is tried a second time, and this time the error changes to "Out of system resources", so my gues is that I need to force some kind of garbage collection or similar. Memory usage of the proccess is not higher than when it started, but Windows proccess manager is very pethetic in the information it shows: No mention to system resources anywhere.

C) After some investigation, I pin pointed something strange that minimised the frecuency of those strange errors, and that can be a valuable clue: I had a object created inside the form that needed to reference the canvas of a TImage on the form, so it used a TCanvas varialbe that, on creation, was defined in the fasnion "Canvas:= Sender.Image.Canvas" so I had to pointers to the same canvas. The error was on some cases arraising here, so I changed it so only one pointer exists (Canvas is now a property that, on get it returns "Sender.Image.Canvas" so no copy of the canvas pointer is made).

D) Point C doesn't cause any problem is used by a human user (opening and closing the same Forms but using mouse clicks and closin using X buton) even if the user open and close more forms (by far) that the automatic proccess... so it is not a problem with the amount of TForms that have been opened and closed, it has to be a different between opening and closing programatically or manually.

E) In case it is a problem of garbage collection not being fired, I make a cleaun up each time I am going to open a form with this code:
  if Win32Platform = VER_PLATFORM_WIN32_NT then
    SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);

So here I get lost in a sea of posibilities, none of witch really convince me... my bets goes to those posibilities:

1.- Creating, Showing and then Freeing a TCanvas or TForms from code need some kind of special free or similar that I am not aware of.

2.- FreeAndNil(Form) is not equivalent to closing it by mouse, may be the application is not aware of the freeing and keeps resources allocated in some how.

3.- I have some kind of "resource leak" in my code... I have tested it and I don't have any significant memory leaks, as after 10 or 20 create/free of TForms, the memory usage remains as when it started the job. Using a better proccess manager could help me in that point... I will be cheking while you read this!

4.- I deserve this because what I did during previous lifes ;)

Any suggestion will be tested, never mind if it sounds not very credible, I would beglad to fix it even without knowing why it was happening to me!
0
Comment
Question by:Sergio_Hdez
20 Comments
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13976272
not read all,

but instead of the free-method use the release-method,
this ensures that all pending messages for this from are processed before
the form-object is freed from memory

you may also keep in mind, that delphi manages the memory-usage for you
and not free uneeded memory-allactions immediatly

maybe you should dereference your tcanvas-property-pointer (set to nil)
before you are releasing the form

meikl ;-)
0
 
LVL 3

Accepted Solution

by:
raidos earned 2000 total points
ID: 13976276
1. Not that I'm aware of.
2. FreeAndNil(Form) is definately not the same as closing it with the mouse... Form.Close should be though.
3. Try using Memproof to locate the resource leaks, it can be downloaded from http://www.automatedqa.com/downloads/memproof/
4. We all deserve to get rid of bugs and problems with our code, regardless what we have done in previous lives.

Regards
  //raidos
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976313
More tests:

-I have disabled the object that "stole" the canvas from the TImage, just to see if it was the key to it all: It is not, the error still arrise.

-As the proccess that open all those TForms is a home made web server using Indy componenets, I took all the TForm.create away from the code to test if the server code it self was responsable of the error: It is not, Indy are out of suspects.

So TForm.create and then FreeAndNil(Form) are the key.

What is inside those Forms?

OK, those forms use visual inheritance, they all (there are 3 classes of TForms being opened) inherited from a Form+TDBGrid base form. It has not been a problem in 10 years of application usage in about 100 small companies, so it is really tested.

Inside the forms you find a menu and a speed butons panel in the top (no problem here, I think), a TDataSet connected to a TDataSource connect to a TDBGrid. All them use FreeIB connection to the DB (FreeIB was modified by us to make it dialect 3 compatible so whe can use with FireBird 1.5).

Not much more inside, and all this looks problem free, as I mentioned, all this have been in use (mouse driven use, not programatically created) for years on many installations.

OK, keep waiting for ideas while performing more tests...
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 27

Expert Comment

by:kretzschmar
ID: 13976334
>opening and closing the same Forms but using mouse clicks and closin using X buton
closing a form by the X-Button causes an internal call of the release-method
(in case of CloseAction is set to caFree)

so i would pleased you to test the release-method

meikl ;-)
0
 
LVL 15

Expert Comment

by:mikelittlewood
ID: 13976354
Do you have to call Form1 := nil after you call Form1.Release kretzschmar? Or is it not needed?
Ive always used FreeAndNil(Form1) myself, but I have never looked into the difinitive reasons for which to use.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13976387
>Do you have to call Form1 := nil after you call Form1.Release kretzschmar?
yes, in case you want to have the reference-var clean

FreeAndNil do call the free-method, so that pending messages may not processed properly
(in worst case you get an accesviolation or other exceptions)

meikl ;-)
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976476
raidos:
--------
You were right, I was silly to use free and not close. I have tested it using close instead of free and most of the problem is fixed... but still get out of resources after some more testing, so still there is a more strange issue around... anyhow, points will be yours, but also for the one to find out the second "residual" problem.

The problem comes from the code I mentioned earlier, where a canvas of a TImage is used inside other object. Using MyObject.Canvas:= Sender.Image.Canvas is NOT working the way it should, you don't end up having two pointers to one canvas, you get two different canvas (not sure of this) and freeing the first one seems not to release the resources used by the copied one. Weird!

This code was changed so MyObject.Canvas is now a property, and the write method of the property is like this:

function TGLaser.GetCanvas: TCanvas;
begin
  if Host is TPrinter then
    result:= TPrinter(Host).Canvas
  else if Host is TImage then
    result:= TImage(Host).Canvas
  else
    result:= nil;
end;

Where Host is the Form.Image (it can also be a printer canvas, but is not the case here).

If making Canvas:= Form.Image.Canvas is allocating new resources for the canvas, then this code is not helping, but allocating new resources each time the function is called.

May be I should free both the original and the copied canvas... but shouldn't the second canvas.free result in a exception as Canvas has allready been freed? I will test in the meanwhile to see if it helps or make it crash!

kretzschmar
--------------
Releasing the form is not needed after I changed to close instead of free, as after closing, and performing a Application.ProccessMessages, the Form is nil so you can't free or release it anymore... but the problem was related to it, anyhow!

About making nil the second canvas before Form.close... I will try this, along with making free on both, just to see how it performs, but the second problem have to come from here... may be canvas acts like DLL, and keep a list of TObject pointing/using it, so freeing only one of them doesn't free the windows resource.


I will come in some minutes with the canvas testing made...
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13976533
close will not free the form,
if the CloseAction in the onClose-event is not set to caFree,
the form will be only hidden (CloseAction is defaulted to caHide)

-> check your onClose-event for this

meikl ;-)
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976684
kretzschmar : The OnClose event is set to caFree.

As I was afraid, making Canvas.Free on the object that copied the Form.Image.Canvas raises an error when the Form is closed, as the canvas was pased by reference and then only one object TCanvas exits.

Using Canvas:= Nil on the object.destroy is the next choice, but so far it seems not to help (I am still making test on this change).

Also tried to check Canvas.LockCount, but is was zero so it is not the good way neither.
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976775
OK, more tests:

-I am now doing a Canvas:=Nil on the object destroy, and not using the Canvas property, instead a copy of the pointer directly, as using property didn't help. Error is istill araising.

-Process Explorer show no significant memory leaks, neither on resources. Well, after 4 or 5 create/close cicles, some times the "GDI Handles" can change from 104 to 108 and also "Private bytes" can change a little, but only +16K. When it crashed, GDI where around 140 and private bytes around 2000 KBytes, nothing to make the app crash.

So I am again out of ideas to try, BUT Object.Canvas:= Form.Image.Canvas still smelt as the roten part of the code to me, I don't know why.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13976825
well, more ideas i do also not have yet

how looks your destroy-routine?
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976912
The object that copy the canvas is called TGLaser, in the OnCreate, it copies the canvas of the sender (the sender is a TImage or a TPrinter) with this code:

constructor TGLaser.Create(Sender: TObject; px,py: real);
begin
  if Host is TImage then
    Canvas:= TImage(Host).Canvas;
    ...more code here but nothing about Canvas...

The error has araise in the line "Canvas:= TImage(Host).Canvas;" some times, but most of the times appears on the previous "Form.create", long before the corresponding TGLaser is created, BUT after a lot of Forms (with a TGLaser inside) were created and closed, so it looks to me as if this line was wasting resources in some way, may be I am totally wrong on that point.

The destructor was not necesary, and I have added it just to try the "Canvas:=Nil" idea:
 
destructor TGLaser.destroy;
begin
  Canvas:= Nil;
  inherited;
end;

This canvas it self is not destroyed by code, as it belongs to a TImage on the Form, so the Form.close should free it all... or not?
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976916
Ops, sorry, in the avobe code, the line "Host:= sender" was accidentally deleted while coping it here, so Host=Sender.
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13976929
Another clue: When the error occur in Form.create line, using F7 to step into the code line by line doesn't help, as the error occurs in THAT line, not in any initialization routine inside Form.OnCreate or similar. It is internal form creation that fails to allocate resources for the canvas.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13977002
hmm,

does TGlaser in its Create-constructor any painting on the evaluated canvas?
i just ask, because as long the form itself is in its create or onCreate-event,
the components are all cereated, but the form itself is not shown at this moment.
a paint in this state may result in the exception "canvas allows ...."

meikl ;-)
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13977026
To make it more strange, a new fact:

After about 200 cicles of form.create, form.close, I get the error, but if I let delphi go on, the exception is trapped and the form is create a secont time, in the same way... and this time the error don't appear, and the aplication continue to work 100% ok, and you can create/close more forms with no errors!

Here the code:

      try
        sleep(100); //To avoid timing problems, wait a little
        GridFac:= TGridFactura.create(self); //Here you get "Canvas does not allow drawing"
      except
        beep; //on error, it comes here and try again the same create..
        GridFac:= TGridFactura.create(self); //It succeded here and everything continue OK !?
      end;

By the way, those test are made without creating the TGLaser object, so Canvas:= Sender.Canvas is not the real problem.... if some one solve it, it would really deserve millions of points!

I am beginnign to thing is has to be with my previous lifes bad acts, really.
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13977054
kretzschmar :

TGlaser paints nothing oncreate or destroy, also, the creatin works OK 99% of the time, and also, the last post is about the problem araising without creating any TGLaser, so TGLaser is not the real problem, just may be it make all fail faster.

It sounds to me that it is something similar to that, but on the TForm.create internal process, as the error is fired on TForm.create... but a second call to TForm.create does not raise the error. Extrange.

OK, I am going to try to solve it the rude way: I will try TForm.create 10 times after raising an error. It is sad not to know why it happends, but the goal is to make it work!

Any idea is still wellcome, of course.
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13977281
no new ideas yet, but still thinking . . .
0
 
LVL 6

Author Comment

by:Sergio_Hdez
ID: 13977305
Doing it the rude way, solved it but still can't believe it!

This is the creating code:

      Intentos:= 0; //intentos = tries
      Exito:= false; //Exito = succeded
      while (Intentos<10) and not Exito do begin
        try
          inc(Intentos);
          GridFac:= TGridFactura.create(self); //The form to be created
          Exito:= true;
        except
          beep;
        end;
      end;
      if not Exito then
        raise exception.create('Imposible abrir listado de facturas.');

OK, this code works: After some (10 to 50 or more) times working error free, then the create line fails, the while loop makes a second try, and this time succed... weird!

While tracing and debugging, delphi gets very slow after the first error, and it don't recover its normal speed until the create line is reached again. This time, the creating proccess is instantaneus and delphi continue to work fine and quickly.

The closing code is simple, just this:

      GridFac.Close;
      Application.ProcessMessages; //wait for the window to really close

I will let the quuestion opened a little more, I still would like to know the reason why this fixed the problem!
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 13977348
yep, thats really courious
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
Suggested Courses

831 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question