Re: MFC user-defined message vs. ptr to Wnd
On Thu, 23 Oct 2008 11:32:01 -0700, Jimbo_Jimbob_Jiminator
<JimboJimbobJiminator@discussions.microsoft.com> wrote:
I have been getting my feet wet with the Windows programming thing (as you
have probably noticed from my recent posts!!). I usually do bare-bones
embedded programming.
Anyway, a couple of weeks back I posted about getting a pointer to the
parent of the parent (grandparent of the window?), so that I could have
property pages access a file that far back. I learned several ways to do
that. Also, what came out of that discussion was the fact that the best way
would be to decouple the child (grandchild) and message back to the
grandparent.
I put a test case together and I was able to do that. I had to include the
header for the grandparent so that I could get a pointer to hWnd of that
class type. This hWnd has to go as part of the message.
This, then, brings up the architectural semantics of why is messaging is so
much better. Yes, you don't really expose the inner data and structure of the
grandparent class, this is a plus. However, the need to include the header
file of the grandparent into the child (grandchild?) and then be able to get
a pointer to that object means that it is NOT as decoupled as I had hoped. If
I have to have a pointer to grandparent, then why not just access the member
functions. I mean, I'm only one step away at that point.
It seems that the grandparent has the header file of the grandchild (can't
really get around that) and the child has the header file of the grandparent
and a pointer to the object to obtain the hWnd.
Is this as decoupled as it gets?
No, if you're using raw Windows messages, you just need HWNDs, and you can
get those with functions such as GetParent. You don't need to include the
header files for (say) the parent and grandparent classes to get their
HWNDs. (Actually, you'd typically get their CWnd* and use GetSafeHwnd or
just refer to their public m_hWnd member.) Maybe it would help to review
what a Windows message is, how MFC extracts a nicer form of OOP from it,
and then look at why an MFC program might throw it all away and implement a
custom Windows message.
A Windows message is a set of untyped function parameters, an HWND
specifying a window of some unknown type, a UINT specifying the message
number, a WPARAM which can mean anything, and an LPARAM which can mean
anything. Actually, the message number also can mean anything, as its
interpretation depends on the window that receives the message. It's just a
big mess of untyped data. Ignoring interthread SendMessage semantics and
focusing only on intrathread SendMessage, SendMessage amounts to a regular
function call to the window procedure associated with the HWND passed to
SendMessage, and since the WPARAM and LPARAM can be pointers, a Windows
message can be made to represent any set of function parameters. The window
procedure can decode (or "crack") these parameters and pass them on to
specific functions in its switch statement, which maps message numbers to
whatever it is the messages they represent do. When you look at the HWND as
a sort of "this" pointer and consider that window procedures can be
subclassed, this can be recognized as a very low-level, almost typeless
form of OOP that can implemented in languages that don't directly support
OOP, such as C.
In C++, you work with classes and objects, and MFC's CWnd, message maps,
and so forth turn the Windows message model into something that
approximates a true OOP design. So, why would you ever want to go backwards
and implement Windows-style messages in an MFC program? There are several
reasons.
1. You want to use PostMessage, which posts a message to a message queue
for later consumption by a message retrieval function, usually in a message
loop.
2. You want to write your own Window class (in the Windows, not C++ sense),
and you want non-MFC clients to be able to use it.
3. You have a message that several windows of different types could use.
4. You have a message that, say, a child needs to sent to its parent.
Items (1) and (2) require you to use Windows messages, but (3) and (4) do
not. These latter two items could be satisfied by calling a member function
belonging to the CWnd-derived class of the target window. However, this
creates the undesirable coupling you mentioned, because the target window
class definition would be required. A better way would be to define an
interface class, which contains nothing but pure virtual function
declarations, that derived classes implement. Then the child window would
call the appropriate function through the interface, dynamic_cast'ing the
generic CWnd* for the parent to the relevant Interface*. (Alternatively,
the child window could take a reference to the interface as a constructor
parameter.) This way, you would not require the class definition for the
target window, the coupling is avoided, and you get the type safety of C++
member functions. However, this is not commonly done, perhaps because it
seems like more work, is not well known, people don't like to see long
lists of interfaces, adding an interface means recompiling everything that
uses the class, etc. Probably the biggest advantage to implementing a
custom Windows message is that it allow you to use PostMessage, but there
are others, such as enabling you to use CWnd::SendMessageToDescendants and
other unusual functions like it.
--
Doug Harrison
Visual C++ MVP