如何以编程方式退出第二个消息循环?
我正在尝试创建第二个消息循环来在 C# 中异步处理/过滤低级消息。它的工作原理是创建一个隐藏表单,公开其要挂钩的 Handle 属性,并在单独的线程中运行第二个消息循环。目前我对结果非常满意,但我无法正确退出第二个循环。唯一的解决方法是将 IsBackground 属性设置为 true,因此第二个线程将在主应用程序退出时简单地终止(不处理所有挂起的消息)。
问题是:如何正确退出该消息循环以便第二个 Application.Run() 返回?我尝试了不同的方法来创建单独的 ApplicationContext 并控制各种事件(Application.ApplicationExit、Application.ThreadExit、ApplicationContext.ThreadExit),但它们都因竞争条件而失败,我无法调试。
有什么提示吗?谢谢
这是代码:
public class MessagePump
{
public delegate void HandleHelper(IntPtr handle);
public MessagePump(HandleHelper handleHelper, Filter filter)
{
Thread thread = new Thread(delegate()
{
ApplicationContext applicationContext = new ApplicationContext();
Form form = new Form();
handleHelper(form.Handle);
Application.AddMessageFilter(new MessageFilter(filter));
Application.Run(applicationContext);
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true; // <-- The workaround
thread.Start();
}
}
public delegate bool Filter(ref Message m);
internal class MessageFilter : IMessageFilter
{
private Filter _Filter;
public MessageFilter(Filter filter)
{
_Filter = filter;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
return _Filter(ref m);
}
#endregion // IMessageFilter Members
}
我以这种方式在主表单构造函数中使用它:
_Completion = new ManualResetEvent(false);
MessagePump pump = new MessagePump(
delegate(IntPtr handle)
{
// Sample code, I did this form twain drivers low level wrapping
_Scanner = new TwainSM(handle);
_Scanner.LoadDs("EPSON Perfection V30/V300");
},
delegate(ref Message m)
{
// Asyncrhronous processing of the messages
// When the correct message is found -->
_Completion.Set();
}
编辑:我的答案中的完整解决方案。
I'm trying to create a second message loop to process/filter low level messages asynchronously in C#. It works by creating a hidden form, exposing it's Handle property to be hooked, and run a second message loop in a separate thread. At the moment I'm quite happy with the results but I'm unable to exit from the second loop properly. The only workaround was setting the IsBackground property to true, so the second thread will be simply terminated (without processing all the pending messages) at main application exit.
The question is: how to proper quit that message loop so the second Application.Run() returns? I tried differents approaches creating a separate ApplicationContext and controlling various events (Application.ApplicationExit, Application.ThreadExit, ApplicationContext.ThreadExit) but they all failed with race conditions I'm unable to debug.
Any hint? Thanks
This is the code:
public class MessagePump
{
public delegate void HandleHelper(IntPtr handle);
public MessagePump(HandleHelper handleHelper, Filter filter)
{
Thread thread = new Thread(delegate()
{
ApplicationContext applicationContext = new ApplicationContext();
Form form = new Form();
handleHelper(form.Handle);
Application.AddMessageFilter(new MessageFilter(filter));
Application.Run(applicationContext);
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true; // <-- The workaround
thread.Start();
}
}
public delegate bool Filter(ref Message m);
internal class MessageFilter : IMessageFilter
{
private Filter _Filter;
public MessageFilter(Filter filter)
{
_Filter = filter;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
return _Filter(ref m);
}
#endregion // IMessageFilter Members
}
I use it in the main Form constructor in this way:
_Completion = new ManualResetEvent(false);
MessagePump pump = new MessagePump(
delegate(IntPtr handle)
{
// Sample code, I did this form twain drivers low level wrapping
_Scanner = new TwainSM(handle);
_Scanner.LoadDs("EPSON Perfection V30/V300");
},
delegate(ref Message m)
{
// Asyncrhronous processing of the messages
// When the correct message is found -->
_Completion.Set();
}
EDIT: Full solution in my answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您应该将
Form
实例作为参数传递给ApplicationContext
构造函数:现在,您基本上是在创建一个无上下文实例,它不关心您的表单是关闭。
另外,进行一些清理是一个很好的做法,例如在不再需要过滤器时删除过滤器:
[Edit]
如果您不想显示表单,则可以使用无参数构造函数,但您必须通过调用 ApplicationContext.ExitThread 方法。如果您在构造函数中传递表单,那么当您的表单触发
FormClosed
事件时,实际上会调用此方法。由于隐藏表单与上下文无关,因此您需要在某个时候退出它们。
You should pass the
Form
instance to theApplicationContext
ctor as a parameter:Right now, you are basically creating a no-context instance, which doesn't care about your form being closed.
Also, it is a good practice to do some cleanup, like removing the filter when you don't need it anymore:
[Edit]
If you don't want to show the form, then you can use the paramaterless ctor, but you will have to close the context manually by calling the ApplicationContext.ExitThread method. This method actually gets called when your form fires the
FormClosed
event, if you pass the form in the constructor.Since hidden form is not related to the context, you need to exit them both at some time.
我最终意识到 thread.IsBackground = true; 还不错,因为它是确定“嘿,我是最后一个运行的线程,我应该退出”的唯一方法。仍然需要正确的资源清理,这很困难。为此,需要第三个委托来进行资源清理,我只是将其注册到 AppDomain.CurrentDomain.ProcessExit 事件。我什至为 MessageLoop 类提供了 ExitLoop() 方法(问题中是 MessagePump)。这样,我就可以随时终止消息循环。 ExitLoop() 和 ProcessExit 处理程序的关键部分是互斥的。
代码:
可以这样使用:
I eventually realized that the
thread.IsBackground = true;
was not bad, because it was the only way to determine "hey, I'm the last thread running, I'm supposed to quit". Correct resource cleaning is still needed, tough. For this, a third delegate for resource cleaning is needed and I just registered it to the AppDomain.CurrentDomain.ProcessExit event. I even provided a ExitLoop() method to the MessageLoop class (was MessagePump in the question). In this way, I can terminate the message loop anytime. Critical sections of ExitLoop() and ProcessExit handler are mutexed.The code:
Can be used this way: