Is this code valid:
TMyObj = class(Tobject)
public
FThread: TThread;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyObj.Create(AOwner: TComponent);
begin
inherited;
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
FThread.start;
end;
destructor TMyObj.Destroy;
begin
inherited;
end;
my question is after i do
destructor TMyObj.Destroy;
begin
inherited;
end;
I mean after the object TMyObj is free, what will be the value of FThread inside
FThread:= TThread.createAnonymousThread(
procedure
begin
while not FThread.CheckTerminated do sleep(10000);
end);
No, this code is not valid.
It is important to know that FThread is not captured, but the reference to the containing instance. So the anonymous method code rather looks like this:
FThread := TThread.createAnonymousThread(
procedure
begin
while not Self.FThread.CheckTerminated do sleep(10000);
end);
The compiler generates an object that implements the anonymous method with a field called Self that captures the reference to the TMyObj instance. If that instance gets destroyed while the anonymous method is alive within the thread, it will have a dangling reference.
A thread created with TThread.CreateAnonymousThread() is destroyed automatically when it finishes. So if you store that in a field for later access, you at least need to turn FreeOnTerminate off and not reference it via a captured variable to the thread itself, but rather with TThread.Current. Within the anonymous method, this will give you the thread that method is running in.
I read here about how to create a Window Handle into a non-windowed conrol. I did just the way I read, but nothing happens. So I come to you guys.
My class is this way right now:
interface
type
TMyObject = class
private
fMsgHandlerHWND : HWND;
procedure WndMethod(var Msg: TMessage);
public
constructor Create;
destructor Destroy; Override;
end;
implementation
constructor TMyObject.Create;
begin
inherited;
fMsgHandlerHWND := AllocateHWnd(WndMethod);
end;
destructor TMyObject.Destroy;
begin
deallocatehwnd(fMsgHandlerHWND);
inherited;
end;
procedure TMyObject.WndMethod(var Msg: TMessage);
begin
if Msg.Msg = WM_KEYUP then
MessageBeep(0)
else
Msg.Result := DefWindowProc(fMsgHandlerHWND, Msg.Msg, Msg.wParam, Msg.lParam);
end;
I do use my FormCreate to execute var := TMyObject.Create.
Following the line where Windows sends broadcast messages when I press/release a key (correct me if I'm wrong); I'm not sure why it did not work. Somoeone can tell me what did I do wrong? There is another way to catch KeyBoard input with a non-windowed object? If so, how?
Keyboard events are delivered to the window with input focus. That's never going to be your hidden window.
if you want to catch input events the cleanest way is to use the OnMessage event of the global Application object. All queued messages pass through this event handler. Subscribe to it using a TApplicationEvents instance.
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?
Contrary to David's answer, there is a suitable framework. It's built in higher up in the class hierarchy at TComponent. Sir Rufo is on the right track, but you don't need to force your forms to be owned by this object.
You're welcome to write any number of classes that can take specialised action when a form (or any other component for that matter) is destroyed. E.g.
TDestroyedFormLogger = class(TComponent)
protected
{ Write to log file when forms are destroyed. }
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
end;
TMenuManager = class(TComponent)
protected
{ Remove/hide a menu item corresponding to the form that has been destroyed. }
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
end;
Now whenever you create a form, simply set a notification as follows (assuming you have given yourself access to suitable instances of the above objects):
LForm := TMyForm.Create(Application);
LForm.FreeNotification(DestroyedFormLogger);
LForm.FreeNotification(MenuManager);
This approach is better than using the OnDestroy event because that permits only 1 observer, whereas FreeNotification permits any number of observers.
NOTE: As with any useful technique, don't force-fit a problem to the technique. There may be a more appropriate technique to your specific problem. E.g. The MenuManager idea might be better solved by using the global Screen object to iterate forms OnPopup.
EDIT: Explanation of Observer Pattern
The TComponent notification mechanism is a built-in implementation of the Observer Pattern for when a component is destroyed. FreeNotification (perhaps not ideally named) is the equivalent of registerObserver and RemoveNotification the equivalent of unregisterObserver.
The whole point of the observer pattern is that the subject being observed (sometimes called publisher) has no type-specific knowledge of the objects that are observing it (sometimes called subscribers). Publishers only know that they are able to call a generic notification method on each registered subscriber (observer). This allows objects to be loosely coupled from those that are watching it. In fact the publisher doesn't even need to be observed at all. Obviously the registration method needs to be called either from the subscribers themselves or from a third-party - otherwise the decoupling objective is defeated.
Observers can be implemented at varying degrees of complexity. The simplest being an event or callback. The most complex being a dispatcher that manages registrations in-between and independent of both publishers and subscribers. The dispatcher might even implement thread switching so that publishers don't even get impacted by performance side-effects of slow subscribers.
TComponent's observer implementation has a limitation that both the publisher and subscriber must inherit from TComponent. Basically any component can register with another component to be notified of its destruction.
Perhaps the most common use of this feature in Delphi is: When component A has a reference to component B; If component B is destroyed, component A is notified so that it can set its reference to nil.
What you are wanting is for the framework to fire an event when a form is destroyed. When a form is destroyed, its destructor is run. So, in order for the framework to fire such an event, it would need to be implemented from within the form's destructor. If you take a look inside TCustomForm.Destroy, you will find that there is not such event.
From this we can conclude that there can be no application wide event fired whenever a form is destroyed. This means that you will have to implement a solution yourself. One obvious way to make this happen is to introduce a common base class for all your forms. Ensure that every form in your program derives ultimately from this common base class. Then arrange for the base class to surface an event that is fired whenever an instance is destroyed.
There seems to be some mis-understanding about what I am saying above. Craig demonstrates how to subscribe to notification of a single form's destruction. The ability to do that does not contradict what I am saying. My point is that there is no mechanism in place that allows you to subscribe to receive notification when any form is destroyed.
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..
This is not the best practice (have a look at David's answer) but a way to go.
Since every form can have an owner (type TComponent) and this owner gets notified, if a child component is destroyed, just create a global form owner and pass this as the owner of every created form you want to get notified on destroy.
You have to override the TComponent.Notification method and do what you have to (e.g. fire an event)
unit GlobalViewHolder;
interface
uses
Forms,
Classes;
type
TComponentNotificationEvent = procedure( Sender : TObject; AComponent : TComponent; Operation : TOperation ) of object;
TGlobalViewHolder = class( TComponent )
private
FOnNotification : TComponentNotificationEvent;
protected
procedure Notification( AComponent : TComponent; Operation : TOperation ); override;
public
property OnNotification : TComponentNotificationEvent read FOnNotification write FOnNotification;
end;
// small and simple singleton :o)
function ViewHolder : TGlobalViewHolder;
implementation
var
_ViewHolder : TGlobalViewHolder;
function ViewHolder : TGlobalViewHolder;
begin
if not Assigned( _ViewHolder )
then
_ViewHolder := TGlobalViewHolder.Create( Application );
Result := _ViewHolder;
end;
{ TGlobalViewHolder }
procedure TGlobalViewHolder.Notification( AComponent : TComponent; Operation : TOperation );
begin
inherited;
if Assigned( OnNotification )
then
OnNotification( Self, AComponent, Operation );
end;
end.
The main form owner is always Application but there is no need to track this.
Personally I'd prefer David Heffernan's solution since all my forms are allways based on a template and it would be the cleanest and easiest to implement way.
But coming from you demand
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.
you would be able to patch Destroy to an own method.
I'd take the latest called destructor in the chain and patch TObject.Destroy to TMyClass.Destroy. The place to implement should be the project.
The code for patching is taken from David Heffernan 's answer on Patch routine call in delphi and only included to keep the answer complete, credits regarding this code go there.
program AInformOnCloseForms;
uses
Forms,
Classes,
Windows,
Dialogs,
Unit3 in 'Unit3.pas' {Mainform},
Unit4 in 'Unit4.pas' {Form2};
{$R *.res}
// PatchCode and RedirectProcedure are taken from David Heffernans answer
// https://stackoverflow.com/a/8978266/1699210
// on "Patch routine call in delphi" , credits regarding this code go there
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;
type
TMyClass=Class(TObject) // Dummy to handle "events"
public
Destructor Destroy;override;
End;
destructor TMyClass.Destroy;
begin
// pervent recursion from call to Showmessage
if (Self.InheritsFrom(TCustomForm)) and (Self.ClassName<>'TTaskMessageDialog') then
Showmessage(Self.ClassName);
end;
begin
RedirectProcedure(#TObject.Destroy,#TMyClass.Destroy);
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TMainform, Mainform);
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
As per Vlad's request this expands on my original answer by explaining how to register all forms owned by Application without any changes to the construction of each form. I.e. forms created using TMyForm.Create(Application); and by implication also Application.CreateForm(TMyForm, MyForm);.
The original answer doesn't specify any particular means of registering for FreeNotification because the options vary according to how forms are created. Since the question answered did not put any constraints on how the forms are created, the original answer is more appropriate in the general case.
If we could ensure that Application referred to a custom subclass of TApplication, the problem would be fairly easy to solve by overriding TApplication.Notification;. That's not possible, so this special case leverages the fact that the component ownership framework notifies all owned components when another component is added or removed. So basically all we need is a component tracker also owned by Application and we can react on its "sibling" notifications.
The following test case will demonstrate that new notifications work.
procedure TComponentTrackerTests.TestNewNotifications;
var
LComponentTracker: TComponentTracker;
LInitialFormCount: Integer;
LForm: TObject;
begin
LComponentTracker := TComponentTracker.Create(Application);
try
LComponentTracker.OnComponentNotification := CountOwnedForms;
LInitialFormCount := FOwnedFormCount;
LForm := TForm.Create(Application);
CheckEquals(LInitialFormCount + 1, FOwnedFormCount, 'Form added');
LForm.Free;
CheckEquals(LInitialFormCount, FOwnedFormCount, 'Form removed');
finally
LComponentTracker.Free;
end;
end;
procedure TComponentTrackerTests.CountOwnedForms(AComponent: TComponent; AOperation: TOperation);
begin
if (AComponent is TCustomForm) then
begin
case AOperation of
opInsert: Inc(FOwnedFormCount);
opRemove: Dec(FOwnedFormCount);
end;
end;
end;
TComponentTracker is implemented as follows:
TComponentNotificationEvent = procedure (AComponent: TComponent; AOperation: TOperation) of object;
TComponentTracker = class(TComponent)
private
FOnComponentNotification: TComponentNotificationEvent;
procedure SetOnComponentNotification(const Value: TComponentNotificationEvent);
procedure DoComponentNotification(AComponent: TComponent; AOperation: TOperation);
protected
procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
public
property OnComponentNotification: TComponentNotificationEvent read FOnComponentNotification write SetOnComponentNotification;
end;
procedure TComponentTracker.DoComponentNotification(AComponent: TComponent; AOperation: TOperation);
begin
if Assigned(FOnComponentNotification) then
begin
FOnComponentNotification(AComponent, AOperation);
end;
end;
procedure TComponentTracker.Notification(AComponent: TComponent; AOperation: TOperation);
begin
inherited Notification(AComponent, AOperation);
DoComponentNotification(AComponent, AOperation);
end;
procedure TComponentTracker.SetOnComponentNotification(const Value: TComponentNotificationEvent);
var
LComponent: TComponent;
begin
FOnComponentNotification := Value;
if Assigned(Value) then
begin
{ Report all currently owned components }
for LComponent in Owner do
begin
DoComponentNotification(LComponent, opInsert);
end;
end;
end;
WARNING
You could implement anything you choose in the OnComponentNotification event handler. This would include logging that the form is "destroyed". However, such a simplistic approach would actually be flawed because TComponent.InsertComponent allows a component's owner to be changed without destroying it.
Therefore to accurately report destruction, you would have to combine this with using FreeNotification as in my first answer.
This is quite easily done by setting LComponentTracker.OnComponentNotification := FDestructionLogger.RegisterFreeNotification; where RegisterFreeNotification is implemented as follows:
procedure TDestructionLogger.RegisterFreeNotification(AComponent: TComponent; AOperation: TOperation);
begin
if (AComponent is TCustomForm) then
begin
case AOperation of
opInsert: AComponent.FreeNotification(Self);
end;
end;
end;
A very simple approach could be keeping track of the Form count. When it lowers, then there is a Form destroyed. Check in Application.OnIdle:
procedure TMainForm.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
if Screen.CustomFormCount < FFormCount then
FormDestroyed;
if FFormCount <> Screen.CustomFormCount then
FFormCount := Screen.CustomFormCount;
end;
Depending on what action should be taken, you can loop through Screen.CustomForms to determine which Form was destroyed.
I started learning Delphi two days ago but I got stuck. I broke down because nothing goes my way so I decided to write here. I wanted to create class that would have a field with its own TTimer object and which will perform some action at some time interval. Is it even possible? Suppose we have such code:
Sth = class
private
public
clock:TTimer;
procedure clockTimer(Sender: TObject);
constructor Create();
end;
constructor Sth.Create()
begin
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
procedure Sth.clockTimer(Sender: TObject);
begin
//some action on this Sth object at clock.interval time...
end;
My similar code copiles but it doesn't work properly. When I call the constructor the program crashes down (access violation at line: clock.interval:=1000;). I don't know what
Sender:TObject
does but I think that's not the problem. Is it possible to create such class I want to?
You have not created the timer. Declaring a variable is not enough. You do need to create the timer.
constructor Sth.Create()
begin
clock := TTimer.Create(nil);
clock.interval:=1000;
clock.OnTimer := clockTimer;
end;
And you should destroy it too. Add a destructor to the class
destructor Destroy; override;
and implement it like this
destructor Sth.Destroy;
begin
clock.Free;
inherited;
end;
I would also recommend that you make your clock field have private visibility. It's not good to expose the internals of a class like that.
TMyClass = class
private
FClock: TTimer;
procedure ClockTimer(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
end;
....
constructor TMyClass.Create
begin
inherited;
FTimer := TTimer.Create(nil);
FTimer.Interval := 1000;
FTimer.OnTimer := ClockTimer;
end;
destructor TMyClass.Destroy;
begin
FTimer.Free;
inherited;
end;
Note that I have included calls to the inherited constructor and destructor. These are not necessary in this class since it derives directly from TObject and the constructor and destructor for TObject is empty. But if you change the inheritance at some point, and make your class derive from a different class, then you will need to do this. So, in my view, it is good practise to include these calls always.
Since anonymous methods appeared in Delphi I wanted to use them in VCL components events. Obviously for backward compatibility the VCL wasn't updated, so I managed to make a simple implementation with a few caveats.
type
TNotifyEventDispatcher = class(TComponent)
protected
FClosure: TProc<TObject>;
procedure OnNotifyEvent(Sender: TObject);
public
class function Create(Owner: TComponent; const Closure: TProc<TObject>): TNotifyEvent; overload;
function Attach(const Closure: TProc<TObject>): TNotifyEvent;
end;
implementation
class function TNotifyEventDispatcher.Create(Owner: TComponent; const Closure: TProc<TObject>): TNotifyEvent;
begin
Result := TNotifyEventDispatcher.Create(Owner).Attach(Closure)
end;
function TNotifyEventDispatcher.Attach(const Closure: TProc<TObject>): TNotifyEvent;
begin
FClosure := Closure;
Result := Self.OnNotifyEvent
end;
procedure TNotifyEventDispatcher.OnNotifyEvent(Sender: TObject);
begin
if Assigned(FClosure) then
FClosure(Sender)
end;
end.
And this is how it's used for example:
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := TNotifyEventDispatcher.Create(Self,
procedure (Sender: TObject)
begin
Self.Caption := 'DONE!'
end)
end;
Very simple I believe, there are two drawbacks:
I have to create a component to manage the lifetime of the anonymous method (I waste a bit more of memory, and it's a bit slower for the indirection, still I prefer more clear code in my applications)
I have to implement a new class (very simple) for every event signature. This one is a bit more complicated, still the VCL has very common event signatures, and for every special case when I create the class it's done forever.
What do you think of this implementation? Something to make it better?
You can take a look at my multicast event implementation in DSharp.
Then you can write code like this:
function NotifyEvent(Owner: TComponent; const Delegates: array of TProc<TObject>): TNotifyEvent; overload;
begin
Result := TEventHandler<TNotifyEvent>.Create<TProc<TObject>>(Owner, Delegates).Invoke;
end;
function NotifyEvent(Owner: TComponent; const Delegate: TProc<TObject>): TNotifyEvent; overload;
begin
Result := NotifyEvent(Owner, [Delegate]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := NotifyEvent(Button1, [
procedure(Sender: TObject)
begin
Caption := 'Started';
end,
procedure(Sender: TObject)
begin
if MessageDlg('Continue?', mtConfirmation, mbYesNo, 0) <> mrYes then
begin
Caption := 'Canceled';
Abort;
end;
end,
procedure(Sender: TObject)
begin
Caption := 'Finished';
end]);
end;
You could make TNotifyEventDispatcher to be a subclass of TInterfacedObject so you do not need to care about freeing it.
But to be more pragmatic one would use traditional event assignment that takes less lines of code and is supported by the IDE.
Interesting approach.
(Disclaimer: haven't checked this, but it is something to investigate): You may have to be careful though about what goes on in capturing the state of the method that "assigns" the anonymous method to the event. Capturing can be an advantage but can also have side effects you do not want. If your anonymous method needs info about the form at the time it is fired, it may end up with info at the time of its assignment. Update: apparently this is not the case, see comment by Stefan Glienke.
You do not really need different classes. Using overloading you could create different class Create functions that each take a specific signature and return the corresponding event handler and the compiler will sort it out.
Managing lifetime could be simplified if you derived from TInterfacedObject instead of TComponent. Reference counting should then take care of destroying the instance when it is no longer used by the form. Update: this does require keeping a reference to the instance somewhere in the form, or refcounting won't help as the instance will be freed immediately after assigning the notify event. You could add an extra parameter on the class Create functions to which you pass a method that the instance can useto add itself to some list of the form.
Side note: All in all though I have to agree with David in his comment on the question: it does sound like a lot of work for the "sole purpose" of using anonymous methods...