2011-06-17 70 views
4

Delphi是否爲表單創建(或更一般地說,表單生命週期事件)提供某種事件或鉤子?Delphi是否提供表單創建通知的事件處理程序?

因此,如果代碼中的某個地方創建並顯示了一個表單(模態或非模態,動態地或在通常的應用程序starup階段),Delphi調用一個事件處理程序,它允許記錄/分析/修改表單它顯示?

我知道有些選項涉及引入基本窗體類或自定義窗體創建過程,但對於已有多種窗體的現有應用程序來說,'非常好'具有非侵入式選項以添加類似於面向方面編程(AOP)中的交叉問題。例如,如果我有一些使用統計信息跟蹤的代碼來注入附加的事件處理程序,那麼我可以簡單地爲每個表單添加此功能,開發人員不必更改應用程序代碼,只添加類似於此代碼的代碼。

... 
    Application.OnNewForm := MyNewFormCreated; 
... 

procedure TMyApp.MyNewFormCreated(Sender: TCustomForm); 
begin 
    // iterate over components and do other stuff with the new form 
    ... 
end; 
+1

沒有什麼內置的。最好的解決方案是有一個共同的基類。 –

+0

@David:是的,這可能是最好的解決方案。請做出答案。 –

+1

@Andreas這不是一個答案,因爲@mjn說,通用基類不是想要的。 –

回答

1

我現在能想到的最適合您需要的選項是Screen.OnActiveFormChange事件,每次當前活動窗體更改時都會觸發該事件。但是,在您的需求過程中,這可能太晚了。

+0

對我來說聽起來不錯;遲到比從未好;) – mjn

+1

但是'太頻繁'呢?每次更改活動表單時都會觸發。例如,如果在整個應用程序的整個生命週期中只有兩種形式,但在761次之間切換,那麼事件將發生大約761次... –

+1

使用'TListOfKnownForms'解決方案 – mjn

4

在運行時,你可能會覆蓋TCustomForm.DoCreateTCustomFrame.Create方法,因爲這樣的:

type 
    THookedForm = class(TCustomForm) 
    procedure HookedDoCreate; 
    end; 

    THookedFrame = class(TCustomFrame) 
    constructor Create(AOwner: TComponent); override; 
    end; 

var 
    OriginalForm, OriginalFrame: TPatchCode; 

procedure PatchCreate; 
begin 
    if OriginalForm[0]<>0 then 
    exit; // patch once 
    RedirectCode(@THookedForm.DoCreate,@THookedForm.HookedDoCreate,@OriginalForm); 
    RedirectCode(@THookedFrame.Create,@THookedFrame.Create,@OriginalFrame); 
end; 


// hook logic was inspired from GetText() 

{ THookedForm } 

procedure THookedForm.HookedDoCreate; 
// translate form contents just before an OnCreate handler would be called 
begin 
    try 
    try 
    if Language<>nil then begin 
     DisableAlign; 
     DisableAutoRange; 
     try 
     Language.FormTranslateOne(self); // translate form 
     finally 
     EnableAlign; 
     EnableAutoRange; 
     end; 
    end; 
    finally 
    RedirectCodeRestore(@THookedForm.DoCreate,OriginalForm); // disable Hook 
    try 
     DoCreate; // call normal DoCreate event 
    finally 
     RedirectCode(@THookedForm.DoCreate,@THookedForm.HookedDoCreate); 
    end; 
    end; 
    except 
    on Exception do; // ignore all raised exception 
    end; 
end; 

{ THookedFrame } 

constructor THookedFrame.Create(AOwner: TComponent); 
// translate frame contents just after constructor has been called 
begin 
    RedirectCodeRestore(@THookedFrame.Create,OriginalFrame); // disable Hook 
    try 
    inherited Create(AOwner); // call normal constructor 
    finally 
    RedirectCode(@THookedFrame.Create,@THookedFrame.Create); 
    end; 
    if Language=nil then exit; 
    DisableAlign; 
    DisableAutoRange; 
    try 
    Language.FormTranslateOne(self); // translate frame 
    finally 
    EnableAlign; 
    EnableAutoRange; 
    end; 
end; 


.... 

initialization 
    PatchCreate; 

因此,自己DoCreate活動將每次創建TForm的實例時被調用。

此代碼是從mORMoti18n.pas中提取的,您可以在SynCommons.pas中找到補丁程序(適用於Windows和Linux/BSD)。

+0

什麼是TPatchEvent?我想這是[this](http://synopse.info/fossil/artifact?name=56e578b8fc9105b44be2562c63f829028f11bc16)記錄。 –

+0

@Andreas是的,你是對的。 ;) –

+1

@ArnaudBouchez:你的代碼不會工作。調用'THookedForm.HookedDoCreate'中的'DoCreate'將再次調用'THookedForm.HookedDoCreate',它會重複調用DoCreate直到堆棧溢出。 –

-1

如果通用超基於實現是不是一種選擇,這是我wouldbe更好爲時已晚總比沒有回答:-)

  • 使用通知框架

Erik SasseNotification Service項目在Bitbucket上是有希望的。你可以得到它here。它基於TNotify

+0

通用類不是一種選擇,因爲它需要對所有現有應用程序的所有現有形式進行更改。我看不到建議的註冊事件處理程序列表如何提供幫助,爲了將消息從窗體(創建時)傳遞到框架,必須更改每個窗體(每個應用程序的窗體)。 –

2
// Arnaud Bouchez provided great code, but he cut some important pieces of own code. 
// And what is more important - he didn't try to run it even once before posting :) 
// There is correct unit (copy-pasted from another project & tested with XE6/Win.x32) 
// It works for Windows x32 and x64 platforms. 

unit HookCreateFrm; 

interface 

implementation 

uses 
    Windows, Classes, Forms, IdGlobal, SysUtils; 

type 
    THookedForm = class(TCustomForm) 
    procedure HookedDoCreate; 
    end; 

    THookedFrame = class(TCustomFrame) 
    constructor Create(AOwner: TComponent); override; 
    end; 

    PPatchEvent = ^TPatchEvent; 
    // asm opcode hack to patch an existing routine 
    TPatchEvent = packed record 
    Jump: byte; 
    Offset: integer; 
    end; 

var 
    PatchForm, OriginalForm: TPatchEvent; 
    PatchPositionForm: PPatchEvent = nil; 
    PatchFrame, OriginalFrame: TPatchEvent; 
    PatchPositionFrame: PPatchEvent = nil; 

procedure PatchCreate; 
var ov: cardinal; 
begin 
    // hook TForm: 
    PatchPositionForm := PPatchEvent(@THookedForm.DoCreate); 
    OriginalForm := PatchPositionForm^; 
    PatchForm.Jump := $E9; // Jmp opcode 
    PatchForm.Offset := PByte(@THookedForm.HookedDoCreate)-PByte(PatchPositionForm)-5; 
    if not VirtualProtect(PatchPositionForm, 5, PAGE_EXECUTE_READWRITE, @ov) then 
    RaiseLastOSError; 
    PatchPositionForm^ := PatchForm; // enable Hook 
    // hook TFrame: 
    PatchPositionFrame := PPatchEvent(@TCustomFrame.Create); 
    OriginalFrame := PatchPositionFrame^; 
    PatchFrame.Jump := $E9; // Jmp opcode 
    PatchFrame.Offset := PByte(@THookedFrame.Create)-PByte(PatchPositionFrame)-5; 
    if not VirtualProtect(PatchPositionFrame, 5, PAGE_EXECUTE_READWRITE, @ov) then 
    RaiseLastOSError; 
    PatchPositionFrame^ := PatchFrame; // enable Hook 
end; 

// hook logic was inspired from GetText() 

{ THookedForm } 

procedure THookedForm.HookedDoCreate; 
begin 

    // do what you want before original DoCreate 

    PatchPositionForm^ := OriginalForm; 
    try 
    DoCreate; 
    finally 
    PatchPositionForm^ := PatchForm; 
    end; 

    // do what you want after original DoCreate 

end; 

{ THookedFrame } 

constructor THookedFrame.Create(AOwner: TComponent); 
begin 

    // do what you want before original DoCreate 

    PatchPositionFrame^ := OriginalFrame; 
    try 
    inherited Create(AOwner); 
    finally 
    PatchPositionFrame^ := PatchFrame; 
    end; 

    // do what you want after original Create 

end; 

initialization 
    PatchCreate; 

end. 
相關問題