Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect that a form is being destroyed across the Application?

We have many forms in our application, and I need a global event handler to detect when one of the forms is being destroyed (and then take some action).

p.s: I want to avoid adding code to each form that will need to send a message to the main form when it's about to destroy. also most of the forms are created and destroyed dynamicaly at run-time.

I was thinking about maybe use a global TApplicationEvents.

What is the best approach for this?

like image 381
Vlad Avatar asked Dec 07 '13 19:12

Vlad


1 Answers

A constraint on modifying code in existing forms, or creation of forms, as can be seen from other answers and comments, leaves hacks and hooks. A local CBT hook, f.i., would be a little work but probably work fine. Below is one of the simpler hacky solutions.

Screen global object holds a list of forms at all times via a regular TList. TList has a virtual Notify procedure which is called every time an item is added/removed. The idea is to employ a TList derivative that overrides this method and use it in the Screen object.

type
  TNotifyList = class(TList)
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
  end;

procedure TNotifyList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  inherited;
  if (Action = lnDeleted) and (csDestroying in TForm(Ptr).ComponentState) and
      (TForm(Ptr) <> Application.MainForm) then
    // do not use ShowMessage or any 'TForm' based dialog here
    MessageBox(0,
        PChar(Format('%s [%s]', [TForm(Ptr).ClassName, TForm(Ptr).Name])), '', 0);
end;

Testing for csDestroying is required because the Screen adds/removes forms to its list not only when forms are created/destroyed but also when they are activated etc..

Then make the Screen use this list. This requires an "accessing private fields" hack, as the FForms list is private. You can read about this hack on Hallvard Vassbotn's blog. It also requires "changing the class of an object at run time" hack. You can read about this hack on Hallvard Vassbotn's blog.

type
  THackScreenFForms = class
{$IF CompilerVersion = 15}
    Filler: array [1..72] of Byte;
{$ELSE}
    {$MESSAGE ERROR 'verify/modify field position before compiling'}
{$IFEND}
    Forms: TList;
  end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  PPointer(THackScreenFForms(Screen).Forms)^ := TNotifyList;
end;

Note that the notification will be called for every form destruction. This also includes forms created through MessageDlg, ShowMessage etc..

like image 119
Sertac Akyuz Avatar answered Oct 21 '22 10:10

Sertac Akyuz