Important:
This is retired content. This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.
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 MessageInterceptorclass allows you to easily incorporate SMS message handling in your application. This paper provides a description of the MessageInterceptorclass' 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

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 SmsMessageClass and the class' Sendmethod. 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 MessageInterceptorclass. The MessageInterceptorclass is also simple to use but there are a few nuances that are worth discussing.

How to use the MessageInterceptorclass to receive incoming SMS messages and the associated nuances is the subject of this paper. This paper includes several examples of using the MessageInterceptorclass. 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 MessageInterceptorclass 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 SystemStateclass, which monitors a variety of different values, the MessageInterceptormonitors only arriving SMS messages.

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

To use the MessageInterceptorclass you must add references to the Microsoft.WindowsMobileand Microsoft.WindowsMobile.PocketOutlookassemblies. The classes are all contained within the "Microsoft.WindowsMobile.PocketOutlook" and "Microsoft.WindowsMobile.PocketOutlook.MessageInterception" namespaces. C# developers need to include the appropriate usingstatements; Visual Basic .NET developers need to include the namespaces in the Microsoft Visual Studio® Imported namespacelist.

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 MessageInterceptorclass, 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 MessageInterceptorinstance passing the InterceptionAction.Notifyenumeration value to the MessageInterceptorconstructor. 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 MessageInterceptorclass with a constructor that does not accept a InterceptionActionparameter, the value defaults to InterceptionAction.Notify.

Note:
Setting the InterceptionActionto InterceptionAction.NotifyAndDeleteprevents the Windows Mobile Text Message reader from receiving the message but does not guarantee that no other message interceptor receives the message. An InterceptionActionvalue of InterceptionAction.NotifyAndDeletedoes 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 MessageConditionclass to the MessageInterceptorinstance's MessageConditionproperty. The MessageConditionclass 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 MessagePropertyenumeration. Second is the type of comparison to perform such as equal, contains, starts-with, and so on; you specify the comparison using the MessagePropertyComparisonTypeenumeration. 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 MessagePropertyand MessagePropertyComparisonTypeenumerations.

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 MessageClassvalue 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 FileAsfield.

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 caseSensitiveparameter of the MessageConditionconstructor that accepts four parameters.

Handling Message Notifications

The MessageInterceptorclass exposes a single event named MessageReceivedthat accepts a MessageInterceptorEventHandlerdelegate. The MessageInterceptorclass signals the MessageReceivedevent when the device receives an SMS message meeting the condition associated with the MessageInterceptorinstance.

When the MessageReceivedevent signals, it passes a MessageInterceptorEventArgsreference to your event handler method. The MessageInterceptorEventArgsclass has only one public member: the Messageproperty that returns a reference to the received SMS message. The Messageproperty 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 Messagereference to a SmsMessagereference.

Once your application associates an event handler with the MessageReceivedevent, you cannot make any modifications to the MessageInterceptorinstance; therefore, you must associate the MessageConditionwith the MessageInterceptorinstance before associating the event handler. For this reason, Visual Basic .NET developers cannot use the WithEventsdeclaration modifier. The WithEventsdeclaration modifier automatically associates an event handler when the MessageInterceptorinstance is created, which does not give you an opportunity to associate a MessageCondition. To associate a MessageConditionwith the MessageInterceptorclass in Visual Basic .NET, you must declare the MessageInterceptorreference without the WithEventskeyword and use the AddHandlerkeyword to attach an event handler to the MessageInterceptorinstance after you assign the MessageCondition.

Handling Message Notification Cleanup

When you create an instance of the MessageInterceptorclass and attach a delegate to the MessageReceivedevent, the MessageInterceptorclass creates a temporary registry entry under HKLM\Software\Microsoft\Inbox\Rules. The registry entry contains information about the MessageInterceptorinstance, which the messaging subsystem uses to notify the MessageInterceptorinstance when a message arrives. Once you are done with the MessageInterceptorinstance, the registry entry should be removed. Unfortunately, the MessageInterceptorclass does not always clean up the registry properly. As a result, the registry entry may remain in the registry indefinitely even though the MessageInterceptorclass 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 MessageInterceptorclass creates the registry entry when you attach an event handler to the MessageReceivedevent; the MessageInterceptorwill also delete the entry if you remove the event handler from the MessageReceivedevent. 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 MessageInterceptorclass' cleanup process, you must explicitly remove the event handlers to assure that the MessageInterceptorclass 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 MessageInterceptorcharacteristics 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 MessageInterceptorinstance specifying the InterceptionActionand MessageCondition; in most cases, you will also associate an event handler with the MessageReceivedevent. To make the message interceptor persistent, you call the MessageInterceptor.EnableApplicationLaunchermethod.

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

The EnableApplicationLaunchermethod 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 EnableApplicationLaunchermethod 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 EnableApplicationLaunchermethod, 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 MessageInterceptorinstance to handle the message notifications differently. Rather than specifying the InterceptionActionand MessageConditionagain, you instead pass the persistent notification identifier to the MessageInterceptorconstructor. Using the identifier, the constructor reads the persistent message interceptor information from the registry and sets the MessageInterceptorinstance's InterceptionActionand MessageConditionappropriately. Whether you create the MessageInterceptorinstance using the transient notification or persistent notification constructor, you must associate an event handler with the MessageReceivedevent to receive the message notifications.

If you attempt to construct a MessageInterceptorinstance with a message interceptor identifier that that has not yet been registered by a call to the EnableApplicationLaunchermethod, the MessageInterceptorconstructor will throw an exception. To confirm that the message interceptor identifier is already registered, use the MessageInterceptor.IsApplicationLauncherEnabledstatic method. The IsApplicationLauncherEnabledmethod accepts a persistent message interceptor identifier and returns true if a persistent message interceptor definition exists with the specified identifier. When the IsApplicationLauncherEnabledmethod returns true, you create the MessageInterceptorinstance using the constructor that accepts the identifier; otherwise, you create the MessageInterceptornormally and call the EnableApplicationLauncherto 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.DisableApplicationLaunchermethod on a MessageInterceptorinstance that is associated with the persistent notification. You associate a MessageInterceptorinstance with a persistent notification by either calling the MessageInterceptorconstructor with the persistent notification identifier as a parameter or by calling the EnableApplicationLaunchermethod.

Message Notifications and Threading

By default, the MessageInterceptorclass signals the MessageReceivedevent 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 MessageInterceptorconstructors accept a Boolean parameter named useFormThread. Passing false to the useFormThreadparameter causes the MessageInterceptorclass to monitor for arriving messages on a separate thread and therefore executes your MessageReceivedevent 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 MessageReceivedevent on the separate message-interceptor thread, you should avoid performing any long-running tasks in the event handler. All MessageInterceptorinstances share a single message-interceptor thread. As a result only one MessageInterceptorevent handler can run at a time. Therefore, a long-running event handler in one MessageInterceptorinstance prevents not only that instance from processing incoming messages but all MessageInterceptorinstances 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 MessageInterceptorclass' thread handling is very much like the thread handling of the SystemStateand RegistryStateclasses. 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 Threadand ThreadPoolclasses, you may want to read the "Threading" section of the .NET Compact Framework version 2.0 Performance and Working Set FAQ blog post .

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

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.Invokeand Control.BeginInvokemethods to interact with the user interface; failure to do so will cause your application to throw an exception.

Disposing Message Notifications

The MessageInterceptorimplements the IDisposableinterface which, of course, indicates that you should call the MessageInterceptor.Disposemethod when you no longer need the MessageInterceptorinstance. Many classes in the .NET Compact Framework are very forgiving if you forget to call the Disposemethod; however, that is not the case for the MessageInterceptorclass. Forgetting to call the MessageInterceptor.Disposemethod 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 MessageInterceptorclass, the message-interceptor thread does not terminate until you call the Disposemethod; therefore, your application will hang during the exit process if you forget to call the Disposemethod. As mentioned earlier in this paper, all MessageInterceptorinstances share a single message-interceptor thread. The MessageInterceptorclass tracks the number of MessageInterceptorinstances that are using the message-interceptor thread and does not terminate the thread until you call the Disposemethod 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 MessageInterceptorclass. 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 MessageInterceptorclass. All examples are provided in both C# and Visual Basic .NET. You can test the behavior of the MessageInterceptorin 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 MessageInterceptorinstance 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 MessageInterceptorinstance is handling the message notifications on the main application thread.

C#

Copy Code
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

Copy Code
' 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 MessageInterceptorinstance that receives a copy of all SMS messages sent from the phone number 425.555.1212. The MessageInterceptorinstance 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#

Copy Code
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

Copy Code
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 MessageInterceptorinstance that is associated with a persistent notification. The program checks to see if the persistent notification was previously created and constructs the MessageInterceptorappropriately. The MessageInterceptorreceives only those messages where the message body begins with the text "Contoso Data:". The MessageInterceptorinstance 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_OnThreadand Form1_OnClosedmethods are the same as those in Example 2 and are therefore not repeated.

C#

Copy Code
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

Copy Code
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 MessageInterceptorclass, you can easily incorporate SMS message handling into your .NET Compact Framework application. The MessageInterceptorclass 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 MessageInterceptorclass 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 MessageInterceptorclass, 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

Jim Wilson is president of JW Hedgehog, Inc. (http://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 http://pluralsight.com/blogs/jimw.