我正在尝试创建第二个消息循环,以便在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();
}
编辑:我的答案中的完整解决方案。
答案 0 :(得分:3)
您应该将Form
实例作为参数传递给ApplicationContext
ctor:
applicationContext = new ApplicationContext(form);
现在,您基本上是在创建一个无上下文的实例,它不关心您的表单是否已关闭。
此外,最好进行一些清理工作,例如在不再需要时删除过滤器:
Form form = new Form();
ApplicationContext applicationContext = new ApplicationContext(form);
handleHelper(form.Handle);
MessageFilter filter = new MessageFilter(filter);
Application.AddMessageFilter(filter);
Application.Run(applicationContext);
Application.RemoveMessageFilter(filter);
<强> [编辑] 强>
如果您不想显示表单,则可以使用无参数的ctor,但是您必须通过调用ApplicationContext.ExitThread方法手动关闭上下文。如果您在构造函数中传递表单,则当表单触发FormClosed
事件时,实际上会调用此方法。
由于隐藏表单与上下文无关,因此您需要在某个时间退出它们。
答案 1 :(得分:2)
我最终意识到thread.IsBackground = true;
并不坏,因为这是确定“嘿,我是最后一个线程运行的唯一方法,我应该放弃”。仍然需要正确的资源清理,这很难。为此,需要第三个资源清理代表,我只是将其注册到AppDomain.CurrentDomain.ProcessExit事件。我甚至为MessageLoop类提供了一个ExitLoop()方法(问题中是MessagePump)。通过这种方式,我可以随时终止消息循环。 “ExitLoop()”和“ProcessExit”处理程序的关键部分是互斥的。
代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace System
{
public class MessageLoop
{
#region Fields
private Object _Lock;
private ApplicationContext _ApplicationContext;
private CustomMessageFilter _MessageFilter;
private HandleProvider _ResourceCleaner;
private ManualResetEvent _Completion;
private bool _Disposed;
#endregion // Fields
#region Constructors
/// <summary>
/// Run a second message pump that will filter messages asyncronously
/// </summary>
/// <param name="provideHandle">A delegate that provide a window handle for
/// resource initializing</param>
/// <param name="messageFilter">A delegate for message filtering</param>
/// <param name="cleanResources">A delegate for proper resource cleaning
/// before quitting the loop</param>
/// <param name="background">State if the loop should be run on a background
/// thread or not. If background = false, please be aware of the
/// possible race conditions on application shut-down.</param>
public MessageLoop(HandleProvider initializeResources, MessageFilter messageFilter,
HandleProvider cleanResources, bool background)
{
_Lock = new Object();
_ResourceCleaner = cleanResources;
_Completion = new ManualResetEvent(false);
_Disposed = false;
Thread thread = new Thread(delegate()
{
_ApplicationContext = new ApplicationContext();
WindowHandle window = new WindowHandle();
initializeResources(window.Handle);
_MessageFilter = new CustomMessageFilter(messageFilter);
Application.AddMessageFilter(_MessageFilter);
// Signal resources initalizated
_Completion.Set();
// If background = true, do resource cleaning on ProcessExit event
if (background)
{
AppDomain.CurrentDomain.ProcessExit +=
new EventHandler(CurrentDomain_ProcessExit);
}
// Run the message loop
Application.Run(_ApplicationContext);
// Clean resource before leaving the thread
cleanResources(window.Handle);
// Signal resources cleaned
_Completion.Set();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = background;
thread.Start();
// Before returning the instace, wait for thread resources initialization
_Completion.WaitOne();
}
#endregion // Constructors
#region Inquiry
/// <summary>
/// Early exit the message loop
/// </summary>
public void ExitLoop()
{
lock (_Lock)
{
if (_Disposed)
return;
// Completion was already signaled in the constructor
_Completion.Reset();
// Tell the message loop thread to quit
_ApplicationContext.ExitThread();
// Wait for thread resources cleaning
_Completion.WaitOne();
_Disposed = true;
}
}
#endregion // Inquiry
#region Event handlers
void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
lock (_Lock)
{
if (_Disposed)
return;
// Completion was already signaled in the constructor
_Completion.Reset();
// Tell the message loop thread to quit
_ApplicationContext.ExitThread();
// Wait for thread resources cleaning
_Completion.WaitOne();
_Disposed = true;
}
}
#endregion // Event handlers
#region Support
public delegate void HandleProvider(IntPtr handle);
public delegate bool MessageFilter(ref Message m);
internal class CustomMessageFilter : IMessageFilter
{
private MessageFilter _Filter;
public CustomMessageFilter(MessageFilter filter)
{
_Filter = filter;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
return _Filter(ref m);
}
#endregion // IMessageFilter Members
}
#endregion // Support
}
public class WindowHandle : NativeWindow
{
public WindowHandle()
{
CreateParams parms = new CreateParams();
CreateHandle(parms);
}
~WindowHandle()
{
DestroyHandle();
}
}
}
可以这样使用:
_Completion = new ManualResetEvent(false);
MessageLoop messageLoop = new MessageLoop(
delegate(IntPtr handle) // Resource initializing
{
// 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) // Message filtering
{
// Asyncrhronous processing of the messages
// When the correct message is found -->
_Completion.Set();
},
delegate(IntPtr handle) // Resource cleaning
{
// Resource cleaning/disposing. In my case, it's the following...
_Scanner.Dispose();
}, true); // Automatically quit on main application shut-down
// Anytime you can exit the loop
messageLoop.ExitLoop();