How to programmatically exit the second message loop?

I am trying to create a second message loop for asynchronously processing / filtering low-level messages in C #. It works by creating a hidden form, revealing its Handle property for connection and launching the second message loop in a separate thread. At the moment, I am satisfied with the results, but I can’t normally exit the second cycle. The only workaround was to set the IsBackground property to true, so the second thread would simply terminate (without processing all pending messages) when exiting the main application.

The question arises: how to correctly exit the message loop so that the second Application.Run () application returns? I tried different approaches to creating a separate ApplicationContext and managing various events (Application.ApplicationExit, Application.ThreadExit, ApplicationContext.ThreadExit), but they all could not cope with the conditions of the race. I can not 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 as follows:

 _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 : complete solution in my answer.

+4
source share
2 answers

You must pass the Form instance to the ApplicationContext ctor as a parameter:

 applicationContext = new ApplicationContext(form); 

Now you basically create a no-context instance that doesn't care about closing your form.

In addition, it is recommended to perform some cleaning, for example, remove the filter when you no longer need it:

 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); 

[change]

If you do not want to show the form, then you can use paramaterless ctor, but you will have to close the context manually by calling ApplicationContext.ExitThread . This method is actually called when your form fires the FormClosed event if you submit the form in the constructor.

Since the hidden form is not context related, you need to display both of them at some time.

+3
source

In the end, I realized that thread.IsBackground = true; was not bad because it was the only way to determine "hey, I am the last stream, I have to leave." Proper cleaning of resources is still necessary, tough. This requires a third delegate to clean up the resources, and I just registered it in the AppDomain.CurrentDomain.ProcessExit event. I even provided the ExitLoop () method to the MessageLoop class (there was a MessagePump question in the question). This way I can end the message loop at any time. Critical sections of ExitLoop () and ProcessExit handlers are mutexed.

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(); } } } 

It can be used as follows:

 _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 the following... _Scanner.Dispose(); }, true); // Automatically quit on main application shut-down // Anytime you can exit the loop messageLoop.ExitLoop(); 
+2
source

Source: https://habr.com/ru/post/1337298/


All Articles