views:

312

answers:

3

I want to make a custom VCL control that wraps a SDL rendering surface via the SDL_CreateWindowFrom function. SDL_CreateWindowFrom takes an existing HWND handle and puts a high-performance rendering context (it has several backends available, including DirectX and OpenGL) onto it.

The helpfile says "Do not refer to the Handle property during component creation or streaming." But it doesn't say why. It says that the first time you try to access the Handle property, it'll call HandleNeeded to ensure that a valid handle exists.

So I have two questions. 1: What's the reason why you shouldn't reference the Handle property during component creation? 2. If the entire point of the control is to wrap a rendering surface that requires a HWND to be initialized, when is it safe to perform the initialization that (ideally) ought to be taking place during creation/streaming?

+2  A: 

To answer your second question: Assuming your control is a TCustomControl you should probably override CreateWindowHandle(). This has the benefit that all initialization is correctly repeated every time a new window handle is created for the control. This allows to change some window style flags that can not be set or reset without recreating the window. It does also allow to conserve resources by freeing the handle when it is not needed, and recreating it later.

mghie
So I'll want to override CreateWnd and DestroyWnd... anything else?
Mason Wheeler
+12  A: 

At it's core, it's a performance thing. There are potentially other "bad" side-effects that can happen as well since during the streaming process. Things are in "mid-construction" and all that is normally expected to be there are probably not.

When you reference the "Handle" property, this will initiate the handle creation process. This is because reading Handle actually calls GetHandle. Do this too soon in the streaming process, and you may end up with, at best, slower streaming performance, at worse, a partially configured "handle."

If you need to refer to the Handle properly from within a property setter, you should check if the handle has been created by checking HandleAllocated, and only then do you reference it. If you needed to make some flag changes to the handle like calling SetWindowLong() or something, then you should "cache" that state in the component instance and then override CreateWnd and apply those settings at that point. Another option is to defer all handle access while streaming (if csLoading in ComponentState then) until the Loaded virtual method is called.

Finally, you need to be aware of cases where your handle may need to get recreated. This can happen if the surrounding form or the parent component's handle goes through a recreate process. Up until more recent releases of Windows, the only way to change some window flags was to destroy the handle and recreate with new flags in the CreateWindowEx() call. There are many components that still do this. You know if you're in a recreate situation by checking (csRecreating in ControlState).

So to directly answer your question, the best place is to override CreateWnd and do your work in there. CreateWnd will only be called when the handle gets created. A properly designed component should get only one call to CreateWnd right before it is going to be shown.

Allen Bauer
A: 

See also this question whats-the-difference-between-createwnd-and-createwindowhandle and even more the detailed answers on what to do and when...

François