Why do we even have the DefWindowProc function?


Some time ago, I looked at two ways of reimplementing the dialog procedure (method 1, method 2). Commenter "8" wondered why we have a DefWindowProc function at all. Couldn't window procedures have followed the dialog box model, where they simply return FALSE to indicate that they want default processing to occur? Then there would be no need to export the DefWindowProc function.

This overlooks one key pattern for derived classes: Using the base class as a subroutine. That pattern is what prompted people to ask for dialog procedures that acted like window procedures. If you use the "Return FALSE to get default behavior" pattern, window procedures would go something like this:

BOOL DialogLikeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 switch (uMsg) {
 ... handle messages and return TRUE ...
 }
 // We didn't have any special processing; do the default thing
 return FALSE;
}

Similarly, subclassing in this hypothetical world would go like this:

BOOL DialogLikeSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 switch (uMsg) {
 ... handle messages and return TRUE ...
 }
 // We didn't have any special processing; let the base class try
 CallDialogLikeWindowProc(PrevDialogLikeWndProc, hwnd, uMsg, wParam, lParam);
}

This works as long as what you want to do is override the base class behavior entirely. But what if you just want to augment it? Calling the previous window procedure is analogous to calling the base class implementation from a derived class, and doing so is quite common in object-oriented programming, where you want the derived class to behave "mostly" like the base class. Consider, for example, the case where we want to allow the user to drag a window by grabbing anywhere in the client area:

LRESULT CALLBACK CaptionDragWndProc(
    HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 LRESULT lres;

 switch (uMsg) {
 case WM_NCHITTEST:
  lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
  if (lres == HTCLIENT) lres = HTCAPTION;
  return lres;
 ...
 }
 return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

We want our hit-testing to behave just like normal, with the only exception that clicks in the client area should be treated as clicks on the caption. With the DefWindowProc model, we can do this by calling DefWindowProc to do the default processing, and then modifying the result on the back end. If we had use the dialog-box-like model, there would have been no way to call the "default handler" as a subroutine in order to make it to the heavy lifting. We would be forced to do all the work or none of it.

Another avenue that an explicit DefWindowProc function opens up is modifying messages before they reach the default handler. For example, suppose you have a read-only edit control, but you want it to look like a normal edit control instead of getting the static look. You can do this by modifying the message that you pass to DefWindowProc:

...
 case WM_CTLCOLORSTATIC:
  if (GET_WM_CTLCOLOR_HWND(wParam, lParam) == m_hwndEdit)
  {
   // give it the "edit" look
   return DefWindowProc(hwnd, WM_CTLCOLOREDIT, wParam, lParam);
  }
  ...

Another common operation is changing one color attribute of an edit control while leaving the others intact. For this, you can use DefWindowProc as a subroutine and then tweak the one attribute you want to customize.

 case WM_CTLCOLORSTATIC:
  if (GET_WM_CTLCOLOR_HWND(wParam, lParam) == m_hwndDanger)
  {
   // Start with the default color attributes
   LRESULT lres = DefWindowProc(hwnd, uMsg, wParam, lParam);
   // Change text color to red; leave everything else the same
   SetTextColor(GET_WM_CTLCOLOR_HDC(wParam, lParam), RGB(255,0,0));
   return lres;
  }
  ...

Getting these types of operations to work with the dialog box model would be a significantly trickier undertaking.

Comments (8)
  1. Florian says:

    Sounds reasonable. So why don’t dialog procedures behave the same way? I know that it’s possible to subclass the dialog class and call DefDlgProc, but I think it’s confusing (you then have two procedures, the window procedure and the dialog procedure, and both could process messages like WM_COMMAND). It could be simpler, couldn’t it?

  2. Jonathan Wilson says:

    I am guessing that the way dialogs work (i.e. the "return false to do default processing" mechanism) dates back to the early days when dialog boxes were first implemented. Why it was done that way I dont know (but wish I did)

  3. bugreport says:

    I’ve found a bug on this blog: the search results sometimes contain bad links.  

    Repro:

    In the Search box at the top right of The Old New Thing, type "WM_QUIT".  

    Result:

    The header of the first and third results (for example) link to "http://weblogs.asp.net/…" which is a bad link.  The comment link for these blog entries, however, is correct ("http://blogs.msdn.com/…").

  4. Lauren Smith says:

    Was this a conscious decision to support this usage pattern, or is this a post-hoc justification of an early implementation detail? Not that it really makes a difference in the end…

  5. waleri says:

    >> Why it was done that way I dont know (but wish I did)

    Because you will have to create a custom class dialog for every dialog – how would you point which dialog function to use otherwise? Alternative is to subclass every created dialog – I guess at that time subclassing wasn’t invented yet.

  6. Raymond II says:

    I know, Dean, and if I reply it will only get worse off-topic; I forcefully tried to wedge it in as "related."  Now other people like me, who do a search for WM_NCCREATE and window title, *may* get some feedback on how it (mis)behaves.  I planned on getting this issue through the suggestion box, but it’s been closed each time I’ve tried, so today, before I left for work, I just crammed it in.–The information does more good than bad, even if it is slightly off-topic, at least in my view.  Mr. Chen is free to delete any post, especially one of mine, since I have a habit of drifting off-topic. blablabla

  7. Hummer says:

    From my own experience I’d say that it would have been much easier, and perhaps less complicated if the dialog box scheme had followed the window scheme, i.e. if dialog had inherited window, or vice versa.

    For me, I wrote a custom frame, from which all my window clases are derived. As I attacked the dialog problem I found myself having to consider ditching all of the dialog resource tools, in order to achieve a dialog that had a consistent look and feel, between frames and dialogs.

    Admittedly I could have implemented a solution that repeated the "window" code in the "dialog" code, but given the difficulty of getting it to work "just" right, it wasn’t something I wanted to have to repeat, or maintain as some quite intricate bugs/problems surfaced.

    Fortunately I decided not to use the windows dialog scheme. It was just luck in the end, and the additional effort of implementing a usable resource toolset paid off.

    What I didn’t know at the time was that I would have significant problems moving to a threaded environment, because of things that were happening in windows modal loops. I’m not suggesting that they couldn’t have been made to work, merely that I couldn’t figure out what happens inside them.

    Not using the windows dialog scheme meant that I didn’t have to reimplement dialogs these problems started surfacing. Implementing a popup menu capability was the last of windows modal loops for me! I find it a distinct advantage to own all of my modal loops. I wouldn’t say that it saves me time, but I have confidence that I know what is happening, and that means my code can be simple and unambiguous.

    If I use a windows common dialog, and I do, it gets it’s own thread!

    All I can hope now is that the operating system doesn’t start posting private message id’s to my main UI thread!

Comments are closed.

Skip to main content