Message Management

Passing pointers in messages

When you pass a pointer as a WPARAM or LPARAM, you need to be careful about what you pass and how you pass it. The key is in whether you do a SendMessage or a PostMessage. If you do a SendMessage, you can use a reference to an object on the stack, an object in static storage, or an object on the heap. This is because control does not resume in the thread that does the SendMessage until the handler completes its action. In particular, this means that any address referencing the stack remains valid during the processing of the message. This does not apply to cross-process messages! See below!

However, if you ever plan to use PostMessage to pass a pointer to an object, then you are constrained to always use a static or heap-based object. The address of a stack-based object is nonsensical in this context. This is because the function that performs the PostMessage will quite possibly return long before the message is processed--in fact, if it is posting the message to the same thread, it must return before the message is processed. This means that the object reference to the stack is pointing to valid space, but space that may have been overwritten. If the object on the stack is a C++ object that has a destructor, the destructor will be called and objects within the object on the stack might be deallocated. For example, you cannot use PostMessage in the following context:

     {
      CString s;
      // ... assign a value to s
      PostMessage(UWM_LOG_MESSAGE, 0, (LPARAM)&s);
     }

Even if the address referenced on the stack is not overwritten by subsequent calls, the data referenced by the string is deallocated by the CString destructor. When the handler is called and attempts to reference the string, you will get some effect between completely incorrect data and an access fault. The chances of anything working as you expect are incredibly slim.

However, the following code is correct. We first look at the definition:

/***************************************************************
*                       UWM_LOG_MESSAGE
* Inputs:
*       WPARAM: ignored, 0
*       LPARAM: (LPARAM)(CString *): String to log
* Result: LRESULT
*	Logically void, 0, always
* Effect:
*	Logs the message in the debug output window
* Notes:
*	The CString object must explicitly deallocated by
*	the handler for this message.
*	This message is usually sent via PostMessage. If sent
*	via SendMessage, the sender must not delete the 
*	CString, nor should it assume upon return that it has
*	not been deleted.
***************************************************************/

Then we can write the handler:

/***************************************************************
*                      CMainFrame::OnLogMessage
* Inputs:
*       WPARAM: ignored, 0
*       LPARAM: (LPARAM)(CString *): String to log
* Result: LRESULT
*	Logically void, 0, always
* Effect:
*	Logs the message in the debug output window
* Notes:
*	The CString object must explicitly deallocated by
*	the handler for this message
***************************************************************/

(Note that I often replicate the definitions in both places; while it means I have to update the handler comments if I make changes, since I have to edit the code anyway it is no serious hazard).

LRESULT CMainFrame::OnLogMessage(WPARAM, LPARAM lParam)
    {
     CString * s = (CString *)lParam;
     c_Log.AddString(*s);
     delete s;
     return 0; // logically void, value ignored
    }

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“You can stand on the shoulders of giants OR a big enough pile of dwarfs, works either way.”