How to create a simple Managed Services for Windows Mobile

Note: A quick start tutorial can be found here.

Basic Sample: Call Logger Service

In this section a call logger service will be created. It will log the start- and endtime of a phone call as well as te according number. This can be developed pretty easy using Windows Mobile 5/6 SDK State and Notification API (SNAPI) will be used.

The basic steps for this sample project are the same like in the quick start guide.
  • Create a new smart device application project
  • Add a reference to the ManagedService.dll assembly (found in the binary download as part of the 1.0 Alpha 1 release).
  • Add a reference to the Microsoft.WindowsMobile.Status.dll assembly, which can be found in your Windows Mobile 5/6 SDK
  • Create a new class
  • Add according using statements for the newly added references
  • Derive your new class from the abstract class ManagedService.ServiceApplication
  • Change the constructor code
    • Assign a newly created Guid to your ServiceGuid property
    • Assign a new Name to your ServiceName property

Let's take a break here and let's analyze the code up to here for a second.
Creating the project and adding the references as well as adding the according using statements will be skipped, as they are pretty straight forward.

As the newly created class derives from ManagedService.ServiceApplication, it already inherits all necessary functionality for a Managed Service. The constructor of ServiceApplication alrady registers the messages for Suspend, Resume and Quit. These messages are caught internally, so that the according methods Suspend, Resume and Quit get called. These methods will be covered later in more depth.
ServiceApplication contains additionally the method Start, which runs an endless loop on Application.DoEvents() for processing pending Windows Messages.
The internal used class ServiceMessageWindow needs a Name, which is supposed to be used as the "destination" adress for Windows Messages. Instead of using a common string here the Property ServiceGuid is used for. This ensures a unique Name for the Message Window. The Property ServiceName is just for description purposes.

As seen in the Quick Start Guide this functionality nearly would be sufficient for a Managed Service.

A service should be only instantiated once. Therefore the singleton pattern is to be used:
  • Change the modifier for the constructor to private
  • Add a private static field of your class type
    • Example: private static ManagedServiceSample1 service = new ManagedServiceSample1();
  • Add a static method, which returns the instance of your service
    • Example:
        public static ManagedServiceSample1 GetInstance()
        {
            return service;
        }


The following code will be used for handling the phone calls and saving them to a text file.
  • Add a static string containing a full qualified file name
    • Example: private static string logFilePath = @"\phonelog.txt";
  • Add a static field of type DateTime
    • Example: private static DateTime startTime;
  • Add a static field of type string
    • Example: private static string phoneNumber;
  • Add an field of type SystemState and instatntiate it for the SystemProperty PhoneCallCalling
    • Example: SystemState systemState = new SystemState(SystemProperty.PhoneCallTalking);
  • Create an Changed-Eventhandler for systemState in your constructor
    • Example: systemState.Changed += new ChangeEventHandler(systemState_Changed);
  • Add the following code to your class
void systemState_Changed(object sender, ChangeEventArgs args)
{
    if (SystemState.PhoneCallTalking)
    {
        startTime = DateTime.Now;
        phoneNumber = SystemState.PhoneTalkingCallerNumber;
    }
    else
    {
        // When phone call ends write new line to the log file
        WriteLogFile();
    }
}

void WriteLogFile()
{
    FileStream fs = new FileStream(logFilePath, FileMode.Append);
    StreamWriter sr = new StreamWriter(fs);
    sr.WriteLine(string.Format("{0} -> {1} : {2}",
        startTime.ToShortTimeString(),
        DateTime.Now.ToShortTimeString(),
        phoneNumber));

    sr.Close();
    fs.Close();
}


The last missing piece for running your service is adding the Start for your Service in your Main method
  • Add the line ManagedServiceSample1.GetInstance().Start(); to your Main method

If you start your application now, and do a phone call, either using your Windows Mobile Phone, or your Windows Mobile Phone Emulator, you will see a file being created ("\phonelog.txt"). If you open your file, you will see, when your call started and when it ended as well as which number was called.

Amazing, isn't it?

Note: From an architectual point of view, the solution should consist of 2 projects - 1 project as a smart device library containing the implementation for the Service, and 1 project as a smart device console application, which acts as a host.

Extended sample: Adding custom Messages

Your service can be extended by additional custom messages. How about creating a custom message for the previous example, which deletes your Call Log file?

Adding this functionality just takes a few more lines of code, which have to be added to the previous sample:
  • Add a new constant to your class, which will be used as a custom message
public const int WM_CLEAR_LOG = WM_USER + 5005;
  • Register your custom WM_CLEAR_LOG message within your ServiceApplication in your constructor.
this.RegisterMessage(WM_CLEAR_LOG);
  • Create an EventHandler for receiving your custom message
this.OnRegisteredMessage += new RegisteredMessageReceivedEventHandler(ManagedServiceSample1_OnRegisteredMessage);
  • Create the method ManagedServiceSample1_OnRegisteredMessage for receiving your custom message
void ManagedServiceSample1_OnRegisteredMessage(ref Microsoft.WindowsCE.Forms.Message message)
{
   switch (message.Msg)
   {
      case WM_CLEAR_LOG:
              ClearLogFile();
              break;
   }
}
  • Create the method ClearLogFile for deleting the Call Log
void ClearLogFile()
{
    File.Delete(logFilePath);
}


A custom message is nothing else than a plain number. But there are restrictions. The documentation for Messages says, that custom messages have to have a higher value than 0x400(=WM_USER, see here. Values below are reserved for the OS/System.

To be absolutly sure a value of above 5000 is added.
The first 4 numbers are already in use for:
WM_START_SERVICE = WM_USER + 5001;
WM_SUSPEND_SERVICE = WM_USER + 5002;
WM_RESUME_SERVICE = WM_USER + 5003;
WM_QUIT_SERVICE = WM_USER + 5004;

These values can't be overriden or altered.
If a message is tried to be registered with one of the mentioned values, the message is skipped internally.
This has to be done to ensure the basic functionality.

Subscribing to the OnRegisteredMessage event enables capturing Window Messages. This includes custom messages as well as the automatically registered WM_* messages, registered by the ServiceApplication class.

These samples can be found in the binary download as part of the 1.0 Alpha 1 release.

Last edited May 30, 2008 at 6:07 PM by PeterNowak, version 1

Comments

No comments yet.