A Windows Service to accept messages via a web service and send them over MSN Messenger

I was working with Russell when he blogged about sending continuous build messages over MSN Messenger.  The implementation that we ended up with at the time suffered from a few drawbacks:

  1. The network we were on had a lot of congestion.  So sometimes the sign-in process to Messenger would time out and return an error to MSBuild.  This would break the CC.Net build [:(]
  2. Every time a message was sent, the code would sign-in to Messenger.  This meant that if you were a recipient you would see the following:

This would work a lot better if there was a Windows Service which signed in to Messenger and held onto the connection.  It could then accept messages from the CC.Net build process and forward them on to the Messenger network.  And this is what I've written.  The service exposes some web services, puts the messages into an in-memory queue and then has a timer which polls this queue and pushes message out over Messenger.  It was then fairly trivial to write a custom MSBuild task which is a proxy for these web services.  You can download the whole lot from here.

Below are some details on installation and configuration, along with some discussions around the technically more interesting bits.

Installation and Configuration

Installation is pretty straightforward:

  1. Copy the entire contents (files and subdirectories) of MessengerSvc\build to where you want them
  2. Run "installutil MessengerSvc.exe" (installutil.exe is part of the .Net SDK)
  3. Open the Services control panel and configure whatever service account, startup properties, etc that you like

A word of warning - I haven't done much testing with different privileges on the service account.  I can't tell you exactly what the minimum required privileges are.  If you do install and run this service then I'd be very ineterested to hear how you got on.

Configuration settings are all contained in the MessengerSvc.exe.config file.  There are comments in there, so I'm not going to describe each setting here.

Exposing the Web Services

This turned out to be fiddly but not overly complex.  We use the ApplicationHost class in the System.Web.Hosting namespace to start it all off.  This creates a new AppDomain and loads an instance of HttpListenerWrapper into it.  This class primarily does what it says on the tin, and wraps the HttpListener class. Our service then creates a background thread which makes a blocking call to HttpListenerWrapper.ProcessRequest which uses the HttpListener class to receive an HTTP request and calls HttpRuntime.ProcessRequest to perform the heavy-lifting of processing the request.

Incoming message requests accepted via web service are recorded in a singleton instance of QueueManager.  The HttpListenerWrapper class has a method which retrieves all the values held in the instance of the QueueManager and returns them.  This method allows us to transfer the requests from the secondary AppDomain (created by ApplicationHost) into our primary AppDomain.  It is called from the background thread created by the service, once the call to HttpListenerWrapper.ProcessRequest has completed.

Sending Messages Over Messenger

Given that I started with the code described by Russell (which comes from Dr.NETjes) which already includes the DotMSN library (available here), I was really standing on the shoulders of giants.  And so the code for actually sending the message over MSN Messenger deals more with time outs, validity periods, exception conditions and retry counts than it does with networking.  All this logic is contained in the QueueManager.PollMessages method.

It's worth pointing out that I did modify the MessengerWrapper class a bit to make it (a) thread-safe; and (b) more aware of the current state of its connection.  A good example of this is in the SendMessage method, where if a login operation is underway then we attempt to wait for it to complete.  And if there isn't a login operation in progress but we aren't logged in, then we start a login operation.  Logic like this is a lot more important in a service that in a straightforward single-threaded executable.

Service Logging

I've used log4net within the service.  There isn't a config file in the download, but you can find out how to write one by going to the official log4net site.  I've tried to aim for some consistency, so you may want to consider logging Error messages from the MessengerSvc.HostService logger into the event log, or some other important place.

Using the Service From MSBuild

Creating a custom build task which makes web service calls was really quite trivial.  If you're looking for a good primer on how to create a custom task, try this one from Waaargh.Net.  There is a basic MSBuild script in the download, which looks like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets = "SendMessage">
   <
UsingTask TaskName="MessengerNotification.Tasks.MsnNotification" AssemblyName="MessengerNotification.Tasks" />
   <
Target Name="SendMessage">
      <
MsnNotification 
            Message="Hello, World!"
            Recipients="john.rayner@conchango.com"
            />
   </
Target>
</
Project>

Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets = "SendMessage">
   <
UsingTask TaskName="MessengerNotification.Tasks.MsnNotification" AssemblyName="MessengerNotification.Tasks" />
   <
Target Name="SendMessage">
      <
MsnNotification 
            Message="Hello, World!"
            Recipients="john.rayner@conchango.com"
            />
   </
Target>
</
Project>

 

One of the benefits of using a service is that you don't need to specify the Messenger login details in your build script.  They are located in the service's config file instead. 

Future Enhancements

  1. It occurred to me after writing this code (once I learnt a bit about WCF) that we can replace the entire web service bit with a few choice WCF calls.  If I get time to do this, then I'll make another post about it.
  2. When writing the code to push the messages out onto Messenger, it started to become clear how you could write a Messenger bot, i.e. an application that used Messenger as its UI.  Users could then connect via Messenger to send commands / queries to the service.  This is similar to something we already have at Conchango for accessing the corporate phonebook.
  3. A professional installer is a nice-to-have.  Something that would prompt the user for various details, update the config file with these values and install the service automatically.

If you do make use this service, then please drop me a line (or a comment) to let me know how it works for you.

 

blog comments powered by Disqus