views:

947

answers:

3

I have a thread that reads messages from a named pipe. It is a blocking read, which is why it's in its own thread. When this thread reads a message, I want it to notify the Windows Forms message loop running in the main thread that a message is ready. How can I do that? In win32 I would do a PostMessage, but that function does not seem to exist in .Net (or at least I could not find it).

+1  A: 

PostMessage (and likewise SendMessage) are Win32 API functions, and thus are not directly associated with .NET. .NET does however have good support for interoping with the Win32 API, using P/Invoke calls.

As it seems you are new to doing Win32 programming .NET, this MSDN Magazine article provides a solid introduction on the topic.

The excellent pinvoke.net website details how to use many of these API functions from C#/VB.NET. See this page for PostMessage specifically.

The standard declaration is the following:

[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

But as the page indicates, it is wise to wrap this function to handle Win32 errors properly:

void PostMessageSafe(HandleRef hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
    bool returnValue = PostMessage(hWnd, msg, wParam, lParam);
    if(!returnValue)
    {
        // An error occured
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }
}
Noldorin
I don't necessarily want to use PostMessage. Is there no plain .Net way of doing what I want to do?
dangph
Short answer: No.
Mike Hofer
Mike is correct. You are using the Windows message loop, which is based on the Win32 API, and thus requires P/Invoke.
Noldorin
+2  A: 

Are you actually wanting to post a message to the message loop or are you simply wanting to update some control in your Form, display a message box, etc.? If it's the former, then refer to @Noldorin's response. If it's the latter, then you need to use the Control.Invoke() method to marshal the call from your "reading" thread to the main UI thread. This is because controls can only be updated by the thread they were created on.

This is a pretty standard thing in .NET. Refer to these MSDN articles to get the basics:

Once you understand how to do this, refer Peter Duniho's blog for how to improve on the canonical technique.

Matt Davis
+4  A: 

You can achieve this with Control.BeginInvoke. An example:

public class SomethingReadyNotifier
{
   private readonly Control synchronizer = new Control();

   /// <summary>
   /// Event raised when something is ready. The event is always raised in the
   /// message loop of the thread where this object was created.
   /// </summary>
   public event EventHandler SomethingReady;

   protected void OnSomethingReady()
   {
      // Use this technique to avoid potential race condition.
      EventHandler handler = SomethingReady;
      if (handler != null)
         handler(this, EventArgs.Empty);
   }

   /// <summary>
   /// Causes the SomethingReady event to be raised on the message loop of the
   /// thread which created this object.
   /// </summary>
   /// <remarks>
   /// Can safely be called from any thread. Always returns immediately without
   /// waiting for the event to be handled.
   /// </remarks>
   public void NotifySomethingReady()
   {
      this.synchronizer.BeginInvoke(new Action(OnSomethingReady));
   }
}
Wim Coenen
As shown, there is a race condition in the OnSomethingReady() method. The SomethingReady event could be set to null after the check for null but before the event is raised. To avoid this, follow the advice here: http://blogs.msdn.com/brada/archive/2005/01/14/353132.aspx
Matt Davis
@wcoenen, forgive me, but I went ahead and fixed the issue.
Matt Davis