Message Management

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.

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.

“There are only two kinds of languages: the ones people complain about and the ones nobody uses” - Bjarne Stroustrup