Windows独占消息环独占线程通讯如何开发
2015-04-21 10:37
435 查看
http://stackoverflow.com/questions/4815699/how-to-programmatically-exit-from-a-second-message-loop
http://stackoverflow.com/questions/4815699/how-to-programmatically-exit-from-a-second-message-loop/4818485#4818485
oldest
votes
How to programmatically exit from a second message loop?
oldest
votes
http://stackoverflow.com/questions/4815699/how-to-programmatically-exit-from-a-second-message-loop/4818485#4818485
1.1 如何从第二消息环中退出
How to programmatically exit from a second message loop?up vote 2 down vote favorite 2 | 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. c# hidden window-handles message-pump message-loop
| |||
add comment |
1.2 2 Answers 2
activeoldest
votes
up vote 1 down vote accepted | You should pass the Forminstance to the ApplicationContextctor as a parameter: applicationContext = new ApplicationContext(form); 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: 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); [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 FormClosedevent, 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.
| |||||||||||||||||||
| ||||||||||||||||||||
up vote 1 down vote | 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: 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(); } } } Can be used this way: _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(); | |||||||||||||||||||
|
1.3 How to programmatically exit from a second message loop?
How to programmatically exit from a second message loop?
up vote 2 down vote favorite 2 | 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. c# hidden window-handles message-pump message-loop
| |||
add comment |
1.4 2 Answers 2
activeoldest
votes
up vote 1 down vote accepted | You should pass the Forminstance to the ApplicationContextctor as a parameter: applicationContext = new ApplicationContext(form); 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: 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); [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 FormClosedevent, 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.
| |||||||||||||||||||
| ||||||||||||||||||||
up vote 1 down vote | 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: 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(); } } } Can be used this way: _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();
| |||||||||||||||||||
|
相关文章推荐
- 无人机开发-介绍Mavlink协议的消息组成、如何看懂繁杂的mavlink官网介绍、简单介绍地面站与飞控的通讯流程
- Windows消息:如何自定义窗口消息与线程消息
- Windows消息:如何自定义窗口消息与线程消息
- 如何正确入门Windows系统下驱动开发领域
- 如何在windows上使用eclipse远程连接hadoop进行程序开发
- 如何在Windows下开发Python:在cmd下运行Python脚本
- 细说UI线程和Windows消息队列
- c#中如何跨线程调用windows窗体控件
- Windows内核开发中如何区分文件对象究竟是文件还是文件夹?
- windows的python开发环境下如何安装其他模块(httplib2)
- 如何实现可变消息的通讯
- 如何正确入门Windows系统下驱动开发领域?
- 如何在windows上使用eclipse远程连接hadoop进行程序开发
- 在Windows Mobile和Wince(Windows Embedded CE)下进行Native C++开发,如何取出当前执行文件的路径和调用模块的路径
- Windows 8(64位)如何搭建 Android 开发环境与真机测试
- Android开发:如何安全的中止一个自定义线程Thread
- winform自定义组件开发之windows消息机制
- C#开发 Windows 应用程序时消息的处理
- Java程序开发中如何应用线程
- Object C学习笔记6-如何在Windows环境搭建Object C开发环境