How to override the message loop in winforms?
-
Hi All, I have a situation where I need to do some custom message filtering. Unfortunately I need more functionality than provided by the IMessageFilter system or by overriding a Form's wndproc() method. What I need is to be able to run code before and after a message is processed and optionally drop selected messages. As I understand it: The IMessageFilter system only allows filtering out selected messages (can't run code after they have been processed) A Form's wndproc method only sees messages destined for that form. (I need to handle all messages for the entire application) In an unmanaged application say in visual c++ it would be a simple matter of implementing ones own getmessage/dispatchmessage loop. In the dotnet framework the actual message loop is implemented in native code and is inaccessible to the programmer. Does anyone know a solution for this? Regards, Felix
-
Hi All, I have a situation where I need to do some custom message filtering. Unfortunately I need more functionality than provided by the IMessageFilter system or by overriding a Form's wndproc() method. What I need is to be able to run code before and after a message is processed and optionally drop selected messages. As I understand it: The IMessageFilter system only allows filtering out selected messages (can't run code after they have been processed) A Form's wndproc method only sees messages destined for that form. (I need to handle all messages for the entire application) In an unmanaged application say in visual c++ it would be a simple matter of implementing ones own getmessage/dispatchmessage loop. In the dotnet framework the actual message loop is implemented in native code and is inaccessible to the programmer. Does anyone know a solution for this? Regards, Felix
Unfortunately, the only thing you can do outside the form is use Application.AddMessageFilter to add your own filter, and (as you've surmised), this only works on capturing a message before it is dispatched to the window. What you could possibly do (and this would be one heck of a hack), is use a combination of AddMessageFilter and a Mediator to control what happens. What you'd do is hook into the WndProc of each form, and pass the details to the mediator to work out whether or not the message could be processed.
"WPF has many lovers. It's a veritable porn star!" - Josh Smith
As Braveheart once said, "You can take our freedom but you'll never take our Hobnobs!" - Martin Hughes.
-
Unfortunately, the only thing you can do outside the form is use Application.AddMessageFilter to add your own filter, and (as you've surmised), this only works on capturing a message before it is dispatched to the window. What you could possibly do (and this would be one heck of a hack), is use a combination of AddMessageFilter and a Mediator to control what happens. What you'd do is hook into the WndProc of each form, and pass the details to the mediator to work out whether or not the message could be processed.
"WPF has many lovers. It's a veritable porn star!" - Josh Smith
As Braveheart once said, "You can take our freedom but you'll never take our Hobnobs!" - Martin Hughes.
Thanks for your answer. I think I may have to add a custom BaseForm class that overrides the wndproc and which all of my Forms inherit from. That way I could insert global message processing which would be similar to writing a custom message loop.
-
Thanks for your answer. I think I may have to add a custom BaseForm class that overrides the wndproc and which all of my Forms inherit from. That way I could insert global message processing which would be similar to writing a custom message loop.
Keep in mind existing dialogs (OpenFileDialog, MessageBox, ...) will not inherit from your BaseForm, so while one of those is open, your special message handling will not occur automatically. :)
Luc Pattyn [Forum Guidelines] [My Articles]
The quality and detail of your question reflects on the effectiveness of the help you are likely to get. Show formatted code inside PRE tags, and give clear symptoms when describing a problem.
-
Keep in mind existing dialogs (OpenFileDialog, MessageBox, ...) will not inherit from your BaseForm, so while one of those is open, your special message handling will not occur automatically. :)
Luc Pattyn [Forum Guidelines] [My Articles]
The quality and detail of your question reflects on the effectiveness of the help you are likely to get. Show formatted code inside PRE tags, and give clear symptoms when describing a problem.
Yeah, it is not ideal. As there seem to be so many helpful knowledgeable people on here, I'll describe the actual problem I'm trying to solve and see if there are any other ideas. The problem I'm having is that inside one of my event handlers I'm calling some DirectShow code. Inside the DirectShow code a message loop is run to handle the COM calls. Unfortunately this inner message loop pumps my input events so if I click quickly with the mouse I can get a second event handler running nested inside the first at the point of the DirectShow call. I'm not sure what the correct term is for this behaviour so I am calling it reentrant event processing (although it is not strictly rentrancy as the event handlers are likely to be different ones). The problem arises because all the event handler code assumes that it is single threaded (which it is) and that events will be processed in order from the queue. I get unpredictable crashes of my application due to this problem. One solution I am looking at is to simply discard user input messages and timer messages while a message is being processed, hence the original topic of this thread. Another more complicated but better solution is to run a second thread with it's own message loop and invoke the COM calls on that thread. I haven't looked into the details of this too much as yet. I've got a simple project that demonstrates the problem which I can post if anyone would like to see it. Thanks for your help! regards, Felix
-
Yeah, it is not ideal. As there seem to be so many helpful knowledgeable people on here, I'll describe the actual problem I'm trying to solve and see if there are any other ideas. The problem I'm having is that inside one of my event handlers I'm calling some DirectShow code. Inside the DirectShow code a message loop is run to handle the COM calls. Unfortunately this inner message loop pumps my input events so if I click quickly with the mouse I can get a second event handler running nested inside the first at the point of the DirectShow call. I'm not sure what the correct term is for this behaviour so I am calling it reentrant event processing (although it is not strictly rentrancy as the event handlers are likely to be different ones). The problem arises because all the event handler code assumes that it is single threaded (which it is) and that events will be processed in order from the queue. I get unpredictable crashes of my application due to this problem. One solution I am looking at is to simply discard user input messages and timer messages while a message is being processed, hence the original topic of this thread. Another more complicated but better solution is to run a second thread with it's own message loop and invoke the COM calls on that thread. I haven't looked into the details of this too much as yet. I've got a simple project that demonstrates the problem which I can post if anyone would like to see it. Thanks for your help! regards, Felix
I've had a similar issue when I try to link controls together, like a textBox and a trackBar, where updating one control should update the other. Here's how I solved it. Keep in mind this only works if it's managed code that you're trying to keep "non-reentrant". If your code is assuming a single-threaded environment, you could try setting a lock before the DirectShow code gets called, and check that lock at the beginning of the nested events, like so:
bool lock = false;
void SomeEvent(object sender, EventArgs e)
{
if(!lock){
lock = true;
// Some code
DirectShowCall();
// more code.
lock = false;
}
}void NestedEvent(object sender, EventArgs e)
{
if(!lock)
{
// Your code here.
}
}The lock check in SomeEvent will prevent your DirectShow code from getting called in a nested fashion. This method isn't thread-safe. If you need thread-safety, you may want to look at Monitor.TryEnter.[^] Dybs
-
I've had a similar issue when I try to link controls together, like a textBox and a trackBar, where updating one control should update the other. Here's how I solved it. Keep in mind this only works if it's managed code that you're trying to keep "non-reentrant". If your code is assuming a single-threaded environment, you could try setting a lock before the DirectShow code gets called, and check that lock at the beginning of the nested events, like so:
bool lock = false;
void SomeEvent(object sender, EventArgs e)
{
if(!lock){
lock = true;
// Some code
DirectShowCall();
// more code.
lock = false;
}
}void NestedEvent(object sender, EventArgs e)
{
if(!lock)
{
// Your code here.
}
}The lock check in SomeEvent will prevent your DirectShow code from getting called in a nested fashion. This method isn't thread-safe. If you need thread-safety, you may want to look at Monitor.TryEnter.[^] Dybs
Hi Dybs, naturally that is a great solution if you can easily identify SomeEvent and NestedEvent, however in my case this is not so easy, there are many events that can lead to a DirectShow call and many events that could be unsafe to process nested within another call. I could search through my code and put locks in all event handlers that look likely to cause trouble but this discipline would need to be maintained in maintenance and future development, so is not ideal. The solution you propose is really a special case of my proposal to override the wndproc of all forms (by using a custom BaseForm class). The whole point of an event queue and a single gui thread is so that in programming these events one can consider them as occurring serially. Overriding message processing for the whole app will place some overhead on all activity so I'd rather avoid the wndproc solution. Another disadvantage is that by effectively discarding some messages I might see strange behaviour, for example if a user started typing while in the lock the letters entered up to the point of releasing the lock would be lost. The alternative solution I'm going to investigate is to start a second thread with its own message loop and make all DirectShow calls synchronously on that thread. Due to good architectural choices early on in the project the references to DirectShow in my application are limited to one module so it should be easy to ensure that all calls are routed through the second thread. If it works I'll post an article here to explain the solution. Regards, Felix
-
Hi Dybs, naturally that is a great solution if you can easily identify SomeEvent and NestedEvent, however in my case this is not so easy, there are many events that can lead to a DirectShow call and many events that could be unsafe to process nested within another call. I could search through my code and put locks in all event handlers that look likely to cause trouble but this discipline would need to be maintained in maintenance and future development, so is not ideal. The solution you propose is really a special case of my proposal to override the wndproc of all forms (by using a custom BaseForm class). The whole point of an event queue and a single gui thread is so that in programming these events one can consider them as occurring serially. Overriding message processing for the whole app will place some overhead on all activity so I'd rather avoid the wndproc solution. Another disadvantage is that by effectively discarding some messages I might see strange behaviour, for example if a user started typing while in the lock the letters entered up to the point of releasing the lock would be lost. The alternative solution I'm going to investigate is to start a second thread with its own message loop and make all DirectShow calls synchronously on that thread. Due to good architectural choices early on in the project the references to DirectShow in my application are limited to one module so it should be easy to ensure that all calls are routed through the second thread. If it works I'll post an article here to explain the solution. Regards, Felix