代码之家  ›  专栏  ›  技术社区  ›  dan-gph

向windows窗体消息循环发送或投递消息

  •  16
  • dan-gph  · 技术社区  · 15 年前

    我有一个从命名管道读取消息的线程。这是一个阻塞读取,这就是为什么它在自己的线程中。当这个线程读取消息时,我希望它通知主线程中运行的windows forms消息循环,消息已准备就绪。我怎样才能做到呢?在win32中,我会做一个postmessage,但是这个函数似乎不存在于.net中(或者至少我找不到它)。

    3 回复  |  直到 7 年前
        1
  •  16
  •   Wim Coenen    8 年前

    在WiFr窗表中,你可以做到这一点。 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);
            }
        }
    
        2
  •  18
  •   Noldorin    15 年前

    PostMessage (同样 SendMessage win32 api函数 ,因此与.NET没有直接关联。但是.NET确实很好地支持使用p/invoke调用与win32 api进行互操作。

    似乎你对win32 programming.net还不熟悉, this MSDN Magazine article 提供关于该主题的可靠介绍。

    The excellent pinvoke.net website 详细说明如何使用c/vb.net中的许多api函数。 See this page 对于 发送消息 明确地。

    标准声明如下:

    [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());
        }
    }        
    
        3
  •  4
  •   Matt Davis    15 年前

    您是真的想将消息发送到消息循环,还是仅仅想更新表单中的某些控件、显示消息框等?如果是前者,请参考@noldorin的回复。如果是后者,则需要使用control.invoke()方法将调用从“reading”线程封送到主ui线程。这是因为控件只能通过它们创建的线程来更新。

    这在.net中是一个相当标准的东西。请参阅以下MSDN文章以了解基本信息:

    一旦你了解如何做到这一点,请参考 Peter Duniho's blog 如何改进规范技术。