Independent Message Handlers in Window Message Processing

.

Picture 1. Dialog with FrameBounder and LogoPainter Message Handlers attached to it.

Content:

The Article consists of two parts. The Part 1 contains an example of CFrameBounder which guards the size of window and doesn't allow to exceed it's maximal and minimal sizes. In the implementation of CFrameBounder the special approach was used. This approach of Independent Message Handlers (IMH) is described in Part 2 of the article. CFrameBounder is a helpful class itself (both with base class CMsgHandler) and if you don't want to penetrate into IMH approach you can omit Part 2 of the article.

Part 1. Class CFrameBounder.

CFrameBounder is a class which guards the size of window and doesn't allow to exceed it's maximal and minimal sizes.

How to use CFrameBounder:

It's enough to add one substantial line of a code in your application, which initializes CFrameBounder:

#include "FrmBnd.h"

class CBndWnd : public CWnd
{
  CFrameBounder Bnd;
  //...
}
   
int CBndWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;
  
  // add initialization
  Bnd.Initialize(m_hWnd,CSize(100,100),CSize(200,200));
}

Implementation:

CFrameBounder is derived from class CMsgHandler which changes the WndProc of the window as follows (picture 2). The message processed by new WndProc is passed to the PreWndProc function of the first message handler. If PreWndProc processes the message (returns 1), then processing is completed. Otherwise message is passed to the the PreWndProc function of the next message handler etc. The last message handler passes the message to the old WndProc. After that the message is passed to PostWndProc functions of the message handlers in the back order. This chain can be broken if any WndProc function returns 1. PreWndProc and PostWndProc are the dummy virtual functions of class CMsgHandler

Picture 2. WndProc substituted by CMsgHandler class

CFrameBounder overrides PreWndProc function as follows:

LRESULT CFrameBounder::PreWndProc(HWND hWnd, 
                                  UINT uMsg, 
                                  WPARAM wParam, 
                                  LPARAM lParam)
{
  if(uMsg==WM_SIZING)
  {
    // adjust rect stored in lParam
    //..
    
    wndProc(hWnd,uMsg,wParam,lParam);
    return 1;
  }
  return 0;
}

If it is required to add in WndProc special message handler or message filter, you have to derive your class from CMsgHandler and override PreWndProc and PostWndProc functions according to the specificity.

Part 2. IMH approach.

On the first sight it can seem, that CFrameBounder is unnecessary. A person who uses MFC can say - I can do without CFrameBounder. If I have CMyWnd class derived from CWnd and it has some size restrictions, than I can use ClassWizard to handle WM_SIZING message by adding some code like given below and it'll be ok.

void CMyWnd::OnSizing(UINT fwSide, LPRECT pRect) 
{
  // adjust pRect (25 lines of code)
  // ..
  
  CWnd::OnSizing(fwSide, pRect);
}

This method is not quite right because it leads to duplication of a code. Each our CWnd derived class with size restrictions will contain a copy of this 25 lines of code. Then this person can derive class CBoundWnd from CWnd with the specific sizing behavior implemented in it. After that he can derive all the classes with size restrictions from CBoundWnd believing that he will avoid duplication.

class CBoundWnd : public CWnd;
class CMyFirstWnd : public CBoundWnd;
class CMySecondWnd : public CBoundWnd;

It is also not the right method according to the following reasons.
a) If CMyWnd have to be derived from CFrameWnd or CDialog, then we have also implement CBoundFrameWnd and CBoundDialog classes.
b) If we have several standard functionalities such as size restrictions, restoring window position after application restarts or filtration of keyboard messages, then inheritance leads to combinatorial growth of classes (picture 3). Also MFC has some problems with multiple inheritance of CWnd derived classes.

Picture 3. Combinatorial growth of classes.

IMH approach suggests to attach a set of standard message handlers to the given window. Each handler will handle some messages in the special way. Those messages which are not handled by one handler are passed to the next message handler. (pattern "Chain of responsibility")

Example:

Class CMyWnd has 3 standard independent message handlers attached to it:

  1. CFrameBounder sets size restrictions.
  2. CLogoPainter paints the logo text after WM_PAINT message of the window was processed.
  3. CKbFilter filter keyboard messages passed to the window
class CMyWnd : public CWnd
{
  CFrameBounder Bnd;
  CLogoPainter Pnt;
  CKbFilter Flt;
  //...
}

int CMyWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  Bnd.Initialize(m_hWnd,CSize(100,100),CSize(200,200));
  Pnt.Initialize(m_hWnd,_T("Logo"),CRect(0,0,100,50));
  Flt.Initialize(m_hWnd);
}

In the example given above the message handlers are included to CMyWnd as members for simplicity. It's also possible to attach and detach handlers to the window dynamically at run time.

References:

Microsoft Systems Journal - Paul DiLascia - More Fun With MFC: DIBs, Palettes, Subclassing and a Gamut of Goodies, Part II

In this article CMsgHook class has the same functionality as CMsgHandler. As for me, the CMsgHook class has three lacks: a) It is MFC based. b) It is impossible to unhook CMsgHook class from the window. Unhooking is required to change window behavior at Run Time. c) In some cases it is required to hook message after it was handled. CLogoPainter is an example of the class which hooks WM_PAINT message after it was already handled. It is possible but not so elegant to handle postprocess messages in preprocess functions.

Resume:

The IMH approach has the following advantages:

  1. It allows to avoid code duplication while implementing standard window functionality.
  2. IMH approach suppresses combinatorial growth of classes with standard elements of behavior.
  3. Message Handlers use only Win32 API - it is sufficient to pass HWND to the handler.
  4. It allows to change dynamically the behavior of the window at run time.
  5. IMH approach is simple in use.

About Demo:

The demo project shows how to attach and detach CMsgHandler derived classes to the window at Run Time on the example of CFrameBounder and CLogoPainter classes.

Downloads

Download demo project - 16 Kb
Download source - 5 Kb


Comments

  • Is it simple ?

    Posted by Legacy on 08/08/2003 12:00am

    Originally posted by: Mirror


    this simple
    but i don't test it in CDialog.
    but in CFrameWnd it's work.
    CFrameWnd and CDialog have one parent CWnd.

    WindowProc(UINT message, LPARAM lParam, WPARAM wParam)
    {
    if (message == WM_GETMINMAXINFO)
    {
    MINMAXINFO* mx = (MINMAXINFO*) lParam;
    if (mx)
    {
    mx->ptMinTrackSize = CPoint(640,480);
    }
    }
    }

    Reply
  • May be useful

    Posted by Legacy on 09/24/2001 12:00am

    Originally posted by: George

    Adding the lightweight class to the window object
    which change the window behaviour is good
    in some cases.

    Reply
  • Nice work

    Posted by Legacy on 09/11/2001 12:00am

    Originally posted by: Compiler


    This is a great way to handle a lot of stuff that is currently done via derivation. Lots of advantages, few drawbacks.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

  • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds