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
}