Defining a custom *constructor* (not initializer!)

Mehrdad wfunction at hotmail.com
Mon May 7 16:00:22 PDT 2012


On Monday, 7 May 2012 at 21:07:15 UTC, Steven Schveighoffer wrote:
> I guess I don't really understand that.  Who is responsible for 
> cleaning up your class instance?  The way I was understanding 
> your description, I thought it was the C window runtime calling 
> a callback you provide to it.  Why do you need to have the GC 
> clean it up?


Ah, I see why that's confusing.

Here's a (hopefully better) explanation:

Just as with any other object that represents an outside resource 
(e.g. a File/Stream/whatever), the lifetime of the unmanaged 
object should always follow the lifetime of the managed object.

In other words, this means that the creation of a Window object 
MUST be associated with the system call CreateWindow() (which in 
turns calls the window dispatcher function, WndProc, with the 
message WM_CREATE).

And, more importantly, this means that if the GC collects the 
Window object, then DestroyWindow() MUST be called on the kernel 
object, so that the window handle doesn't get leaked.

Just as with any other resource.


The trouble is that, as-is, this behavior is NOT implementable 
with a simple Window class whose constructor calls CreateWindow() 
and whose destructor calls DestroyWindow().

Why? Because if you were to do that in the constructor or 
destructor, the system would call back your WndProc() function, 
which is a *virtual* function meant to be overridden in the 
derived classes (so that they can handle events, such as the 
creation/destruction of the window, or the calculation of the 
window size, etc. properly).

That would mean your WndProc() in the derived instance would be 
called *before* the constructor of the derived instance is 
called, which is obviously not what you want.

Ditto with the destructor -- if you were to call DestroyWindow() 
in the ~Window(), then it would call back WndProc with the 
message WM_DESTROY in the derived class.



The only solution I see is to somehow call DestroyWindow() 
*BEFORE* you call the destructor, and call the destructor 
manually when the WM_DESTROY message is received. Ditto with the 
constructor: you need to somehow call CreateWindow() *BEFORE* you 
call the constructor, so that when you receive the WM_CREATE 
message, you can call the constructor manually.

Only by hijacking the construction and destruction call can you 
guarantee that the construction/destruction happens in an orderly 
fashion, i.e. that the kernel object lifetime tracks the user 
object lifetime properly. Otherwise you either miss getting some 
notifications at critical points (which results in incorrect 
code, which some people may or may not care about), or you risk 
getting a handle leak (also obviously bad).

Or you risk making the design pattern pretty horrific, kind of 
like how C# has a "NativeWindow" inside of the Control class, and 
they both have creation parameters, and they both register 
themselves ("park" themselves) globally on a static/shared field, 
and run into all sorts of ugly issues that you shouldn't need to 
run into. (If you look at the code and understand what they're 
doing, you'll see what I mean when I say it's ugly...)


Summary: Yes, I need the GC so I can manage the HWND lifetime 
properly, i.e. so it tracks the lifetime of the Window object 
(otherwise you can easily leak a handle). But in order to do 
this, I also need to be able to call the constructor/destructor 
manually, because the C code does this through a callback, which 
isn't possible when your object is being GC'd, due to virtual 
method re-entrancy issues with finalization.


Does that make sense? Is any part of it still unclear?


More information about the Digitalmars-d mailing list