Sending messages across threads
It is often a Bad Idea to send messages across threads. The problems of potential
deadlock are substantial. If you SendMessage to a thread that is blocked,
and that thread is waiting for the sending thread to complete, you're dead in
the water. Your process is blocked, and it will stay blocked, and you will have
to set of sticks of dynamite under it to make it go away.
Note that you can get into this inadvertently. You should never, ever manipulate
a GUI object from a worker thread, or a GUI object owned by a thread other than
the user-interface thread that is sending the message. If you are getting deadlock,
these are key problems to look for.
You are safest, when transferring information across threads, to use PostMessage
to handle it. A cross-thread PostMessage will not block the sender. If
you need a positive acknowledgement, it is often best to restructure your algorithm
to be a two-phase algorithm, where one method posts a message to the alternate
thread, and expects the alternate thread to post a message back indicating completion.
While harder to program, it avoids the deadlock issue.
If you must send across threads, and you need a positive response, and you
have the potential for deadlock, you should use SendMessageTimeout. This
will send the message to the thread, but if the thread does not respond within
the timeout period, the message is completed, and you get control back, with
an error indication. A typical call I use looks like the example below.
// The following replaces the unconditional send
// result = wnd->SendMessage(UWM_QUERY_SOMETHING);
//
DWORD result;
if(!SendMessageTimeout(wnd->m_hWnd, // target window
UWM_QUERY_SOMETHING, // message
0, // WPARAM
0, // LPARAM
SMTO_ABORTIFHUNG |
SMTO_NORMAL,
TIMEOUT_INTERVAL,
&result))
{ /* error or timed out */
// take some appropriate action on timeout or failure
// if we care, we can distinguish timeout from other
// errors
if(::GetLastError() == 0)
{ /* time out */
// take timeout action
} /* time out */
else
( /* other error */
// take error action
} /* other error */
} /* error or timed out */
else
{ /* successful */
// decode the result
switch(result)
{ /* result */
case ...:
break;
case ...:
break;
} /* result */
} /* successful */
Sending Messages Across Processes
Sending a user-defined message across processes is somewhat more complex. First,
you really have to use a Registered Window Message. Using a WM_APP-based
message is somewhere between seriously dangerous and totally insane.
When you send a message across processes, you are implicitly sending it across
threads. All of the caveats about cross-thread messages apply. But even more,
there are other serious restrictions on cross-process messages.
The most significant one is that you cannot send a pointer across process boundaries.
This is because process address spaces are separate, and a pointer has no meaning
when it is received in the other process. For example,
LRESULT CMainFrame::OnLogMessage(WPARAM, LPARAM lParam)
{
CString * s = (CString *)lParam;
if(s[0] == _T('$')) // app crashes hard here
{ /* special message */
// ...
} /* special message */
}
When the operation s[0] is performed, the chances are almost dead certainty
that the application receiving the message will take an access fault. The chances
of the pointer being valid (and if it is, it will point to meaningless gibberish)
are close to zero, and the gibberish pointed to will certainly not resemble a
CString.
You can't even pass a pointer to shared memory, even DLL-shared-segment shared
memory. This is because the shared memory is not guaranteed to be in the same
locations in all processes that share the memory (this is described in detail
in Win32 Programming). Essentially, figure that
you can't pass information across the process boundary using ordinary messages.