How to know that a form was created?
Asked Answered
M

3

4

I want to find a way to know that a form was created at run time (or destroyed). This for Delphi or fpc. Many thanks

PS : Is there a way to retrieve that info for all objects ?

Moult answered 11/6, 2013 at 11:0 Comment(4)
"Is there a way to retrieve that info for all objects?" What does that mean?Donnelldonnelly
I want to have a event who tell me that a new object was just created at run time (or destroyed).Moult
Can you explain why you would need such an event? A little background might give other possible solutions to your problem.Gratin
related #11576199Quiroz
D
6

I want to have a event that tells me that a new object was just created at run time (or destroyed).

There are no built in events that fire whenever an object is created or destroyed.

Because I like writing code hooks, I offer the following unit. This hooks the _AfterConstruction method in the System unit. Ideally it should use a trampoline but I've never learnt how to implement those. If you used a real hooking library you'd be able to do it better. Anyway, here it is:

unit AfterConstructionEvent;

interface

var
  OnAfterConstruction: procedure(Instance: TObject);

implementation

uses
  Windows;

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    Move(NewCode, Address^, Size);
    FlushInstructionCache(GetCurrentProcess, Address, Size);
    VirtualProtect(Address, Size, OldProtect, @OldProtect);
  end;
end;

type
  PInstruction = ^TInstruction;
  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;//jump relative
  NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;

function System_AfterConstruction: Pointer;
asm
  MOV     EAX, offset System.@AfterConstruction
end;

function System_BeforeDestruction: Pointer;
asm
  MOV     EAX, offset System.@BeforeDestruction
end;

var
  _BeforeDestruction: procedure(const Instance: TObject; OuterMost: ShortInt);

function _AfterConstruction(const Instance: TObject): TObject;
begin
  try
    Instance.AfterConstruction;
    Result := Instance;
    if Assigned(OnAfterConstruction) then
      OnAfterConstruction(Instance);
  except
    _BeforeDestruction(Instance, 1);
    raise;
  end;
end;

initialization
  @_BeforeDestruction := System_BeforeDestruction;
  RedirectProcedure(System_AfterConstruction, @_AfterConstruction);

end.

Assign a handler to OnAfterConstruction and that handler will be called whenever an object is created.

I leave it as an exercise to the reader to add an OnBeforeDestruction event handler.

Note that I am not saying that such an approach is a good thing to do. I'm just answering the direct question that you asked. You can decide for yourself whether or not you want to use this. I know I would not do so!

Donnelldonnelly answered 11/6, 2013 at 12:1 Comment(5)
+1 but in my opinion in most cases he could simply inherit a common ancestor form. It would be more simple and easy to manageKarlin
@MatheusFreitas Indeed. Take a read of my final paragraph. I am not advocating the use of this code.Donnelldonnelly
@MatheusFreitas Yes it's not quite a good practice, but it's damn elegant anyway...Wolverine
@David Heffernan : You are great and generous, i gonna study your code, many thanks.Moult
You should study more his comments which say "DON'T USE THIS".Bipod
W
0

Use TForm's OnCreate event to inform whoever you want in whatever way you want.

Wolverine answered 11/6, 2013 at 11:3 Comment(9)
Ok, it is what i thinking but how can you assisgn a general event for all new form created, for example a dialog box ?Moult
To be more clear, is it possible to assign a new OnCreate event for all new forms who will be created ?Moult
@Moult yes - just assign one. When you do NewForm := TNewForm.Create;, follow it with NewForm.OnCreate := MyOnCreateHandler;Lengthen
@Moult Many thanks, it seems to be on the right way. But, please, could you give a short code example. I do not understand what you mean with "just assign one". Many thanksMoult
I think i must be clearer. It is about a new library for making your application assistive. There is a procedure that you load at runtime "loadassistive" who scan all the form of the application and assisgn a new onClick, onKeypress, etc for each object (with application.component). It is working perfectly but if you create a new form at runtime the list of assistive objects is not updated, so that is the question : does it exist a way to know if a new form was created. Knowing that the library does not know what are the name of the form who gonna be created. ThanksMoult
I think it's better you program an Event in an ancestor form and inherit it for all others forms.Karlin
@ David. ;-). Ok, i try one more time. I want to have a signal saying that a new form (or object) was created at run-time.Moult
@ Matheus : thanks, but could you give me a code example how to do that ?. ThanksMoult
Generally, to create an inherited form, you goes at Menu: File / New / Other / <Name of Your Project> / Choose your form, make sure inherited is marked, and use it. I don't know about other versions, I'm using Delphi 7 yet.Karlin
M
0

In MS Windows you can hook events of your process using this small template:

{$mode objfpc}{$H+}
uses
    Windows, JwaWinUser;

function ShellProc(nCode: longint; wParam: WPARAM; lParam: LPARAM): longint; stdcall;
var
    wnd: HWND;
begin
    Result := 0;
    case nCode of
        HSHELL_WINDOWCREATED:
        begin
            wnd := wParam;
            // Check window
            // Get task handle
            // Get window icon
            // Add task to the list
            // Call event
        end;
        HSHELL_WINDOWDESTROYED:
        begin
            wnd := wParam;
            // Check window
            // Get task handle
            // Get window icon
            // Remove task to the list
            // Call event
        end;
        HSHELL_LANGUAGE:
        begin
            // Get language
            // Call event
        end;
        HSHELL_REDRAW:
        begin
            // Call event
        end;
        HSHELL_WINDOWACTIVATED:
        begin
            // Get language
            // Call event
        end;
        //HSHELL_APPCOMMAND:
        //begin
        //    { TODO 1 -ond -csys : Specify return value for this code }
        //    Result := -1;
        //end;
    end;

    // Call next hook in the chain
    Result := CallNextHookEx(
        0,
        nCode,
        wParam,
        lParam);
end;

var
    FCallbackProc: HOOKPROC;

function InitShellHook(AProc: HOOKPROC): HHOOK; stdcall; export;
begin
    FCallbackProc := AProc;
    Result := SetWindowsHookEx(WH_SHELL, @ShellProc, 0, 0);
end;

procedure DoneShellHook(AHook: HHOOK); stdcall; export;
begin
    UnhookWindowsHookEx(AHook);
end;

HSHELL_WINDOWCREATED will inform you that your process was create new window.

Call InitShellHook with your procedure address (see HOOCPROC declaration).

Marriage answered 11/6, 2013 at 16:11 Comment(6)
It's quite hard to relate that to Delphi objects. Not least to window re-creation techniques in VCL.Donnelldonnelly
@DavidHeffernan It works for any window (as i remember question was also about ShowMessage). For regular Delphi forms i will try to see to Application object or try to write TForm helper.Marriage
Not any window. Only top level unowned windows. My point was more about re-creation. You'll get these notifications more than once over a form's life, and not when the form is originally created.Donnelldonnelly
@DavidHeffernan This hook works on the any event of calling process: new window, nwe keybd layout and so on (remember my question about win taskbar? It is part of this).Marriage
I know. I use that same hook myself. I'm just pointing out that it will not fire once and once only when the Delphi form is created.Donnelldonnelly
@DavidHeffernan In case of native Delphi form I hope if Form.Handle = wnd will helpful :o) In my current poject i will use a lot of Win API features. So I hope for your advices :o)Marriage

© 2022 - 2024 — McMap. All rights reserved.