3
votes

Is there a failsafe way of freeing a Delphi control?

I have a TStringGrid descendent which I am "embedding" a custom control in it for an inplace editor. When the user navigates within the cells of the grid via the tab key or arrow keys, I need to create a dynamic control if the cell is editable. I have hooked the needed events and am utilizing the OnKeyDown event of my custom control to pass the navigation keys back to the parent TStringGrid.

Previously, the TStringGrid descendent would simply call FreeAndNil on the embedded control, but under some circumstances this would cause access violations inside of UpdateUIState/GetParentForm. Looking at the call stack, it appears that sometimes after the control was free'd, a WM_KEYDOWN (TWinControl.WMKeyDown) message was still happening.

I've all ready looked at and implemented the changes discussed in How to free control inside its event handler?. This appears to have resolved the issue, but I am wondering if there are any other cavet's to this approach.

In effect, this workaround has simply delayed the destruction of the control until after all the existing messages on the queue at the time the CM_RELEASE message was posted.

Would it not be possible that after the CM_RELEASE was posted, another WM_KEY* or similar message could all ready have been posted to the message queue?

My current CM_RELEASE handler looks like:

procedure TMyCustomControl.HandleRelease(var Msg: TMessage);
begin
  Free;
end;

So, will this be safe in all instances or should I do something to clear any other messages from the queue? ( SendMessage(Self.Handle, WM_DESTROY, 0, 0) comes to mind )

2
in a similar situation I don't create and free the embedded component every time I need it. Instead, I create it at init time and then make it visible/invisible as needed.PA.
should I do something to clear any other messages from the queue? No, the messages are dispatched from Parent to children, AFAIK.Trinidad
CodeInChaos's answer is closer than that of Andreas, as I have no issues with my composite control itself. While I understand the design of creating and freeing controls is sub-optimal, it is needed due to the way the cells are edited. This custom control ties into a constraint engine, and is not easily rewired to other cell values. I guess I really want to know if there is any means of removing any messages that may be queued after the CM_RELEASE message. I certainly want to avoid Application.ProcessMessages if possible, but that would have the effect of clearing out any existing messages.jchoover

2 Answers

0
votes

In general you should not destroy a control in an event-handler of that control.

But since your function is a plain non virtual message handler which is never called from internal code in that control you should be ok. I don't like too too much from a style point of view, but I think it's ok for your use-case.

But a custom message might be cleaner.

Would it not be possible that after the CM_RELEASE was posted, another WM_KEY* or similar message could all ready have been posted to the message queue?

If messages in queue would cause big problems you could never safely destroy a control since messages can be posted from other threads and applications. Just make sure the correct functioning of your application doesn't depends on those messages being handles in every case.

0
votes

SendMessage sends the message and wait for it to return, that's why you can not use it safely in the event handler of the control you are freeing.

PostMessage in the other hand, will send the message and will be processed after the event exits (if there are no more code in the event).