May 11, 2012
On Mon, 07 May 2012 19:00:22 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> 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.

OK, I understand what you are saying now.

What about using NVI?

class Window
{
  protected void processMessage_impl(int message) // virtual call
  {}
  final public void processMessage(int message)
  {
    if(message == WM_CREATE)
    {
       // handle specially
    }
    else if(message == WM_DELETE)
    {
       // handle specially
    }
    else
    {
        processMessage_impl(int message);
    }
  }
  private HWND window;
  this()
  {
    window = CreateWindow(&processMessage); // or whatever mechanism, you get the idea
  }
  ~this()
  {
    DestroyWindow(window);
  }
}

-Steve
May 11, 2012
> OK, I understand what you are saying now.
>
> What about using NVI?
>
> class Window
> {
>   protected void processMessage_impl(int message) // virtual call
>   {}
>   final public void processMessage(int message)
>   {
>     if(message == WM_CREATE)
>     {
>        // handle specially
>     }
>     else if(message == WM_DELETE)
>     {
>        // handle specially
>     }
>     else
>     {
>         processMessage_impl(int message);
>     }
>   }
>   private HWND window;
>   this()
>   {
>     window = CreateWindow(&processMessage); // or whatever mechanism, you get the idea
>   }
>   ~this()
>   {
>     DestroyWindow(window);
>   }
> }
>
> -Steve

The trouble is that if you "handle these specially", then your subclasses won't....
But your subclasses MUST still be able to handle these messages, because they might need to do something special when the messages are sent. (For example, you might need to calculate the size of the window when WM_CREATE or WM_NCCREATE is sent. Or something else... there are a ton of situations that can happen -- such as MDI window creation, etc. -- that don't happen too often, but happen often enough that you simply can't assume you can get by without allowing them to handle the messages.)
May 11, 2012
On Fri, 11 May 2012 09:33:54 -0400, Mehrdad <wfunction@hotmail.com> wrote:

>> OK, I understand what you are saying now.
>>
>> What about using NVI?
>>
>> class Window
>> {
>>   protected void processMessage_impl(int message) // virtual call
>>   {}
>>   final public void processMessage(int message)
>>   {
>>     if(message == WM_CREATE)
>>     {
>>        // handle specially
>>     }
>>     else if(message == WM_DELETE)
>>     {
>>        // handle specially
>>     }
>>     else
>>     {
>>         processMessage_impl(int message);
>>     }
>>   }
>>   private HWND window;
>>   this()
>>   {
>>     window = CreateWindow(&processMessage); // or whatever mechanism, you get the idea
>>   }
>>   ~this()
>>   {
>>     DestroyWindow(window);
>>   }
>> }
>>
>> -Steve
>
> The trouble is that if you "handle these specially", then your subclasses won't....
> But your subclasses MUST still be able to handle these messages, because they might need to do something special when the messages are sent. (For example, you might need to calculate the size of the window when WM_CREATE or WM_NCCREATE is sent.

I'm probably not getting the entire picture here, but I assume from your description that CreateWindow directly calls your WndProc.  If that is the case, isn't it guaranteed that after the base ctor runs the class has received the WM_CREATE message?  I mean, the only issue happens when you are not quite through the ctor chain, no?

> Or something else... there are a ton of situations that can happen -- such as MDI window creation, etc. -- that don't happen too often, but happen often enough that you simply can't assume you can get by without allowing them to handle the messages.)

You can use another mechanism to decide.  for instance:

class Window
   private bool inCreateWindow = false;
   ...
   final public void processMessage(int message)
   {
       if(inCreateWindow && message == WM_CREATE)
       {
           // handle specially
       }
       ...
   }
   this()
   {
      inCreateWindow = true;
      window = CreateWindow(&processMessage);
      inCreateWindow = false; // all WM_CREATE messages will now go through normal mechanism.
   }
}

-Steve
May 11, 2012
> I'm probably not getting the entire picture here, but I assume from your description that CreateWindow directly calls your WndProc.  If that is the case, isn't it guaranteed that after the base ctor runs the class has received the WM_CREATE message?  I mean, the only issue happens when you are not quite through the ctor chain, no?

Right, what I'm saying is that it's the *subclass* who needs the notification more, than anyone else (although, for other reasons, the base class needs it as well).
The whole point is to get the messages to the *subclasses* properly...

>> Or something else... there are a ton of situations that can happen -- such as MDI window creation, etc. -- that don't happen too often, but happen often enough that you simply can't assume you can get by without allowing them to handle the messages.)
>
> You can use another mechanism to decide.  for instance:
>
> class Window
>    private bool inCreateWindow = false;
>    ...
>    final public void processMessage(int message)
>    {
>        if(inCreateWindow && message == WM_CREATE)
>        {
>            // handle specially
>        }
>        ...
>    }
>    this()
>    {
>       inCreateWindow = true;
>       window = CreateWindow(&processMessage);
>       inCreateWindow = false; // all WM_CREATE messages will now go through normal mechanism.
>    }
> }
>
> -Steve


May 11, 2012
On Fri, 11 May 2012 10:04:00 -0400, Mehrdad <wfunction@hotmail.com> wrote:

>> I'm probably not getting the entire picture here, but I assume from your description that CreateWindow directly calls your WndProc.  If that is the case, isn't it guaranteed that after the base ctor runs the class has received the WM_CREATE message?  I mean, the only issue happens when you are not quite through the ctor chain, no?
>
> Right, what I'm saying is that it's the *subclass* who needs the notification more, than anyone else (although, for other reasons, the base class needs it as well).
> The whole point is to get the messages to the *subclasses* properly...

Yeah, but given that the subclass is calling the base constructor, and the base constructor does not return until WM_CREATE is received, can't the subclass constructor pretend it received the message?

-Steve
May 11, 2012
On Friday, 11 May 2012 at 14:17:04 UTC, Steven Schveighoffer wrote:
> Yeah, but given that the subclass is calling the base constructor, and the base constructor does not return until WM_CREATE is received, can't the subclass constructor pretend it received the message?
>
> -Steve

"pretend"?

No, because the window handle doesn't even EXIST until WM_CREATE passes it to the window instance! (Sorry I guess I didn't mention that, but it was kind of implicit in the fact that messages are passed *before* CreateWindow() returns.)

Yes, CreateWindow() also returns a copy of the handle, but stuff happens *before* the handle is returned, and in order for the class to properly handle it, the handle is passed along with WM_CREATE (and, in fact, with another message called WM_NCCREATE, but we can ignore that for now).
The subclass has NO access to the window handle whatsoever before the message comes, so no, it can't "pretend" to have received the handle because that wouldn't be of any use.
May 11, 2012
On Friday, 11 May 2012 at 14:20:47 UTC, Mehrdad wrote:
> On Friday, 11 May 2012 at 14:17:04 UTC, Steven Schveighoffer wrote:
>> Yeah, but given that the subclass is calling the base constructor, and the base constructor does not return until WM_CREATE is received, can't the subclass constructor pretend it received the message?
>>
>> -Steve
>
> "pretend"?
>
> The subclass has NO access to the window handle whatsoever before the message comes, so no, it can't "pretend" to have received the handle because that wouldn't be of any use.


Or did you mean *after* the base class constructor is called?

If you meant that: no, that's pretty useless because the whole
point of receiving a message is to respond to it appropriately!

If you miss the actual notification you can't time-travel back
and say, "just kidding, I actually wanted to do X-Y-Z when the
message came, so can we redo this whole window creation thing?" :P

That's exactly what I was saying when I said that you can't
afford to miss a message and hope that you can just change stuff
later...
May 11, 2012
On Fri, 11 May 2012 10:31:26 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> On Friday, 11 May 2012 at 14:20:47 UTC, Mehrdad wrote:
>> On Friday, 11 May 2012 at 14:17:04 UTC, Steven Schveighoffer wrote:
>>> Yeah, but given that the subclass is calling the base constructor, and the base constructor does not return until WM_CREATE is received, can't the subclass constructor pretend it received the message?
>>>
>>> -Steve
>>
>> "pretend"?
>>
>> The subclass has NO access to the window handle whatsoever before the message comes, so no, it can't "pretend" to have received the handle because that wouldn't be of any use.
>
>
> Or did you mean *after* the base class constructor is called?
>
> If you meant that: no, that's pretty useless because the whole
> point of receiving a message is to respond to it appropriately!
>
> If you miss the actual notification you can't time-travel back
> and say, "just kidding, I actually wanted to do X-Y-Z when the
> message came, so can we redo this whole window creation thing?" :P
>
> That's exactly what I was saying when I said that you can't
> afford to miss a message and hope that you can just change stuff
> later...

Yes I did mean that.

I'm thinking about it, one thing you did not mention, but almost certainly is the case (it usually is with GUI toolkits) is that the WndProc is called in a different thread or context than the constructor.  This means, there may be functions that you can call in WndProc that you cannot call in the ctor.  So even though CreateWindow returning means WM_CREATE must have been received, the context you need to handle WM_CREATE is gone.  Is that true?

I'll wait for an answer before thinking more about this.  It feels imminently solvable...

-Steve
May 11, 2012
On Friday, 11 May 2012 at 15:23:10 UTC, Steven Schveighoffer wrote:
> Yes I did mean that.
>
> I'm thinking about it, one thing you did not mention, but almost certainly is the case (it usually is with GUI toolkits) is that the WndProc is called in a different thread or context than the constructor.

It's in the same exact thread as the caller of CreateWindow in Win32 (not sure what you mean by "context" here, but AFAIK nothing is changed about the thread... nothing fiber-like or anything like that going on).
I'm not familiar with other OSes though.

> This means, there may be functions that you can call in WndProc that you cannot call in the ctor.

> So even though CreateWindow returning means WM_CREATE must have been received, the context you need to handle WM_CREATE is gone.  Is that true?

Again, not sure what you mean by "context", but there is nothing changed about the thread here.

You could nest the creation of 10 windows down this chain of WM_CREATE's (in fact, that's one of its purposes) and it's all gonna happen for the same thread, in the same "context" (assuming that's referring to the thread context).

Furthermore, the system waits for your response, because if you return -1, the window is not created, and an error is returned to the caller. (Yet another reason why you need to be able to handle the message when it's sent, not a year later.)

>
> I'll wait for an answer before thinking more about this.  It feels imminently solvable...

Okay. :P Though I've thought about it for a while and I'm getting more and more convinced there isn't a good solution for this without the ability to do custom construction...
May 11, 2012
On Friday, 11 May 2012 at 15:36:37 UTC, Mehrdad wrote:
> It's in the same exact thread as the caller of CreateWindow in Win32

Minor correction: that's under the assumption that the thread that registered the window class (with RegisterClass()) is the same thread creating the window, which is the case most of the time.

But if it's one process instantiating the window of a different process (which happens sometimes), or if it's one thread instantiating the window of a different thread (which happens more rarely), that's obviously not the case...