We need you!

We're working hard on the next version of Developer Fusion. Let us know what you think we should be up to!

Members

Technology Zones

Articles

Hosted By

MaximumASP

Info

Rated
Read 22,191 times

Contents

Related Categories

Message Management - Sending messages across threads

flounder

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.

Comments

  • Posted by hadidi on 26 Jan 2004

    Thx a lot for this useful tip and grats for your clear explanation.

    Best Regards,

    Hadi

  • User defined messages

    Posted by velavar on 08 Sep 2003

    Nice working stuff.. very good information to use...