How To: Intercept Incoming Short Message System (SMS) Messages

4/7/2010

Jim Wilson, JW Hedgehog, Inc.

November 2007

Summary

Short Message System (SMS) messaging provides a convenient way to communicate between applications running on separate devices; the MessageInterceptor class allows you to easily incorporate SMS message handling in your application. This paper provides a description of the MessageInterceptor class' capabilities and includes samples of the class' most common uses. Samples are shown in both C# and Microsoft® Visual Basic® .NET.

Applies To

Microsoft Windows Mobile® 6 Professional

Microsoft Windows Mobile 6 Standard

Microsoft Windows Mobile 6 Classic

Microsoft Windows Mobile 5.0 for Pocket PC Phone Edition

Microsoft Windows Mobile 5.0 for Smartphone

Microsoft Windows Mobile 5.0 for Pocket PC

Introduction

Message Interception

Persistent Message Notifications

Message Notifications and Threading

Examples

Conclusion

Introduction

Exchanging Short Message System (SMS) messages, also known as "texting," is a simple, easy, and convenient way to communicate between mobile devices. In addition to being a great way for people to communicate, SMS can be a useful way for applications to exchange simple messages between devices. SMS doesn't require a direct connection between devices, the infrastructure for the system is already in place, and it works across most cellular service providers. One aspect of SMS that makes it especially useful for mobile applications is that it relies on a devices fixed identity, the phone number; this provides a distinct benefit over other technologies that rely on IP addresses because a device's IP address varies depending on its current network.

When it comes to working with SMS messages, there are of course two sides: sending messages and receiving messages. To send an SMS message using the .NET Compact Framework you simply use the SmsMessage Class and the class' Send method. Sending SMS messages is reasonably easy; there's little information to add beyond what's covered in the documentation. To receive or more appropriately, to intercept, SMS messages you use the MessageInterceptor class. The MessageInterceptor class is also simple to use but there are a few nuances that are worth discussing.

How to use the MessageInterceptor class to receive incoming SMS messages and the associated nuances is the subject of this paper. This paper includes several examples of using the MessageInterceptor class. The examples are provided in both C# and Visual Basic .NET, and are located at the end of the paper.

Message Interception

If you've ever worked with the State and Notifications Broker API's primary class SystemState, you'll find working with the MessageInterceptor class to be very similar. In both cases, you create an instance of the class, associate a filtering condition, and provide an event handler that the system calls each time an event or message meeting the criteria occurs. Unlike the SystemState class, which monitors a variety of different values, the MessageInterceptor monitors only arriving SMS messages.

Note

Like the SystemState class, the MessageInterceptor class must remain in-scope otherwise it may stop monitoring. For this reason, you rarely, if ever, create an instance of the MessageInterceptor class as a local variable. Instead, you'll almost always create instances of the MessageInterceptor class as a class-level field

To use the MessageInterceptor class you must add references to the Microsoft.WindowsMobile and Microsoft.WindowsMobile.PocketOutlook assemblies. The classes are all contained within the "Microsoft.WindowsMobile.PocketOutlook" and "Microsoft.WindowsMobile.PocketOutlook.MessageInterception" namespaces. C# developers need to include the appropriate using statements; Visual Basic .NET developers need to include the namespaces in the Microsoft Visual Studio® Imported namespace list.

Note

The topics discussed in the following subsections are demonstrated in Examples 1 and 2 located near the end of this paper.

Deciding Whether to Share the Message

When you create an instance of the MessageInterceptor class, the first thing you need to decide is whether you would like the intercepted messages to be available to other interceptor applications including the Windows Mobile Text Message reader. If you would like the message to be available to other interceptor applications, create the MessageInterceptor instance passing the InterceptionAction.Notify enumeration value to the MessageInterceptor constructor. In this case, the messaging system passes a copy of the message to your application and continues notifying other interceptor applications. If you instead pass InterceptionAction.NotifyAndDelete, the messaging system deletes the message when your application finishes with it. If you create an instance of the MessageInterceptor class with a constructor that does not accept a InterceptionAction parameter, the value defaults to InterceptionAction.Notify.

Note

Setting the InterceptionAction to InterceptionAction.NotifyAndDelete prevents the Windows Mobile Text Message reader from receiving the message but does not guarantee that no other message interceptor receives the message. An InterceptionAction value of InterceptionAction.NotifyAndDelete does not affect any interceptors that process the message prior to the messaging subsystem notifying your application.

Filtering Messages

In most situations, the user's mobile device is not used exclusively for your application. The user likely uses the device for a variety of work and personal purposes. For this reason, when registering to intercept SMS messages, you should provide a filter condition. Creating a copy of the message and routing the copy to your application consumes device resources, therefore you should register your SMS interceptor so that it receives only those messages that meet a specific criteria. Allowing the messaging system to filter the incoming messages is much more efficient than performing the filtering in your application.

To create a filter condition you simply assign an instance of the MessageCondition class to the MessageInterceptor instance's MessageCondition property. The MessageCondition class requires four pieces of information for a complete condition. First you must identify the part of the message to which the condition applies; you specify this value with the MessageProperty enumeration. Second is the type of comparison to perform such as equal, contains, starts-with, and so on; you specify the comparison using the MessagePropertyComparisonType enumeration. Finally, the condition needs the value to use for the comparison and whether the comparison should be case-sensitive. Tables 1 and 2, respectively, show the values for the MessageProperty and MessagePropertyComparisonType enumerations.

Table 1. MessageProperty enumeration values

Value Description

Body

Perform the comparison using the message body contents.

MessageClass

Perform the comparison using the message class. For all standard SMS messages the MessageClass value is the same, IPX.SMSText; an application would normally only check this value when working with a custom message type.

Sender

Perform the comparison using the sender information. When working with SMS messages, this value always includes the sender's phone number as consecutive digits with no separators such as a period, hyphen, or parenthesis. If the sender is contained in your Contacts list, the sender will also contain the sender's name as it appears in the Contact FileAs field.

Subject

Perform the comparison using the message subject field. SMS messages do not have a subject therefore this value is not normally used.

Table 2. MessagePropertyComparisonType enumeration values

Value Description

Equal

The message property and the comparison value must be an exact match. Use this comparison type with caution as any small change in the message property or the comparison value will result in a failed comparison.

NotEqual

The message property and comparison value must be different.

Contains

The comparison value appears within the message property.

StartsWith

The message property starts with the comparison value.

EndsWith

The message property ends with the comparison value.

By default, the comparisons are case-sensitive. You can specify a case-insensitive comparison by passing false to the caseSensitive parameter of the MessageCondition constructor that accepts four parameters.

Handling Message Notifications

The MessageInterceptor class exposes a single event named MessageReceived that accepts a MessageInterceptorEventHandler delegate. The MessageInterceptor class signals the MessageReceived event when the device receives an SMS message meeting the condition associated with the MessageInterceptor instance.

When the MessageReceived event signals, it passes a MessageInterceptorEventArgs reference to your event handler method. The MessageInterceptorEventArgs class has only one public member: the Message property that returns a reference to the received SMS message. The Message property return type is the base class Message, which does not provide a reference to the message body; therefore, to access the message body you must cast the Message reference to a SmsMessage reference.

Once your application associates an event handler with the MessageReceived event, you cannot make any modifications to the MessageInterceptor instance; therefore, you must associate the MessageCondition with the MessageInterceptor instance before associating the event handler. For this reason, Visual Basic .NET developers cannot use the WithEvents declaration modifier. The WithEvents declaration modifier automatically associates an event handler when the MessageInterceptor instance is created, which does not give you an opportunity to associate a MessageCondition. To associate a MessageCondition with the MessageInterceptor class in Visual Basic .NET, you must declare the MessageInterceptor reference without the WithEvents keyword and use the AddHandler keyword to attach an event handler to the MessageInterceptor instance after you assign the MessageCondition.

Handling Message Notification Cleanup

When you create an instance of the MessageInterceptor class and attach a delegate to the MessageReceived event, the MessageInterceptor class creates a temporary registry entry under HKLM\Software\Microsoft\Inbox\Rules. The registry entry contains information about the MessageInterceptor instance, which the messaging subsystem uses to notify the MessageInterceptor instance when a message arrives. Once you are done with the MessageInterceptor instance, the registry entry should be removed. Unfortunately, the MessageInterceptor class does not always clean up the registry properly. As a result, the registry entry may remain in the registry indefinitely even though the MessageInterceptor class will never use that entry again. These excess entries clutter the registry and over time, as the number of these unused entries grows, they may negatively impact message processing performance.

The MessageInterceptor class creates the registry entry when you attach an event handler to the MessageReceived event; the MessageInterceptor will also delete the entry if you remove the event handler from the MessageReceived event. Most applications do not explicitly remove event handlers and instead just let the .NET Compact Framework garbage collector remove them. Due to the problem in the MessageInterceptor class' cleanup process, you must explicitly remove the event handlers to assure that the MessageInterceptor class properly removes its registry entries.

Persistent Message Notifications

The discussion of message interception to this point focuses on transient message interception, message interception that begins and ends with a given program. Although transient message interception is appropriate for many situations, there will undoubtedly be other situations where you would like message interception to be in effect any time that the device is turned on without regard for whether your application is running. Persistent message notifications make this possible.

A persistent message notification stores the MessageInterceptor characteristics and the message handling application's information in the device registry; these registry entries remain even after your application exits. When a message arrives, the device messaging subsystem determines whether the message meets the persistent condition; when it does, the messaging subsystem identifies whether the application associated with the persistent condition is running. If the application is running, the messaging subsystem sends the message to the application just as the messaging subsystem normally does; if the application is not running, the messaging subsystem launches the application and then sends the message to the application. The beauty of persistent message notifications is that the message subsystem handles all the details of locating, launching, and notifying your application; your application requires very little change.

Note

Note: The topics discussed in the following subsections are demonstrated in Example 3 located near the end of this paper.

Creating Persistent Message Notifications

A persistent message interceptor usually starts as a transient message interceptor. You create the MessageInterceptor instance specifying the InterceptionAction and MessageCondition; in most cases, you will also associate an event handler with the MessageReceived event. To make the message interceptor persistent, you call the MessageInterceptor.EnableApplicationLauncher method.

You can call the EnableApplicationLauncher method any time after you set the MessageCondition property. Unlike the MessageCondition property, you can safely call the EnableApplicationLauncher method after you attach an event handler to the MessageReceived event; this is safe because the EnableApplicationLauncher method does not change the state of the MessageInterceptor instance. Exactly when you call the EnableApplicationLauncher method is up to you. You can call the EnableApplicationLauncher method immediately after you create and configure the MessageInterceptor instance, at the end of your program just before you call the MessageInterceptor instance's Dispose method, and anywhere in between.

The EnableApplicationLauncher method accepts a string parameter that uniquely identifies the MessageInterceptor. The string can be any value that you like as long as it will not collide with any other persistent message interceptor identifiers. Common examples of persistent message interceptor identifiers include globally unique identifiers (GUIDs), URLs, strings comprised of a combination of your organization and application name, and so on. Other versions of the EnableApplicationLauncher method let you specify a program other than the calling program as the program that the messaging subsystem should notify; you can also specify command-line arguments that the messaging subsystem will include when launching the application.

Note

For a more detailed discussion of creating persistent notification identifiers, see the Persistent Notifications section of The State and Notifications Broker API Part 2.

Once you call the EnableApplicationLauncher method, your application is registered as a persistent notification handler; however, nothing changes for your application until the application exits.

Handling Application Start Up

Once you create a persistent message interceptor, you create the MessageInterceptor instance to handle the message notifications differently. Rather than specifying the InterceptionAction and MessageCondition again, you instead pass the persistent notification identifier to the MessageInterceptor constructor. Using the identifier, the constructor reads the persistent message interceptor information from the registry and sets the MessageInterceptor instance's InterceptionAction and MessageCondition appropriately. Whether you create the MessageInterceptor instance using the transient notification or persistent notification constructor, you must associate an event handler with the MessageReceived event to receive the message notifications.

If you attempt to construct a MessageInterceptor instance with a message interceptor identifier that that has not yet been registered by a call to the EnableApplicationLauncher method, the MessageInterceptor constructor will throw an exception. To confirm that the message interceptor identifier is already registered, use the MessageInterceptor.IsApplicationLauncherEnabled static method. The IsApplicationLauncherEnabled method accepts a persistent message interceptor identifier and returns true if a persistent message interceptor definition exists with the specified identifier. When the IsApplicationLauncherEnabled method returns true, you create the MessageInterceptor instance using the constructor that accepts the identifier; otherwise, you create the MessageInterceptor normally and call the EnableApplicationLauncher to create the persistent message interceptor.

Disabling Persistent Notifications

Because the persistent notification information is stored in the registry, the persistent notification information is truly persistent; even a soft reset of the device will not remove it. To disable a persistent notification, you must explicitly remove it. To remove a persistent notification, you call the MessageInterceptor.DisableApplicationLauncher method on a MessageInterceptor instance that is associated with the persistent notification. You associate a MessageInterceptor instance with a persistent notification by either calling the MessageInterceptor constructor with the persistent notification identifier as a parameter or by calling the EnableApplicationLauncher method.

Message Notifications and Threading

By default, the MessageInterceptor class signals the MessageReceived event on the main application thread, which in turn causes your event handler to run on the main application thread. If your application processes a large number of messages or message processing causes your application user interface to appear sluggish, you may want to move the message process to a separate thread.

Note

The topics discussed in the following subsections are demonstrated in Examples 2 and 3 located near the end of this paper.

Handling Message Notifications on a Separate Thread

Two of the MessageInterceptor constructors accept a Boolean parameter named useFormThread. Passing false to the useFormThread parameter causes the MessageInterceptor class to monitor for arriving messages on a separate thread and therefore executes your MessageReceived event handler on this same thread. To simplify the discussion, the remainder of this paper will refer to the thread handling the message processing as the "message-interceptor" thread.

Even when you handle the MessageReceived event on the separate message-interceptor thread, you should avoid performing any long-running tasks in the event handler. All MessageInterceptor instances share a single message-interceptor thread. As a result only one MessageInterceptor event handler can run at a time. Therefore, a long-running event handler in one MessageInterceptor instance prevents not only that instance from processing incoming messages but all MessageInterceptor instances from processing incoming messages until the long-running event handler exits. In this case, your event handler should pass the message processing to a thread separate from the message-interceptor thread. By using a thread separate from the message-interceptor thread to process the message, your event handler can exit quickly and make the message-interceptor thread available to receive additional message notifications.

Note

The MessageInterceptor class' thread handling is very much like the thread handling of the SystemState and RegistryState classes. If you would like to read a detailed discussion of the threading behavior of these classes, please see the "Threading and Notification Handling" section of The State and Notifications Broker API Part 3. For guidelines on how to decide between using the Thread and ThreadPool classes, you may want to read the "Threading" section of the .NET Compact Framework version 2.0 Performance and Working Set FAQ blog post.

The MessageInterceptor instance's threading choice is not stored as part of a persistent notification. If you would like the MessageInterceptor instance associated with a persistent notification to run on a background thread, you must specify the desired threading behavior when you call the MessageInterceptor constructor.

Interacting with the User Interface

Just as is the case with any application code running on a non-UI thread, your event handler must not attempt to interact directly with the application user interface. Your event handler must instead use the Control.Invoke and Control.BeginInvoke methods to interact with the user interface; failure to do so will cause your application to throw an exception.

Disposing Message Notifications

The MessageInterceptor implements the IDisposable interface which, of course, indicates that you should call the MessageInterceptor.Dispose method when you no longer need the MessageInterceptor instance. Many classes in the .NET Compact Framework are very forgiving if you forget to call the Dispose method; however, that is not the case for the MessageInterceptor class. Forgetting to call the MessageInterceptor.Dispose method can cause your application to hang indefinitely at application shutdown.

In the .NET Compact Framework, an application does not fully exit until all of the application threads terminate. In the case of the MessageInterceptor class, the message-interceptor thread does not terminate until you call the Dispose method; therefore, your application will hang during the exit process if you forget to call the Dispose method. As mentioned earlier in this paper, all MessageInterceptor instances share a single message-interceptor thread. The MessageInterceptor class tracks the number of MessageInterceptor instances that are using the message-interceptor thread and does not terminate the thread until you call the Dispose method on all of them.

Note

In some cases, a .NET Compact Framework application will automatically terminate running threads as part of the application shutdown process but this not the case with the MessageInterceptor class. If you would like to learn more about affects of threads on an application's lifecycle, see Foreground and Background Threads in the .NET Framework Developers Guide.

Examples

The following examples demonstrate several common usages of the MessageInterceptor class. All examples are provided in both C# and Visual Basic .NET. You can test the behavior of the MessageInterceptor in your application using the Device Emulator and Cellular Emulator included as part of the Windows Mobile 6 SDK.

If you're not familiar with how to set up and use the Cellular Emulator, you may find the following short video helpful: How Do I: Configure the Device Emulator to Use an Emulated Cellular Connection? You may also find the Cellular Emulator section of the Developer's Guide to the ARM Emulator useful.

Example #1

This example demonstrates how to create a MessageInterceptor instance that receives a copy of all SMS messages from a person named John Smith. To check the sender by name, the sender must be in the device's Contacts list. The MessageInterceptor instance is handling the message notifications on the main application thread.

C#

MessageInterceptor _smsInterceptor = null;

private void Form1_Load(object sender, EventArgs e)
{
    // Default constructor indicates that message handling should occur
    // on the main application thread, and message should be passed
    // to other message interceptors and to the SMS message reader
    _smsInterceptor = new MessageInterceptor();
    // The name is reported in the form of the Contact's File As field
    // Therefore must check for "Smith, John", not "John Smith"
    _smsInterceptor.MessageCondition = 
        new MessageCondition(MessageProperty.Sender, 
        MessagePropertyComparisonType.Contains, "Smith, John", false);
    _smsInterceptor.MessageReceived += SmsInterceptor_MessageReceived;
}

void SmsInterceptor_MessageReceived(object sender, MessageInterceptorEventArgs e)
{
    // Cast to SmsMessage to access message body
    // Not expecting to receive any non-SMS messages but use "as" to
    // cast to be extra safe
    SmsMessage newMessage = e.Message as SmsMessage;
    if (newMessage != null)
    {
        statusBar1.Text = "From:" + newMessage.From.Address;
        Debug.WriteLine(string.Format("Sender:{0} - Body:{1}", _
          newMessage.From.Address, newMessage.Body));
    }
}

private void Form1_Closed(object sender, EventArgs e)
{
    if (_smsInterceptor != null)
    {
        // Remove event handler to assure proper registry cleanup
        _smsInterceptor.MessageReceived -= SmsInterceptor_MessageReceived;
        _smsInterceptor.Dispose();
    } 
}

Visual Basic .NET

' Do not use WithEvents or cannot associate a MessageCondition
Private _smsInterceptor As MessageInterceptor = Nothing

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ' Default constructor indicates that message handling should occur
    ' on the main application thread, and message should be passed
    ' to other message interceptors and to the SMS message reader
    _smsInterceptor = New MessageInterceptor()
    _smsInterceptor.MessageCondition = New MessageCondition(MessageProperty.Sender, _
        MessagePropertyComparisonType.Contains, "Smith, John", False)
    ' Explicitly attach the event handler rather than use WithEvents on the decleration 
    AddHandler _smsInterceptor.MessageReceived, AddressOf SmsInterceptor_MessageReceived
End Sub

Private Sub SmsInterceptor_MessageReceived(ByVal sender As Object, ByVal e As MessageInterceptorEventArgs)
    ' Cast to SmsMessage to access message body
    ' Not expecting to receive any non-SMS messages but use "as" to
    ' cast to be extra safe
    Dim newMessage As SmsMessage = TryCast(e.Message, SmsMessage)
    If Not newMessage Is Nothing Then
        StatusBar1.Text = "From:" & newMessage.From.Address
        Debug.WriteLine(String.Format("Sender:{0} - Body:{1}", newMessage.From.Address, newMessage.Body))
    End If
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

Example #2

This example demonstrates how to create a MessageInterceptor instance that receives a copy of all SMS messages sent from the phone number 425.555.1212. The MessageInterceptor instance handles the message notifications on a thread separate from the main application thread and prevents any further message interceptors, including the SMS message reader, from receiving the message.

C#

MessageInterceptor _smsInterceptor = null;

private void Form1_Load(object sender, EventArgs e)
{
    // Construct interceptor so that the message does not propagate
    // any further than this application which prevents the message
    // from appearing in the message reader. 
    // Handle message notifications on background thread
    _smsInterceptor = new MessageInterceptor(InterceptionAction.NotifyAndDelete, false);
    // When filtering by phone number, do not include any special
    // characters such as ().- 
    // Can filter by phone number for all senders - whether the sender
    // is in the Contact list does not affect this.
    _smsInterceptor.MessageCondition =
        new MessageCondition(MessageProperty.Sender,
        MessagePropertyComparisonType.Contains, "4255551212");
    _smsInterceptor.MessageReceived += SmsInterceptor_MessageReceived_OnThread;
}

// Notification runs on the message-interceptor thread, not the main
// application thread
void SmsInterceptor_MessageReceived_OnThread(object sender, MessageInterceptorEventArgs e)
{
    SmsMessage newMessage = e.Message as SmsMessage;
    if (newMessage != null)
    {
        // Cannot interact directly with user interface - in this case
        // using an anonymous delegate with the BeginInvoke method to
        // to transfer control to the main application thread to update
        // the status bar
        statusBar1.BeginInvoke(
            (MethodInvoker)delegate { 
                statusBar1.Text = "From:" + newMessage.From.Address; 
            });
        Debug.WriteLine(string.Format("Sender:{0} - Body:{1}", newMessage.From.Address, newMessage.Body));
    }
}

private void Form1_Closed(object sender, EventArgs e)
{
    if (_smsInterceptor != null)
    {
        // Remove event handler to assure proper registry cleanup
        _smsInterceptor.MessageReceived -= SmsInterceptor_MessageReceived;
        _smsInterceptor.Dispose();
    } 
}

#if PocketPC || Smartphone
        // MethodInvoker is a delegate with no arguments and no return value
        // MethodInvoker is part of the full .NET Framework but not the
        // .NET Compact Framework so must declare explicitly
        delegate void MethodInvoker();
#endif

Visual Basic .NET

Private _smsInterceptor As MessageInterceptor = Nothing

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ' Construct interceptor so that the message does not propagate
    ' any further than this application which prevents the message
    ' from appearing in the message reader. 
    ' Handle message notifications on background thread
    _smsInterceptor = New MessageInterceptor(InterceptionAction.NotifyAndDelete, False)
    ' When filtering by phone number, do not include any special
    ' characters such as ().- 
    ' Can filter by phone number for all senders - whether the sender
    ' is in the Contact list does not affect this.
    _smsInterceptor.MessageCondition = New MessageCondition(MessageProperty.Sender, _
        MessagePropertyComparisonType.Contains, "4255551212")
    AddHandler _smsInterceptor.MessageReceived, AddressOf SmsInterceptor_MessageReceived_OnThread
End Sub

' Notification runs on the message-interceptor thread, not the main
' application thread
Private Sub SmsInterceptor_MessageReceived_OnThread(ByVal sender As Object, ByVal e As MessageInterceptorEventArgs)
    Dim newMessage As SmsMessage = TryCast(e.Message, SmsMessage)
    If Not newMessage Is Nothing Then
        ' Cannot interact directly with user interface 
        ' using a custom delegate with the BeginInvoke method 
        ' to transfer control to the main application thread to update
        ' the status bar
        Dim updateDelegate As New UpdateStatusBarDelegate(AddressOf UpdateStatusBar)
        StatusBar1.BeginInvoke(updateDelegate, New Object() {"From:" & newMessage.From.Address})
        Debug.WriteLine(String.Format("Sender:{0} - Body:{1}", newMessage.From.Address, newMessage.Body))
    End If
End Sub

' This method is run on the main application thread by the call to
' StatusBar1.Invoke. Running on the main application thread, this method
' can safely update the user interface
Sub UpdateStatusBar(ByVal text As String)
    StatusBar1.Text = text
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

' Custom delegate used when StatusBar1.BeginInvoke calls UpdateStatusBar
Delegate Sub UpdateStatusBarDelegate(ByVal text As String)

Example #3

This example demonstrates how to create a MessageInterceptor instance that is associated with a persistent notification. The program checks to see if the persistent notification was previously created and constructs the MessageInterceptor appropriately. The MessageInterceptor receives only those messages where the message body begins with the text "Contoso Data:". The MessageInterceptor instance handles the message notifications on a thread separate from the main application thread and prevents any further message interceptors, including the SMS message reader, from receiving the message.

This example also includes a menu handler that disables the persistent notification.

Note

The SmsInterceptor_MessageReceived_OnThread and Form1_OnClosed methods are the same as those in Example 2 and are therefore not repeated.

C#

MessageInterceptor _smsInterceptor = null;
const string _persistentIdentifier = "Contoso.Pharmaceuticals.MessageHandlerApp";

private void Form1_Load(object sender, EventArgs e)
{
    if ( ! MessageInterceptor.IsApplicationLauncherEnabled(_persistentIdentifier))
    {
        // Persistent notification does not yet exist - must explicitly create
        _smsInterceptor = new MessageInterceptor(InterceptionAction.NotifyAndDelete, false);
        _smsInterceptor.MessageCondition = new MessageCondition(MessageProperty.Body, 
            MessagePropertyComparisonType.StartsWith, "Contoso Data:", false);
        // Make the interceptor persistent
        _smsInterceptor.EnableApplicationLauncher(_persistentIdentifier);
    }
    else
    {
        // Persistent notification already defined - create this instance using the
        // same characteristics
        _smsInterceptor = new MessageInterceptor(_persistentIdentifier, false);
    }

    // Once the interceptor is created, add event handler. Whether the interceptor is constructed
    // explicitly or constructed from the persistent notification identifier, 
    // the event handling behavior is the same.
    _smsInterceptor.MessageReceived += SmsInterceptor_MessageReceived_OnThread;

}

// Remove persistent notification 
private void menuDisablePersistentNotification_Click(object sender, EventArgs e)
{
    // Confirm that _smsInterceptor is a valid reference, that the current 
    // _smsInterceptor instance is associated with the correct persistent 
    // notification identifier, and that a persistent notification exists
    // that has the specified identifier
    if (_smsInterceptor != null &&
        _smsInterceptor.ApplicationLaunchId == _persistentIdentifier &&
        MessageInterceptor.IsApplicationLauncherEnabled(_persistentIdentifier))
    {
        _smsInterceptor.DisableApplicationLauncher();
    }
}

Visual Basic .NET

Private _smsInterceptor As MessageInterceptor = Nothing

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    If Not MessageInterceptor.IsApplicationLauncherEnabled(_persistentIdentifier) Then
        ' Persistent notification does not yet exist - must explicitly create
        _smsInterceptor = New
 MessageInterceptor(InterceptionAction.NotifyAndDelete, False)
        _smsInterceptor.MessageCondition = New MessageCondition(MessageProperty.Body, _
                MessagePropertyComparisonType.StartsWith, "Contoso Data:", False)
        ' Make the interceptor persistent
        _smsInterceptor.EnableApplicationLauncher(_persistentIdentifier)
    Else
        ' Persistent notification already defined - create this instance using the
        ' same characteristics
        _smsInterceptor = New MessageInterceptor(_persistentIdentifier, False)
    End If

    ' Once the interceptor is created, add event handler. Whether the interceptor is constructed 
    ' explicitly or constructed from the persistent notification identifier, the event
    ' handling behavior is the same.
    AddHandler _smsInterceptor.MessageReceived, AddressOf SmsInterceptor_MessageReceived_OnThread
End Sub

' Remove persistent notification 
Private Sub MenuEx3Disable_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuEx3Disable.Click
    If Not _smsInterceptor Is Nothing Then
        If _smsInterceptor.ApplicationLaunchId = _persistentIdentifier And _
            MessageInterceptor.IsApplicationLauncherEnabled(_persistentIdentifier) Then
            _smsInterceptor.DisableApplicationLauncher()
        End If
    End If
End Sub

Conclusion

Communication is a key part of most mobile applications and SMS messaging is one of the simplest ways to exchange messages between applications on different devices. Using the MessageInterceptor class, you can easily incorporate SMS message handling into your .NET Compact Framework application. The MessageInterceptor class enables you to easily filter unwanted messages and prevent non-user messages from appearing in the device SMS message reader; you can also configure the MessageInterceptor class to automatically start your application when a message of interest arrives. You do, of course, need to be sure that you clean up the resources appropriately to avoid cluttering the device registry, but once you create and configure an instance of the MessageInterceptor class, it takes care of the details of filtering and routing the messages; you simply need to process the message contents when it arrives and release the associated resources when you are done.

See Also

Other Resources

SmsMessage Class Documentation
MessageInterceptor Class Documentation
The State and Notifications Broker API Part 1
The State and Notifications Broker API Part 2
The State and Notifications Broker API Part 3
.NET Compact Framework version 2.0 Performance and Working Set FAQ blog post
Foreground and Background Threads
Developer's Guide to the ARM Emulator

Jim Wilson is president of JW Hedgehog, Inc. (https://www.jwhh.com) a New Hampshire–based consulting firm specializing in solutions, content creation, and mentoring for the Windows Mobile platform. Jim has worked extensively with the .NET Framework and .NET Compact Framework since the original beta release of each; he has over 20 years experience in the software industry including more than 14 years experience with relational database programming including SQL Server and SQL Server Compact Edition. Jim writes frequently for MSDN® and has developed mobility curriculums for two of the industry’s leading technology training organizations, DevelopMentor and PluralSight. Jim speaks regularly at Tech Ed, the Professional Developer's Conference (PDC), VSLive, and the Mobility & Embedded DevCon. You will find Jim online at https://pluralsight.com/blogs/jimw.