【问题标题】:Send or post a message to a Windows Forms message loop向 Windows 窗体消息循环发送或发布消息
【发布时间】:2010-12-02 15:32:18
【问题描述】:

我有一个从命名管道读取消息的线程。这是一个阻塞读取,这就是它在自己的线程中的原因。当该线程读取消息时,我希望它通知在主线程中运行的 Windows 窗体消息循环消息已准备好。我怎样才能做到这一点?在 win32 中我会做一个 PostMessage,但该功能在 .Net 中似乎不存在(或者至少我找不到它)。

【问题讨论】:

    标签: c# winforms multithreading messages


    【解决方案1】:

    PostMessage(以及SendMessage)是Win32 API 函数,因此与.NET 没有直接关联。然而,.NET 对使用 P/Invoke 调用与 Win32 API 进行互操作有很好的支持。

    您似乎是第一次接触 Win32 编程 .NET,this MSDN Magazine article 提供了有关该主题的可靠介绍。

    The excellent pinvoke.net website 详细介绍了如何使用 C#/VB.NET 中的许多 API 函数。 See this page 专门用于PostMessage

    标准声明如下:

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

    但正如页面所示,包装此函数以正确处理 Win32 错误是明智的:

    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());
        }
    }        
    

    【讨论】:

    • 我不一定要使用 PostMessage。有没有简单的 .Net 方式来做我想做的事?
    • 迈克是正确的。您正在使用基于 Win32 API 的 Windows 消息循环,因此需要 P/Invoke。
    【解决方案2】:

    您是真的想向消息循环发布消息,还是只是想更新表单中的某些控件、显示消息框等?如果是前者,请参考@Noldorin 的回复。如果是后者,那么您需要使用 Control.Invoke() 方法将调用从“读取”线程编组到主 UI 线程。这是因为控件只能由创建它们的线程更新。

    这是 .NET 中非常标准的事情。请参阅这些 MSDN 文章以了解基础知识:

    一旦您了解如何执行此操作,请参阅 Peter Duniho's blog 了解如何改进规范技术。

    【讨论】:

      【解决方案3】:

      在 WinForms 中,您可以使用 Control.BeginInvoke 实现此目的。一个例子:

      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()
         {
             SomethingReady?.Invoke(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));
         }
      }
      

      上述不依赖于 WinForms 的更简洁的变体是使用SynchronizationContext。在主线程上调用SynchronizationContext.Current,然后将该引用传递给如下所示的类的构造函数。

      public class SomethingReadyNotifier
      {
          private readonly SynchronizationContext synchronizationContext;
      
          /// <summary>
          /// Create a new <see cref="SomethingReadyNotifier"/> instance. 
          /// </summary>
          /// <param name="synchronizationContext">
          /// The synchronization context that will be used to raise
          /// <see cref="SomethingReady"/> events.
          /// </param>
          public SomethingReadyNotifier(SynchronizationContext synchronizationContext)
          {
              this.synchronizationContext = synchronizationContext;
          }
      
          /// <summary>
          /// Event raised when something is ready. The event is always raised
          /// by posting on the synchronization context provided to the constructor.
          /// </summary>
          public event EventHandler SomethingReady;
      
          private void OnSomethingReady()
          {
              SomethingReady?.Invoke(this, EventArgs.Empty);
          }
      
          /// <summary>
          /// Causes the SomethingReady event to be raised.
          /// </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.synchronizationContext.Post(
                      state => OnSomethingReady(),
                      state: null);
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-04
        • 1970-01-01
        相关资源
        最近更新 更多