C# 向 Windows 窗体消息循环发送或发布消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1497947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Send or post a message to a Windows Forms message loop
提问by dan-gph
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).
我有一个从命名管道读取消息的线程。这是一个阻塞读取,这就是为什么它在自己的线程中。当此线程读取消息时,我希望它通知主线程中运行的 Windows 窗体消息循环消息已准备就绪。我怎样才能做到这一点?在 win32 中,我会执行 PostMessage,但该功能在 .Net 中似乎不存在(或者至少我找不到它)。
采纳答案by Wim Coenen
In WinForms you can achieve this with Control.BeginInvoke. An example:
在 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));
}
}
A cleaner variant of the above which doesn't depend on WinForms would be to use SynchronizationContext
. Call SynchronizationContext.Currenton your main thread, and then pass that reference to the constructor of the class shown below.
不依赖于 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);
}
}
回答by Noldorin
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.
PostMessage
(同样SendMessage
)是Win32 API 函数,因此与 .NET 没有直接关联。然而,.NET 确实很好地支持与 Win32 API 互操作,使用 P/Invoke 调用。
As it seems you are new to doing Win32 programming .NET, this MSDN Magazine articleprovides a solid introduction on the topic.
由于您似乎不熟悉 Win32 编程 .NET,这篇 MSDN 杂志文章提供了有关该主题的可靠介绍。
The excellent pinvoke.net websitedetails how to use many of these API functions from C#/VB.NET. See this pagefor PostMessage
specifically.
优秀的 pinvoke.net 网站详细介绍了如何使用 C#/VB.NET 中的许多 API 函数。看到这个页面的PostMessage
具体。
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:
但是正如页面所指出的,包装这个函数以正确处理 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());
}
}
回答by Matt Davis
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.
您实际上是想将消息发布到消息循环,还是只是想更新表单中的某些控件、显示消息框等?如果是前者,请参考@Noldorin 的回复。如果是后者,则需要使用 Control.Invoke() 方法将调用从“阅读”线程编组到主 UI 线程。这是因为控件只能由创建它们的线程更新。
This is a pretty standard thing in .NET. Refer to these MSDN articles to get the basics:
这是 .NET 中非常标准的事情。请参阅这些 MSDN 文章以获取基础知识:
- Control.Invoke Method
- Control.InvokeRequired Property(refer to first example in Community Content)
- Control.Invoke 方法
- Control.InvokeRequired 属性(请参阅社区内容中的第一个示例)
Once you understand how to do this, refer Peter Duniho's blogfor how to improve on the canonical technique.
一旦您了解了如何执行此操作,请参阅Peter Duniho 的博客以了解如何改进规范技术。