Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sending and receiving messages between windows

Programming

Sending and receiving messages between windows

by  qednick  Posted    (Edited  )
This FAQ is the result of particulary long thread on the subject of posting and receiving messages between windows in a program. The main reason, I have seen many requests for help on this subject before in this forum BUT there are some very powerful features which VC++ users should be aware of in addition to simple message routines - such as the ability to not only send a 'message' but actually attach objects to the message for retrieval in a different window.
In any VC++ program, messages are bouncing around everywhere all the time between the different parts of the program. For example, when a user clicks the 'close box' of a window, a WM_CLOSE message is sent to different parts of the program which, in turn, enables those different parts to process the closing window accordingly.

However, you can actually post your own 'custom' messages between windows when the need arises. This helps with the OOP model ofeffectively 'doing-away' with global variables.

Suppose, for example, you have an MDI project. Your project will have a Main Frame window plus any number of document windows open at any time. Now, suppose also you have a dialog in the program which displays a list (database if you like) of all available documents. The database contains a ListCtrl item which displays the doc name and creation date. When the user creates a new document the database obviously needs updating to reflect the new doc.
This works fine with only one drawback: if the database dialog is open when the new document is created, the changes are not reflected in the database dialog until you close the dialog and open it back up (effectively re-loading all DB entries). What we need is some way of knowing that a new document has been created PLUS we also need to know the title and creation date of the document.
Many new programmers will immediately think of using some kind of global variable - but this defeats the whole point of OOP (Object Orientated Programming) where each class looks after itself and is not dependent on global objects flying around.

In cometh the messaging system and the [color blue]
Code:
CWnd::PostMessage()
[/color] function.

Here's how it works: In your document class you can override the OnNewDocument() function and post a message to your CMainFrame code.

Code:
BOOL CMyDoc::OnNewDocument()
{
[color red]
Code:
// Get a pointer to the main frame window
[/color]
Code:
   CWnd*  pMainFrame = ::AfxGetMainWnd();

[color red]
Code:
// Now post a message to the main frame
[/color]
Code:
   pMainFrame->PostMessage(WM_USER,NULL,NULL);
}

Now in your CMainFrame code you can use class wizard to create a message handler for the PreTranslateMessage() function. In there you will be looking to intercept messages of type WM_USER. If you catch one, you know a new document had just been created:

Code:
BOOL MainFrame::PreTranslateMessage(MSG* pMsg)
{
[color red]
Code:
// catch any WM_USER messages
[/color]
Code:
   if (pMsg->message == WM_USER)
   {
[color red]
Code:
// we know a new doc has been created!
[/color]
Code:
   }
}


Now, this is a message in it's simplest form. There are two extra parameters in the
Code:
PostMessage()
function which we can take advantage of: [color blue]wParam[/color] and [color blue]lParam[/color]. These two parameters are effectively of type integer. Therefore you can further enhance your message processing capabilities at the catching end end by passing in numerical codes:

[color red]
Code:
// Defines go in the header file!
[/color]
Code:
#define   kMsg_Open   1234
#define   kMsg_Save   1235
#define   kMsg_New    1236
[color red]
Code:
// An example would be to send this message from the 
// document's OnSaveDocument() override
[/color]
Code:
pMainFrame->PostMessage(WM_USER,kMsg_Save,NULL);

When it comes to catching the message in your Main Frame code, you can test each part of the message:

Code:
BOOL MainFrame::PreTranslateMessage(MSG* pMsg)
{
   if (pMsg->message == WM_USER &&
         pMsg->wParam == kMsg_Save && 
            pMsg->lParam == NULL)
   {
[color red]
Code:
// We know a doc has been saved
[/color]
Code:
   }
}

Now for the most POWERFUL part of the message: You can actually pass a pointer to any object along with message. Recall that a pointer is just a numerical address in memory for the object. Both the wParam and lParam fields of a message are numercial integers so we can store some object in there and pass it around in a message!

Assume you have created a class called
Code:
MyClass
which stores the document's creation date and title. Your document's
Code:
OnSaveDocument()
could look like this:

Code:
BOOL CMyDoc::OnSaveDocument()
{
   MyClass    anObject;
   pMainFrame->PostMessage(WM_USER,kMsg_Save,(int)&anObject);
}

Notice how we typecast the object to type (int) and pass in the address of the object (hence the '&' ampersand symbol).
Now, in your MainFrame code, you can catch both the message and a pointer to anObject in the code:

Code:
BOOL MainFrame::PreTranslateMessage(MSG* pMsg)
{
   if (pMsg->message == WM_USER && 
         pMsg->wParam == kMsg_Save)
   {
      MyClass*  pAnObject = (MyClass*)pMsg->lParam;
   }
}

Notice we typecast the lParam parameter back to type MyClass* so now we have a pointer to the original object.

Going back to the original plan, we have caught the message and a pointer to the object. We now need to relay this to our database dialog for updating. If you've followed me so far you should know how to do this:

Assume the dialog is a child of the main frame, therefore let's assume our dialog is called
Code:
m_DBDialog
and is a member of the MainFrame class.
You would simply relay the message on to the dialog:

Code:
BOOL MainFrame::PreTranslateMessage(MSG* pMsg)
{
   if (pMsg->message == WM_USER && 
         pMsg->wParam == kMsg_Save)
   {
      MyClass*  pAnObject = (MyClass*)pMsg->lParam;
[color red]
Code:
// relay the message and pointer to database dialog
[/color]
Code:
      m_DBDialog->PostMessage(WM_USER,kMsg_Save,(int)pAnObject);
   }
}

You would then catch the message in your dialog's
Code:
PreTranslateMessage()
function in the same manner as you did in the MainFrame code!

Cool no????
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top