Solved

How to free dynamic TImages... tricky question.

Posted on 1998-10-13
13
213 Views
Last Modified: 2010-04-06
I create 10 to 20 TImages when a certain button is pressed.  I fill in the tag of each image with a certain value.  I load a bitmap into the image.  Most importantly I assign a one predefined procedure to the OnClick of each TImage.

This predefined procedure performs some loading of stuff in the program.  As soon as the procedure is completed I want to free the TImages... I don't want them hanging around using precious resources (my program is already relatively resource hungry).  Unfortunately I can't free the TImages in the procedure that is attached to the TImages OnClick event, as it causes an AccessViolation when the procedure tries to end... since the TImages no longer exist, the fucntion no longer has the reference back to the objects it was expecting.

How do I free these TImages??  Is there any way to call a procedure from the OnClick procedure that would be guarenteed to execute after the OnClick procedure is finished?  I thought of using a TTimer, but this just seems to kludgy.  There has got to be a better way.
0
Comment
Question by:mheacock
  • 5
  • 2
  • 2
  • +3
13 Comments
 
LVL 10

Expert Comment

by:Jacco
Comment Utility
procedure TForm1.FormCreate(Sender: TObject);
begin
  FImage:=TImage.Create(Application);
  with FImage do begin
    Parent:=Self;
    Picture.LoadFromFile('c:\HotConLogo.bmp');
    OnMouseUp:=Image1MouseUp;
  end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  TImage(Sender).Free;
end;

This way it works!

Regards Jacco
0
 
LVL 7

Expert Comment

by:BlackMan
Comment Utility
What I would do, is to add the images to a TList when they are created and then make the OnClick procedure just send a message with the tag value. Then you can create a messagehandler, which loops thru the list, does whatever is needed and frees the image.

Something like:

Const
  mcMyMessageHandler = WM_USER + 1;

// Implemented in the Form class
     Procedure MyMessageHandler(Var Msg : TMessage); Message mcMyMessageHandler;

And then in the implementation..

Procedure TMainForm.OnImageClick(Sender : TObject);
Begin
  SendMessage(FMainForm.Handle, mcMyMessageHandler, (Sender as TImage).Tag, 0)
End;

Procedure MyMessageHandler;
[pseudo code ON]
Loop thru the list to find a object with tag=msg.wparam
Do whatever you like..
Free the image
[Pseudo code OFF]

If you want a more detailed example, just say so...

0
 
LVL 3

Author Comment

by:mheacock
Comment Utility
I'm going to reject answers until tonight, then I will look over all answers and invite the one I like best to answer again.  I want as many people to answer this as possible.  People tend not to answer answered questions.
0
 
LVL 4

Expert Comment

by:erajoj
Comment Utility
Hi,
Here is how I do it. It uses PostMessage to make sure the message sent doesn't get handled until the event handler is done.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;

const
  MSG_KILLIMAGE = WM_USER;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure ImageClick(Sender: TObject);
  private
    procedure KillImage( var msg: TMessage ); message MSG_KILLIMAGE;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y: Integer;
begin
  SetCurrentDir( ExtractFileDir( ParamStr( 0 ) ) );
  for x := 0 to 3 do
    for y := 0 to 3 do
    begin
      with TImage.Create( Self ) do
      begin
        AutoSize := True;
        Left := 10 + x * 80;
        Top  := 10 + y * 80;
        Parent := Self;
        Picture.LoadFromFile( 'bitmap1.bmp' );
        OnClick := ImageClick;
      end;
    end;
end;

procedure TForm1.KillImage( var msg: TMessage );
begin
  ( TObject( msg.LParam ) as TImage ).Free; // here is where the image gets killed!
end;

procedure TForm1.ImageClick(Sender: TObject);
begin
  PostMessage( Handle, MSG_KILLIMAGE, 0, Integer( Sender ) ); // prepare to die!!!
end;

end.

Hope this helps!

/// John

0
 
LVL 8

Expert Comment

by:ZifNab
Comment Utility
My first idea was also using a TList..., but I wasn't the first... :-)
0
 
LVL 3

Author Comment

by:mheacock
Comment Utility
I'm going to reject answers until tonight, then I will look over all answers and invite the one I like best to answer again.  I want as many people to answer this as possible.  People tend not to answer answered questions.
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 3

Author Comment

by:mheacock
Comment Utility
I was already using a TList, since there doesn't seem to be anyway to get to these components through the Component[I] property.  Setting name doesn't work and checking for TImages with large tag values doesn't work either.  There may be another way of getting dynamically created TImages into the ComponentCount list, but I don't know it.

Anyhow, I'm going to try out some of the suggestions.  I'll probably ask for an answer from someone tonight or tomorrow morning.

It is looking like Erajoj is the winner though, at the moment, since he seems to have a good implementation.   The only difference is that my KillImage proc would free all the images, thus I still need my TList to maintain references to all the TImages I create.

For those that may read this for information, I cannot stress enough the importance of TLists.  They are, for me, the most widely used and general purpose class in the VCL.  Very simple to use and very generic.  Be careful though, improper use can lead to massive memory leaks when freeing the TList and the objects stored in the TList.  Good knowledge of Delphi pointers is usually necessary, as you are usually casting dereferences and such to other type structures.

Thanks everyone.
0
 
LVL 3

Author Comment

by:mheacock
Comment Utility
A toss up between Blackman and Erajoj.  But since Blackman gave Sendmessage as the call and Erajoj correctly gave PostMessage, I will now ask that erajoj post his answer and I shall award him an A.  (Sorry, BM, I know you posted the answer first, but Erajoj's was more correct and more complete).
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
I like Erajoj's answer the most. I'm using such a mechanism in my programs, too. BTW, "Form.Release" works exactly the same way...

Regards, Madshi.
0
 
LVL 7

Expert Comment

by:BlackMan
Comment Utility
No problem with me...
0
 
LVL 10

Expert Comment

by:Jacco
Comment Utility
My answer was simpler.

But the beauty of Delphi is that you can also do thing complex ;-)

Regards Jacco
0
 
LVL 4

Accepted Solution

by:
erajoj earned 250 total points
Comment Utility
Hi,
Glad you think my comment was worth rewarding.
Here's an implementation of freeing all images:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;

const
  MSG_KILLIMAGE = WM_USER;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure ImageClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FImageList: TList;
    procedure KillAllImages;
    procedure KillImage( var msg: TMessage ); message MSG_KILLIMAGE;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y : Integer;
  Image: TImage;
begin
  SetCurrentDir( ExtractFileDir( ParamStr( 0 ) ) );
  FImageList := TList.Create;

  for x := 0 to 3 do
    for y := 0 to 3 do
    begin
      Image := TImage.Create( Self );
      FImageList.Add( Image );
      with Image do
      begin
        AutoSize := True;
        Left := 10 + x * 80;
        Top  := 10 + y * 80;
        Parent := Self;
        Picture.LoadFromFile( 'bitmap1.bmp' );
        OnClick := ImageClick;
      end;
    end;
end;

procedure TForm1.KillAllImages;
begin
  while ( FImageList.Count > 0 ) do // kill 'em all!
  begin
    TImage( FImageList[ 0 ] ).Free; // here is where the images gets killed!
    FImageList.Delete( 0 ); // remove list entry
  end;
end;

procedure TForm1.KillImage( var msg: TMessage );
begin
  KillAllImages; // structural programming ;-)
end;

procedure TForm1.ImageClick(Sender: TObject);
begin
  PostMessage( Handle, MSG_KILLIMAGE, 0, 0 { Integer( Sender ) } ); // prepare to die!!!
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  KillAllImages;   // destroy images
  FImageList.Free; // destroy list
end;

end.

/// John
0
 
LVL 3

Author Comment

by:mheacock
Comment Utility
You didn't have to post a TList implementation.  I already did that myself.  Might be useful for other people, but seeing as there is no "search past answers" functionality, well, I doubt anyone actually looks at any past answers.

To Jacco... your version my work, but it is still sloppy in my opinion (and shouldn't work) as you are still freeing the TImages from a method that is hooked into the TImages (via the OnMouseUp).
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

744 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

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now