I was doing some work with the MessageInterceptor class yesterday - as I was doing some tests, I was checking the registry to be sure that my program wasn't leaving any registry clutter behind. Much to my surprise, I found a substantial amount of clutter in the registry ... but not from my program's direct-registry work ... rather it was from the MessageInterceptor class.
Note: The issue discussed in the post relate to transient message interceptors (begin and end with a program) not persistent message interceptors. Persistent message interceptors explicitly remove their registry entries by calling DisableApplicationLauncher.
Why does it happen?
As you know, the MessageInterceptor class cooperates with the Windows Mobile messaging sub-system. The MessageInterceptor class creates a new registry key under HKLM\Software\Microsoft\Inbox\Rules along with some sub-keys and values to identify the MessageInterceptor's characteristics (interception condition, whether message should be deleted, etc.) to the messaging sub-system.
The MessageInterceptor is supposed to automatically remove these registry entries when the MessageInterceptor is no longer in use. The problem is that it doesn't do this cleanup consistently. The problem is that the code to cleanup the registry entries is in the class' Finalizer method but not in the class' Dispose method ... and ... when you call the Dispose method, the Dispose method calls GC.SuppressFinalizer which prevents the Finalizer method from running (which is what you normally want a Dispose method to do).
One possible solution would be to not call Dispose - but saying "I know MessageInterceptor implements IDisposable but don't call the Dispose method" just doesn't feel right and not calling Dispose is kind'a bad style.
Also... if you create the MessageInterceptor to handle events on a non-UI thread (pass false to the useFormThread constructor parameter), you must call Dispose to shutdown the event handler thread. If you don't call Dispose, your application will hang when you try to exit.
The MessageInterceptor registry entries are volatile and are therefore automatically removed when you soft-reset the device. That said, I wouldn't want to be the one to tell a user that my application requires them to reset their device on a regular basis (especially if that user was my boss).
How to fix it
The MessageInterceptor class doesn't immediately create the registry entries at construction but rather waits until you attach an event handler to the MessageInterceptor.MessageReceived event. Well, just as attaching an event handler to the MessageReceived event creates the registry entries, removing the event handler from the MessageReceived event will delete the corresponding registry entries. Just as you'd expect, when you associate multiple handlers with the MessageReceived event, it waits until the last event handler is removed before deleting the registry entries
So the bottom line is... you should always explicitly remove all MessageInterceptor.MessageReceived event handlers prior to exiting your program so as to avoid leaving registry clutter. Here's a short example....
C#
MessageInterceptor _smsInterceptor = null;
private void Form1_Load (object sender, EventArgs e)
{
// Create MessageInterceptor Normally
_smsInterceptor = new MessageInterceptor();
_smsInterceptor.MessageCondition = new MessageCondition(MessageProperty.Sender,
MessagePropertyComparisonType.Contains, "Smith, John", false);
_smsInterceptor.MessageReceived += SmsInterceptor_MessageReceived;
}
private void Form1_Closed(object sender, EventArgs e)
{
if (_smsInterceptor != null)
{
// explicitly remove event handler before exiting
_smsInterceptor.MessageReceived -= SmsInterceptor_MessageReceived;
_smsInterceptor.Dispose();
}
}
Visual Basic .NET
Private _smsInterceptor As MessageInterceptor = Nothing
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuEx1.Click
' Create MessageInterceptor Normally
_smsInterceptor = New MessageInterceptor()
_smsInterceptor.MessageCondition = New MessageCondition(MessageProperty.Sender, _
MessagePropertyComparisonType.Contains, "Smith, John", False)
AddHandler _smsInterceptor.MessageReceived, AddressOf SmsInterceptor_MessageReceived
End Sub
Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
If Not _smsInterceptor Is Nothing Then
RemoveHandler _smsInterceptor.MessageReceived, AddressOf SmsInterceptor_MessageReceived
_smsInterceptor.Dispose()
End If
End Sub
One last point... I have not performed explicit tests on the effects of the excess registry entries, but my casual observation is that the process of routing messages to active MessageInterceptor instances appears to be slowed by the existence of these extraneous registry entries.
Posted
Oct 26 2007, 09:36 AM
by
jim-wilson