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

More by Author

Must Read