For a utility application, I wanted to add an item in the system menu. It's been quite a long time since I last did this (and was in VB6), so I decided to find some ready made source code. This class provides a nice little helper for wrapping the system menu to add new commands to it, but it requires the owner form to hook into its window procedure and forward messages on which I felt was an awkward design.
The code snippets below should illustrate my point - first we
initialise the instance of the
SystemMenu class, but in order
for custom commands to be processed we have to override to
override the form's
WndProc and pass any messages received
This definitely isn't an ideal situation! As
protected and there is no equivalent event, perhaps the original
author of this code thought this was the only solution.
Fortunately there is a (little used?) feature of Windows Forms
that can inspect and manipulate source messages at an
Application class has the
RemoveMessageFilter methods, both of which accept a
single parameter - an object implementing the
IMessageFilter interface. This interface has a single
PreFilterMessage, allowing you to inspect a message
and then either allow it to be dispatched or blocked ("eaten").
Warning! Caution is advised when dealing with message filters. The MSDN documentation notes that adding filters can degrade application performance but you can also adversely effect your application if you accidentally block messages you shouldn't.
Lets take the
SystemMenu class from the start of the article.
I've stripped out as much as the code as possible to try and
focus only on message filtering.
First of all, we need to implement the interface. I'm choosing to explicitly implement it as it doesn't need a public surface.
This empty implementation returns
false to ensure we don't eat
any messages. If instead we returned
true then our application
would be completely broken - it wouldn't paint and you wouldn't
be able to interact with anything on it.
With that said, a message filter that doesn't do anything is a
bit of a waste, so I'll remove the public
and wrap its code into
PreFilterMessage. I also need to adjust
OnSysCommandMessage to return a result code as well - again,
if you swallow all
WM_SYSCOMMAND messages then your custom
actions might work but the default ones like Close,
Maximize, etc won't - and as this is an application filter
it will break all of your application windows.
Also, I added a check to make sure that the
message is destined for our owner form - there's no point
intercepting it for other forms in our application.
The message filter is now complete.
To install the filter we call
pass in our class instance. I choose to do this from the
If we now run the application, we'll find that our
class is now self contained and working perfectly.
Once we've finished with the filter we should remove it - in
this example, once the form is closed there isn't much point in
waiting for messages that will never arrive. We could make the
class disposable via the
IDisposable interface but as the
Form object has a
FormClosed event we can use that and free
the caller of having to anything except "fire and forget".
Once again, I modify the constructor, this time to wire up the event, and then supply the event handler itself.
As well as removing the handler, I detach the event and free up objects as well, on the assumption that nothing else is going to be done with the class.
Although you probably won't have much call to use them, message filters can be useful when dealing with certain windows messages. I originally became aware of them when I needed to have mouse wheel scrolling working on a control without focus and now again for this generic system menu class.
- 2019-01-01 - First published
- 2020-11-22 - Updated formatting