Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Windows Azure, .NET Services from a Windows Mobile Widget

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

That’s right, if you hadn’t guessed what I was working on with my previous posts on .NET Services (Tokens, Queues I, II, III, Routers I & II) I’ve been working towards accessing .NET Services from within a widget. I must apologise for the following as it’s just a code dump of a sample widget that shows how to create a queue and subscribe to an existing router.  Note that if you are going to use this on a number of devices you need to create a unique queue on each device, otherwise you’ll end up subscribing to the router multiple times – this will cause duplicate messages in your queue and contention between the devices dequeuing the messages.

To get this widget to work, simply create a widget (Developing Widgets for Windows Mobile 6.5) and then copy the following code into your html file.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml" >
<head>
    <title>Widget Chat</title>

      <script type="text/javascript">
          var theToken;
          var theRouter = "myrouter"
          var widgetQueue = "widget";

          function connect() {
              var solutionName = document.getElementById("SolutionNameText");
              var solutionPassword = document.getElementById("SolutionPasswordText");

              alert('Solution Name: ' + solutionName.value);
              alert('Solution Password: ' + solutionPassword.value);
              httpGetAuthenticationToken(solutionName.value, solutionPassword.value, createQueue);
          }

          function createQueue() {
              alert('Creating Queue');
              httpCreateQueue(theToken, widgetQueue, subscribeToRouter);
          }

          function subscribeToRouter() {
              alert('Subscribing to Router');
              httpSubscribeQueueToRouter(theToken, theRouter, widgetQueue, pollForMessage);
          }

          function pollForMessage() {
              httpDequeueMessage(theToken, widgetQueue, messageReceived, pollForMessage);
          }

          function messageReceived(message) {
              alert('Message Received: ' + message);
              pollForMessage();
          }

          function send() {
              var messageBox = document.getElementById("Message");
              httpRouteMessage(theToken, theRouter, messageBox.value);
          }

          function httpGetAuthenticationToken(username, password, callback) {
              try {
                  http = new XMLHttpRequest();
                  http.open("GET", "
https://accesscontrol.windows.net/IssueToken.aspx?u=" + username + "&p=" + password);
                  http.onreadystatechange = function() {
                      if (http.readyState == 4) {
                          if (http.status == 200) {
                              theToken = http.responseText;
                              alert('Authenticated:' + theToken);
                              callback();
                          }
                          else {
                              alert('Authentication failed');
                          }
                      }
                  }
                  http.send();
              }
              catch (e) {
                  alert(e.message);
              }
          }

          function httpCreateQueue(token, queue, callback) {
              var policy = "<entry xmlns=\"
http://www.w3.org/2005/Atom\">" +
  "<QueuePolicy xmlns=\"
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect\">" +
    "<ExpirationInstant>2009-09-03T01:00:00.0000000Z</ExpirationInstant>" +
  "</QueuePolicy>" +
"</entry>";

              http = new XMLHttpRequest();
              http.open("POST", "
https://blogsample.servicebus.windows.net/" + queue, false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 201) {
                          var created = http.responseText;
                          alert('queue created: ' + created);

                      }
                      else {
                          alert('queue not created - ' + http.status);
                      }
                      callback();
                  }
              }
              http.send(policy);
          }

          function httpGetServiceList(token) {
              http = new XMLHttpRequest();
              http.open("GET", "
https://blogsample.servicebus.windows.net/", false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      output = http.responseText;
                      alert(output);
                      // Do something with the service list
                      httpRenewQueue(token, "mobilequeue", output);
                  }
              }
              http.send();
          }

          function httpRenewQueue(token, queue, getResponse) {
              xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
              xmlDoc.async = "false";
              xmlDoc.loadXML(getResponse);

              var entry;
              var entries = xmlDoc.getElementsByTagName("title");
              var i;
              for (i = 0; i < entries.length; i++) {
                  if (entries[ i ].childNodes[0].nodeValue == queue) {
                      entry = entries[ i ].parentNode;
                  }
              }
              entry.getElementsByTagName("ExpirationInstant")[0].childNodes[0].nodeValue = "2009-08-31T09:56:25.2951184Z";

              http = new XMLHttpRequest();
              http.open("PUT", "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue)", false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      var output = http.responseText;
                      alert('renew: ' + output);
                      httpEnqueueMessage(token, queue, "Hi Everyone");
                  }
              }
              http.send(entry.xml);
          }

          function httpDeleteQueue(token, queue) {
              http = new XMLHttpRequest();
              http.open("DELETE", "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue)", false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 200) {
                      var deleted = http.responseText;
                      alert('deleted');
                  }
              }
              http.send();
          }

          function httpEnqueueMessage(token, queue, message) {
              http = new XMLHttpRequest();
              http.open("POST", "
https://blogsample.servicebus.windows.net/" + queue, false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.setRequestHeader("Content-type", "text/plain;charset=utf-8");
              http.onreadystatechange = function() {
                  if (http.readyState == 4 && http.status == 202) {
                      var output = http.responseText;
                      alert('enqueue: ' + output)
                      httpDequeueMessage(token, queue);
                  }
              }
              http.send(message);
          }

          function httpDequeueMessage(token, queue, callback, noMessageCallback) {
              http = new XMLHttpRequest();
              http.open("DELETE", "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue/head)?encoding=asreply&maxmessages=1&timeout=30", true);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 200) {
                          var output = http.responseText;
                          callback(output);
                      }
                      else {
                          noMessageCallback();
                      }
                  }
              }
              http.send();
          }

          function httpRouteMessage(token, router, message) {
              http = new XMLHttpRequest();
              http.open("POST", "
https://blogsample.servicebus.windows.net/" + router, false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.setRequestHeader("Content-type", "text/plain;charset=utf-8");
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 202) {
                          var output = http.responseText;
                          alert('Send ok!');

                      }
                      else {
                          alert('Unable to send');
                      }
                  }
              }
              http.send(message);
          }

          function httpSubscribeQueueToRouter(token, router, queue, callback) {
              var policy = "<entry xmlns=\"
http://www.w3.org/2005/Atom\">" +
              "<link rel='alternate' href='
https://blogsample.servicebus.windows.net/" + queue + "' />" +
  "<HttpHeaders xmlns=\"
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect\">" +
    "<HttpHeader name='X-MS-Identity-Token' value='" + token + "' />" +
  "</HttpHeaders>" +
"</entry>";

              http = new XMLHttpRequest();
              http.open("POST", "
https://blogsample.servicebus.windows.net/" + router + "/!(router/subscriptions)", false);
              http.setRequestHeader("X-MS-Identity-Token", token);
              http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
              http.onreadystatechange = function() {
                  if (http.readyState == 4) {
                      if (http.status == 200) {
                          var created = http.responseText;
                          alert('subscribed ok:' + created);
                          callback();
                      }
                      else {
                          alert('not subscribed ok' + http.status);
                      }
                  }
              }
              http.send(policy);
          }

      </script>
</head>
<body>
    <p>   
    Solution Name:<br />
    <input id="SolutionNameText" type="text" />
        <br />
    Solution Password:<br />
    <input id="SolutionPasswordText" type="password" /><br />
    <Button onclick="connect()">Connect</Button>
    </p>

    <p>   
    Message:
        <br />
    <input id="Message" type="text" /><br />
    <Button onclick="send()">Send</Button>
    </p>

</body>
</html>

Windows Azure, Microsoft .NET Services – Working with Routers (II)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

In the previous post on Working with Routers I showed how you can create a router using a Http Post.  A router is only really useful when it can actually route messages. To do this it needs one or more subscribers.  As you can imagine creating a subscriber (as with other creates) involves a Http Post.  When we created the router one of the links that was returned was the subscriptions url. All you really need to do to create a subscriber is send a Http Post to this url. Of course you need to send the url where you want messages to be routed to, and any other information that is required for the router to route messages.  The upshot is that you end up sending an entry that looks similar to the following:

<entry xmlns="http://www.w3.org/2005/Atom">
  <link rel="alternate" href="
https://blogsample.servicebus.windows.net/myqueue" />
  <HttpHeaders xmlns="
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
    <HttpHeader name="X-MS-Identity-Token" value="zpleGHYQzEiRUXkdFd7UN+gBbBzSIuW5Bc2hIA==" />
  </HttpHeaders>
</entry>

Of course, this needs to be sent as a Http Post:

private static string SubscribeQueueToRouter(string token, string router, string queue)
{
    string subscriptionsUri = "
https://blogsample.servicebus.windows.net/" + router + "/!(router/subscriptions)";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(subscriptionsUri);
    request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "POST";
    request.ContentType = "application/atom+xml;type=entry;charset=utf-8";

    XElement content = new XElement(XName.Get("entry", "http://www.w3.org/2005/Atom"),
                            new XElement(XName.Get("link", "
http://www.w3.org/2005/Atom"),
                                new XAttribute("rel", "alternate"),
                                new XAttribute("href", "
https://blogsample.servicebus.windows.net/" + queue)),
                            ServicesElement("HttpHeaders",
                                ServicesElement("HttpHeader",
                                    new XAttribute("name","X-MS-Identity-Token"),
                                    new XAttribute("value",token))));

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
        writer.Write(content);
        writer.Flush();
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

In this example the link url provided is the tail of the queue (ie the url where you enqueue messages).  The token is included in a HttpHeader element so that it can be routed as part of sending the message to the queue. You should expect a 200, Ok response. If you want to get a list of subscribers you can simply issue a Http Get to the subscriptions url.  This will return a feed of the subscribers.

<feed xmlns="http://www.w3.org/2005/Atom">
   <title type="text">Subscriptions</title>
   <id>uuid:eb256797-2832-4f2b-afd9-ba3e4cfeeb7b;id=1869</id>
   <updated>2009-09-01T06:20:00Z</updated>
   <link rel="self" href="https://blogsample.servicebus.windows.net/myrouter/!(router/subscriptions)"></link>
   <entry>
     <id>uuid:eb256797-2832-4f2b-afd9-ba3e4cfeeb7b;id=1870</id>
      <title type="text">urn:uuid:90d62d9e-2a13-446c-951a-72046101fa17</title>
      <updated>2009-09-01T06:20:00Z</updated>
      <link rel="alternate" href="https://blogsample.servicebus.windows.net/myqueue"></link>
      <link rel="self" href="https://blogsample.servicebus.windows.net/myrouter/!(router/subscriptions/urn:uuid:90d62d9e-2a13-446c-951a-72046101fa17)"></link>
      <content type="text"></content>
      <Expires xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">2009-09-02T06:18:56.9899631Z</Expires>
      <NotifyTo xmlns="http://schemas.xmlsoap.org/ws/2004/08/eventing">
         <Address xmlns="http://www.w3.org/2005/08/addressing">https://blogsample.servicebus.windows.net/myqueue</Address>
      </NotifyTo>
      <HttpHeaders xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
         <HttpHeader name="X-MS-Identity-Token" value="zpleGHYQzEiRUXkdFd7UN+gBbBzSIuW5Bc2hIA=="></HttpHeader>
      </HttpHeaders>
   </entry>
   <entry>
      <id>uuid:eb256797-2832-4f2b-afd9-ba3e4cfeeb7b;id=1871</id>
      <title type="text">urn:uuid:d27a29ab-46e4-49ca-9d1e-cd05bdc24d42</title>
      <updated>2009-09-01T06:20:00Z</updated>
      <link rel="alternate" href="https://blogsample.servicebus.windows.net/myqueue"></link>
      <link rel="self" href="https://blogsample.servicebus.windows.net/myrouter/!(router/subscriptions/urn:uuid:d27a29ab-46e4-49ca-9d1e-cd05bdc24d42)"></link>
      <content type="text"></content>
      <Expires xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">2009-09-02T06:01:01.0635156Z</Expires>
      <NotifyTo xmlns="http://schemas.xmlsoap.org/ws/2004/08/eventing">
         <Address xmlns="http://www.w3.org/2005/08/addressing">https://blogsample.servicebus.windows.net/myqueue</Address>
      </NotifyTo>
      <HttpHeaders xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
         <HttpHeader name="X-MS-Identity-Token" value="J+rsqHMQzEgxQ3yu3JwyDv3J8wNgIQybBoxtng=="></HttpHeader>
      </HttpHeaders>
   </entry>
</feed>

Now the last thing to do is to send a message to the router. In the above example a message sent to the router will get routed through to the subscribed queue. As we did with enqueuing a message on a queue, to send a message to the router you do a Http Post to the alterate url of the router (this is also the original url that you used to create the router).

private static string SendMessageToRouter(string token, string router, string message)
{
    string routerUri = "
https://blogsample.servicebus.windows.net/" + router;
    // send
    HttpWebRequest request = HttpWebRequest.Create(routerUri) as HttpWebRequest;
    request.Method = "POST";
    request.Headers.Add("X-MS-Identity-Token", token);
    request.ContentType = "text/plain;charset=utf-8";

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
        writer.Write(message);
        writer.Flush();
    }

    using (var response = request.GetResponse())
    {
        return (response as HttpWebResponse).StatusCode.ToString();
    }
}

Again you should expect a 202, Accepted response. To retrieve the message off the destination queue, simply send a Http Delete message as discussed in Working with Queues (III).

Windows Azure, Microsoft .NET Services – Working with Routers (I)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

The last couple of posts have covered working with .NET Services queues (parts I, II and III). Now we move on to cover routers. Like I did previously I’ll cover creating and working with routers, before we move on to working with them in conjunction with queues.

To create a router you send a Http Post to the url that you want the router to reside at, supplying a RouterPolicy entry.  The url has to belong to your solution url, so for example https://blogsample.servicebus.windows.net/myrouter. An example RouterPolicy would look like the following:

<entry xmlns="http://www.w3.org/2005/Atom">
  <RouterPolicy xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
    <Discoverability>Public</Discoverability>
    <ExpirationInstant>2009-09-01T05:18:06.9677603Z</ExpirationInstant>
    <TransportProtection>None</TransportProtection>
    <MaxSubscribers>2147483647</MaxSubscribers>
    <MessageDistribution>AllSubscribers</MessageDistribution>
  </RouterPolicy>
</entry>

Now to send this:

private static string HttpCreateRouter(string token, string router)
{
    string routerUri = "
https://blogsample.servicebus.windows.net/" + router;

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(routerUri);
    request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "POST";
    request.ContentType = "application/atom+xml;type=entry;charset=utf-8";

    XElement content = new XElement(XName.Get("entry", "http://www.w3.org/2005/Atom"),
                            ServicesElement("RouterPolicy",
                                ServicesElement("Discoverability","Public"),
                                ServicesElement("ExpirationInstant", DateTime.UtcNow.AddMinutes(30)),
                                ServicesElement("TransportProtection", "None"),
                                ServicesElement("MaxSubscribers", "2147483647"),
                                ServicesElement("MessageDistribution", "AllSubscribers")));

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
            writer.Write(content);
            writer.Flush();
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

private static XElement ServicesElement(string element, params object[] content)
{
    return new XElement(XName.Get(element, "
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect"), content);
}

A note of warning… for some reason the order of the xml elements for the RouterPolicy does matter.  Submitting them in a different order appears to cause a duplicate RouterPolicy entry. This will most likely be resolved for the production release but in the meantime if you are seeing weird RouterPolicy entries being returned this may be the cause.

When you run this code the response (assuming it is successful) should be a 201, Created. Subsequent Posts to this url will result in a 202, Accepted – note however this does not update or alter the router. In addition to the 201 status, the Post also returns information about the created router:

<entry xmlns="http://www.w3.org/2005/Atom">
   <id>uuid:0ae5cae9-6f3f-4fcc-8377-83f34c106124;id=1546</id>
   <title type="text">myrouter</title>
   <updated>2009-09-01T04:48:41Z</updated>
   <link rel="alternate" href="https://blogsample.servicebus.windows.net/myrouter/"/>
   <link rel="self" href="https://blogsample.servicebus.windows.net/myrouter/!(router)"/>
   <link rel="subscriptions" href="https://blogsample.servicebus.windows.net/myrouter/!(router/subscriptions)"/>
   <RouterPolicy xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <Discoverability>Public</Discoverability>
      <ExpirationInstant>2009-09-01T05:18:06.9677603Z</ExpirationInstant>
      <TransportProtection>None</TransportProtection>
      <MaxMessageSize>61440</MaxMessageSize>
      <BufferTimeout>PT10S</BufferTimeout>
      <MaxBufferLength>0</MaxBufferLength>
      <MaxBufferCapacity>0</MaxBufferCapacity>
      <MaxSubscribers>50</MaxSubscribers>
      <MessageDistribution>AllSubscribers</MessageDistribution>
      <PushDeliveryRetries>3</PushDeliveryRetries>
      <PushDeliveryTimeout>PT30S</PushDeliveryTimeout>
   </RouterPolicy>
</entry>

As with when you create a queue, the response contains a number of links.

alternate – This is the url you will use when placing new messages on the router. 

self – This is the url you will now use to update, renew and delete the router.

subscriptions – This is the url that you use when subscribing to the router.

As you can see the process of creating a router is roughly the same as for a queue, with the only big difference being the RouterPolicy you need to supply.  Querying, renewing and deleting a router is done exactly the same way as for a queue, with the exception you need to use the self url returned when you create the router (which ends in (router) instead of (queue)).

Windows Azure, Microsoft .NET Services – Working with Queues (III)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

In my previous posts, Working With Queues (I) & Working with Queues (II), I showed how you can create, query, renew and delete queues with the Windows Azure, Microsoft .NET Services. Now for the interesting stuff – let’s enqueue and dequeue messages.

As you can imagine enqueuing equates to Post(ing) a message to the tail of the queue, whilst dequeuing equates to delete(ing) from the head of the queue. In code, this is:

private static string EnqueueMessage(string token, string queue, string message)
{
    string queueUri = "
https://blogsample.servicebus.windows.net/" + queue;

    // send
    HttpWebRequest request = HttpWebRequest.Create(queueUri) as HttpWebRequest;
    request.Method = "POST";
    request.Headers.Add("X-MS-Identity-Token", token);
    request.ContentType = "text/plain;charset=utf-8";

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
        writer.Write(message);
        writer.Flush();
    }

    using (var response = request.GetResponse())
    {
        return (response as HttpWebResponse).StatusCode.ToString();
    }
}

private static string DequeueMessage(string token, string queue)
{
    string queueUri = "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue/head)?encoding=asreply&maxmessages=1&timeout=30";

    HttpWebRequest request = HttpWebRequest.Create(queueUri) as HttpWebRequest;
    request.ConnectionGroupName = "dequeue";
    request.Method = "DELETE";
    request.Headers.Add("X-MS-Identity-Token", token);
    request.ContentLength = 0;

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

To enqueue a message you send a Http Post to the alternate link (otherwise known as the tail of the queue), ie https://blogsample.servicebus.windows.net/myqueue. You should get back a 202, Accepted response. When you are dequeuing a message you send a Http Delete to the queuehead link (otherwise known as the head of the queue), ie https://blogsample.servicebus.windows.net/myqueue/!(queue/head). You should get back a 200, Ok response, with the message in the content of the response.

As part of the Http Delete you can specify a timeout, in this case 30 seconds. This determines how long the request will wait for a message to appear on the queue before returning.  If a message appears within this timeout you will get the message and a 200 response.  If not, you will get a 204, No Content response.

And now to cover enqueuing and dequeuing in javascript:

function httpEnqueueMessage(token, queue, message) {
    http = new XMLHttpRequest();
    http.open("POST", "
https://blogsample.servicebus.windows.net/" + queue, false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.setRequestHeader("Content-type", "text/plain;charset=utf-8");
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 202) {
            var output = http.responseText;
            // Do something…
        }
    }
    http.send(message);
}

function httpDequeueMessage(token, queue) {
    http = new XMLHttpRequest();
    http.open("DELETE", "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue/head)?encoding=asreply&maxmessages=1&timeout=20", false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200) {
            var output = http.responseText;
            // Do something...
        }
    }
    http.send();
}

Windows Azure, Microsoft .NET Services – Working with Queues (II)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

In my previous post, Working With Queues (I), I showed how you can create a queue using a Http Post message against the Windows Azure, Microsoft .NET Services. Now that we’ve created a queue, let’s look at how you can query, renew and delete your queue.

To query for the existence of a queue (and remember that when a queue expires it is no longer available) you just need to send a Http Get to the parent url. So for example if you had a queue https://blogsample.servicebus.windows.net/application1/myqueue you would send the Get to https://blogsample.servicebus.windows.net/application1. In our case because the queue we created previously was https://blogsample.servicebus.windows.net/myqueue we need to send the Http Get to https://blogsample.servicebus.windows.net/.

public static string GetServicesListing(string token)
{
    string listingUri = "
https://blogsample.servicebus.windows.net/";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(listingUri);
    request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "GET";
    request.ContentLength = 0;

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        var output=  reader.ReadToEnd();
        return output;
    }
}

When you run this code the response (assuming it is successful) should be a 200, Ok. In addition to the 200 status, the Get also returns information about services available under the url that the Get was sent to:

<feed xmlns="http://www.w3.org/2005/Atom">
   <title type="text">Publicly Listed Services</title>
   <subtitle type="text">This is the list of publicly-listed services currently available</subtitle>
   <id>uuid:c285faa1-b963-4668-918f-af7b99ec19ea;id=3944</id>
   <updated>2009-08-31T00:30:31Z</updated>
   <generator>Microsoft® .NET Services - Service Bus</generator>
   <entry>
      <id>uuid:c285faa1-b963-4668-918f-af7b99ec19ea;id=3945</id>
      <title type="text">myqueue</title>
      <updated>2009-08-31T00:30:31Z</updated>
      <link rel="alternate" href="https://blogsample.servicebus.windows.net/myqueue/"/>
      <link rel="self" href="https://blogsample.servicebus.windows.net/myqueue/!(queue)"/>
      <link rel="queuehead" href="https://blogsample.servicebus.windows.net/myqueue/!(queue/head)"/>
      <link rel="queuecontrol" href="https://blogsample.servicebus.windows.net/myqueue/!(queue/control)"/>
      <QueuePolicy xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
         <ExpirationInstant>2009-08-31T03:56:25.2951184Z</ExpirationInstant>
         <MaxMessageSize>61440</MaxMessageSize>
         <EnqueueTimeout>PT10S</EnqueueTimeout>
         <MaxConcurrentReaders>10</MaxConcurrentReaders>
         <MaxDequeueRetries>3</MaxDequeueRetries>
         <MaxMessageAge>PT10M</MaxMessageAge>
         <MaxQueueLength>2147483647</MaxQueueLength>
         <MaxQueueCapacity>10485760</MaxQueueCapacity>
      </QueuePolicy>
   </entry>
</feed>

And for the javascript version:

function httpGetServiceList(token) {
    http = new XMLHttpRequest();
    http.open("GET", "
https://blogsample.servicebus.windows.net/", false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200) {
            output = http.responseText;
            // Do something with the service list
        }
    }
    http.send();
}

As you will have noticed, queues have an ExpirationInstant which is the instance when your queue will expire and be unavailable.  In most cases this is not that desirable so you will want to periodically renew it.  You do this by sending a Http Put to the self link address (ie https://blogsample.servicebus.windows.net/myqueue/!(queue)).  Instead of just sending a new QueuePolicy you need to get the appropriate entry out of the service feed (retrieved using the Http Get we just discussed), update the QueuePolicy with the new expiration instant and then submit the whole entry in the content of the Http Put.

 

public static string RenewQueue(string token, string queue)
{
    string listing = GetServicesListing(token);
    XElement feed = XElement.Parse(listing);
    var content = (from entry in feed.Descendants(XName.Get("entry", "
http://www.w3.org/2005/Atom"))
                  where entry.Element(XName.Get("title", "http://www.w3.org/2005/Atom")).Value == queue
                  select entry).FirstOrDefault();
    if (content == null)
    {
        return null;
    }

    // Update the expiration time
    content.Descendants(ServicesElementName("ExpirationInstant")).FirstOrDefault().SetValue(DateTime.UtcNow.AddMinutes(30));

    string queueUri = "https://blogsample.servicebus.windows.net/" + queue + "/!(queue)";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(queueUri);
    request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "PUT";
    request.ContentType = "application/atom+xml;type=entry;charset=utf-8";

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
        writer.Write(content);
        writer.Flush();
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}
private static XName ServicesElementName(string element)
{
    return XName.Get(element, "
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect");
}

Here you should expect a 200, Ok response with the updated entry being returned in the response (you can use this to double-check the queue attributes were updated correctly). And now the same but in javascript:

function httpRenewQueue(token, queue, getResponse) {
    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async = "false";
    xmlDoc.loadXML(getResponse);

    var entry;
    var entries = xmlDoc.getElementsByTagName("title");
    var i;
    for (i = 0; i < entries.length; i++) {
        if (entries[i].childNodes[0].nodeValue == queue) {
            entry = entries[i].parentNode;
        }
    }
    entry.getElementsByTagName("ExpirationInstant")[0].childNodes[0].nodeValue="2009-08-31T03:56:25.2951184Z";

    http = new XMLHttpRequest();
    http.open("PUT", "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue)", false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
    http.onreadystatechange = function() {
    if (http.readyState == 4 && http.status == 200)
        {
            var output = http.responseText;
            // Do something…
        }
    }
    http.send(entry.xml);
}

Note that in these examples there is NO error handling and the expiration is hard coded – you will need to adjust this otherwise you will get a 500 Internal Server error if the expiration is in the past.

Ok, so finally you need to be able to delete your queue.  This is done by sending a Http Delete message to the Self link, as follows:

public static string DeleteQueue(string token, string queue)
{
    string queueUri = "
https://blogsample.servicebus.windows.net/" + queue + "/!(queue)";

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(queueUri);
    request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "DELETE";
    request.ContentLength = 0;
    using (var response = request.GetResponse())
    {
        return (response as HttpWebResponse).StatusCode.ToString();
    }
}

One important point here is that you must set the ContentLength to 0.  You should expect a 200, Ok if the queue is deleted.  For both the renew (ie Http Put) and delete (ie Http Delete) if the queue has expired, you can expect a 404, Not found exception to be raised.  Lastly, let’s see the delete in javascript:

function httpDeleteQueue(token, queue) {
    http = new XMLHttpRequest();
    http.open("DELETE", "
https://blogsample.servicebus.windows.net/" + queue +"/!(queue)", false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200) {
            var output = http.responseText;
            // Do something….
        }
    }
    http.send();
}

Windows Azure, Microsoft .NET Services – Working With Queues (I)

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

In my previous post on Acquiring an Authentication Token I demonstrated how you can use a Http Get operation to acquire an Authentication Token.  This token can be used to create, modify, delete and send messages to both queues and routers as part of the Windows Azure, Microsoft .NET Services. Again, whilst there are .NET libraries for working with both queues and routers in this post we’ll still to the basic Http messages so that you can see what’s going on under the hood.

The first place to start is to create a queue. To do this you send a Http Post to the url that you want the queue to reside at, supplying a QueuePolicy entry.  The url has to belong to your solution url, so for example https://blogsample.servicebus.windows.net/myqueue. There are a number of attributes you can set in the QueuePolicy you supply but as a minimum you need to set the ExpirationInstant, which as you can imagine is the instant that the queue will expire and self-destruct (unless renewed). An example QueuePolicy would look like the following:

<entry xmlns="http://www.w3.org/2005/Atom">
  <QueuePolicy xmlns="http://schemas.microsoft.com/netservices/2009/05/servicebus/connect">
    <ExpirationInstant>2009-08-30T23:56:25.2951184Z</ExpirationInstant>
  </QueuePolicy>
</entry>

Now to send this:

public static string HttpCreateQueue(string token, string queue)
{
    string queueUri = "
https://blogsample.servicebus.windows.net/" + queue;

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(queueUri);
   request.Headers.Add("X-MS-Identity-Token", token);
    request.Method = "POST";
    request.ContentType = "application/atom+xml;type=entry;charset=utf-8";

    XElement content = new XElement(XName.Get("entry", "http://www.w3.org/2005/Atom"),
                            ServicesElement("QueuePolicy",
                                ServicesElement("ExpirationInstant", DateTime.UtcNow.AddMinutes(30))));

    using (var requestStream = request.GetRequestStream())
    using (var writer = new System.IO.StreamWriter(requestStream))
    {
            writer.Write(content);
            writer.Flush();
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

private static XElement ServicesElement(string element, object content)
{
    return new XElement(XName.Get(element, "
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect"), content);
}

Note that you need to supply the authentication token using the X-MS-Identity-Token header.  Here we have built up the QueuePolicy as an XElement, you can of course use the .NET library to generate this for you. When you run this code the response (assuming it is successful) should be a 201, Created. Subsequent Posts to this url will result in a 202, Accepted – note however this does not update or alter the queue. In addition to the 201 status, the Post also returns information about the created queue:

<entry xmlns="http://www.w3.org/2005/Atom">
   <id>uuid:11cdc611-62fb-49d1-8dee-e47bf9cc93d8;id=1407</id>
   <title type="text">myqueue</title>
   <updated>2009-08-30T23:44:13Z</updated>
   <link rel="alternate" href="
https://blogsample.servicebus.windows.net/myqueue/"/>
   <link rel="self" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue)"/>
   <link rel="queuehead" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue/head)"/>
   <link rel="queuecontrol" href="
https://blogsample.servicebus.windows.net/myqueue/!(queue/control)"/>
   <QueuePolicy xmlns="
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <ExpirationInstant>2009-08-31T00:14:13.2742033Z</ExpirationInstant>
      <MaxMessageSize>61440</MaxMessageSize>
      <EnqueueTimeout>PT10S</EnqueueTimeout>
      <MaxConcurrentReaders>10</MaxConcurrentReaders>
      <MaxDequeueRetries>3</MaxDequeueRetries>
      <MaxMessageAge>PT10M</MaxMessageAge>
      <MaxQueueLength>2147483647</MaxQueueLength>
      <MaxQueueCapacity>10485760</MaxQueueCapacity>
      </QueuePolicy>
</entry>

The important elements to note from this are the links.  These correspond to the urls where you will perform actions on the queue.

alternate – This is the url you will use when placing new messages on the queue.  You can think of this as the tail of the queue.

self – This is the url you will now use to update, renew and delete the queue.

queuehead – This is the url where you will pop messages off the queue.  As the name implies, this is the head of the queue

queuecontrol – Currently unused, it is anticipated that this will be used for queue message control.

We’ll come back to these in a minute but let’s first see what this would look like in javascript (again remember this will only work in the context of Gadgets and Widgets where the cross site scripting rules are relaxed):

function httpCreateQueue(token, queue) {
    var policy = "<entry xmlns=\"
http://www.w3.org/2005/Atom\">" +
  "<QueuePolicy xmlns=\"
http://schemas.microsoft.com/netservices/2009/05/servicebus/connect\">" +
    "<ExpirationInstant>2009-08-31T03:56:25.2951184Z</ExpirationInstant>" +
  "</QueuePolicy>" +
"</entry>";

    http = new XMLHttpRequest();
    http.open("POST", "
https://blogsample.servicebus.windows.net/" + queue, false);
    http.setRequestHeader("X-MS-Identity-Token", token);
    http.setRequestHeader("Content-type", "application/atom+xml;type=entry;charset=utf-8");
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 201) {
            var response = http.responseText;
            // Do stuff with the response message
        }
    }
    http.send(policy);
}

One observation is that the service bus is quite sensitive when it comes to the formatting and contents of the request, and like all secure services if you get something wrong it will give you a 500 Internal Server error with no additional information to diagnose what’s going wrong.  In my case the number one thing that was biting me was the ExpirationInstant.  This needs to be UTC time and in the future :-)

Windows Azure, Microsoft .NET Services – Acquiring an Authentication Token

[This post is based on the Windows Azure Tools for Microsoft Visual Studio July 2009 CTP]

Before we jump into looking at authentication tokens, why you need them and how to acquire them, let’s take a quick refresher on how to get up and running with the Windows Azure, Microsoft .NET Services. To begin with you have to request an account; once you receive your welcome email you need to sign in to the Windows Azure portal where you can then view all your .NET Services solutions and subscriptions.

image

To start working with .NET Services you need to create a solution by selecting the Add Solution button. You will be prompted to enter a name for your solution. It’s important to think about the name of your solution as you can’t (currently at least) change it. The solution name becomes the first segment in the url that you will use to access elements of the .NET Services - https://blogsample.servicebus.windows.net/. After entering a solution name you should use the Validate Name link to see whether the name you have entered is in use.

image

Once you have successfully created your solution you will be returned to the main portal screen.  Initially your new solution will appear in the list of solutions with “Activating…” alongside it. If you hit the refresh button after a few seconds (perhaps a couple of minutes depending on how busy the server is) you will see that your solution is available for use.

image

You can use the three links alongside your newly created solution to manage permissions to your solution (Access Control Service), access the Service Bus Registry or manage the Credentials for your solution.  Before we go on you will need to go into Credentials and specify a Solution Password – this is required for you to programmatically access your .NET Services solution.

image

Now that your solution is setup, lets get into working with your .NET Services solution. Nearly anything you do with your solution requires you to supply an Authentication Token. An Authentication Token can be requested from the Microsoft .NET Access Control Service by supplying a set of credentials in a number of different forms.  For the purpose of this post I’ll be using the username/password option which will mean that you can authenticate using whatever technology stack you feel most comfortable with. Using the .NET library this can be done in just a few lines of code. However, sometimes it’s useful to see what’s going on under the covers, so let’s look at how you can acquire a token using just Http messages.

Essentially acquiring a Authentication Token can be done by sending a Http Get to http://accesscontrol.windows.net/issuetoken.aspx, supplying the solution name and password as query parameters. For example I could enter https://accesscontrol.windows.net/issuetoken.aspx?u=BlogSample&p=<password> into a web browser and would get back a page containing a token “ELp6ymQPzEiKbwgyTg+cQKvvhGVm0oOnGqO1kA==”. To do this in code you just need to create a webrequest to this url.

public static string HttpGetAuthenticationToken(string username, string password)
{
    string requestUri = string.Format("
https://accesscontrol.windows.net/issuetoken.aspx?u={0}&p={1}",
                                    username, Uri.EscapeDataString(password));

    var request = WebRequest.Create(requestUri);
    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var reader = new StreamReader(responseStream))
    {
        return reader.ReadToEnd();
    }
}

You can make a similar request from javascript (note that due to cross-domain scripting this will only work from Windows Gadgets or Windows Mobile Widgets where the normal restrictions don’t apply).

function httpGetAuthenticationToken(username, password) {
    http = new XMLHttpRequest();
    http.open("GET", "
https://accesscontrol.windows.net/IssueToken.aspx?u=" + username + "&p=" + password, false);
    http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200) {
            var token = http.responseText;
            // Do something with the token....
        }
    }
    http.send();
}

New Builds of Windows Mobile 6.5

Earlier this week I noticed a post on MoDaCo talking about newer builds of Windows Mobile 6.5.  By newer, I’m of course comparing the builds to what’s publically available in the emulator images as part of the Windows Mobile 6.5 Developer Toolkit. As I’m currently wielding a HTC Touch Pro device (which for the record was one of the worst purchase decisions I’ve made due to the lack of support from both Expansys Australia and HTC) I figured I’d take a look at some of the xda-developer roms that they’ve been cooking with the latest Windows Mobile 6.5 bits.  Here are just a few screen shots from the NATF_WM6.5_TP_Lite_v5.5A ROM

Disclaimer: Please note I would NOT recommend trying cooked roms as they have the potential to brick your device – do so at your own peril. This post is just to highlight some of the new functionality that we may be expecting in production WM6.5 devices later this year.

image image

1) Notification bar – you will notice that the notification area at the top of the screen is a little smaller than before. It’s also missing the start icon.  Clicking any where in the notification area will show an enlarged notification bar (second image) where you can then click on the icon you actually want to access.

2) Start – this is no longer in the notification area, appearing instead in the SIP/Menu bar at the bottom of the screen.  This is applied throughout the device, not just the today screen.

image

3) Ok – this has also been moved out of the notification area and placed in the SIP/menu bar.

4) Gestures for Tabs –gesturing left and right navigates between tabs.

image

5) Slide Tabs – instead of tabs appearing at the bottom of the screen, they now appear as “previous and next” headings at the top of the screen.  You can use left and right gestures anywhere on the form to navigate to previous or next tabs. Note that they are cyclic so you can keep going “next” (or “previous”) to go repeatedly through all the tabs – imho not particularly intuitive as there’s no indication of what all the tabs are and how to jump straight to a specific tab.

image image

6) Start Menu – instead of the “Move to Top” option you got when you click and hold on an icon in the start menu.  Now when you click and hold you can drag the icon around the start menu to change the layout to suit you. Unfortunately you don’t seem to be able to add/remove folders, nor drop an icon into a folder.

Now I’m sure there are other new features that I haven’t come across but these are the ones that after 20secs caught my eyes.

Widget Security

Following my previous post on Windows Mobile Widget security I got feedback that the proposed solution wasn’t that secure and that it was easily foiled by impersonating the header information, and that the widget should be authenticating the user based on a set of credentials. This is 100% accurate and I whole-heartedly agree…. except we’re looking at two different problems here. 

-If the users of your application/widget are known to you (ie they have registered with your site/application and they have a username/password or some other form of credentials), then you should be prompting them to enter their credentials and use that to authenticate against the server.  Of course you can then cache these credentials on the device so that the user doesn’t need to re-enter them, or slightly better cache an authentication token that permits them access for a certain period – that way at least someone with physical access to the device can’t extract their actual credentials.

-If the users of your application/widget are not know to you, and you want to limit access to your data service then you need to authenticate your application some how.  Unfortunately storing any credentials on the device, unless you write a native module that hides the details, is somewhat problematic as in theory anyone with access to your application can decode the key.  In my previous post I discussed a strategy to minimise not eliminate the possibility of someone using your data source without your permission.

I’d be interested to hear of other alternative strategies for protecting anonymous data services.

Windows Mobile Widget (lack of) Security

I have to preface this post by saying that Windows Mobile Widgets run under the same context as Internet Explorer Mobile on a Windows Mobile 6.5. This means that in terms of securing data across the wire you should use HTTPS/SSL over any connection you wish to send usernames, passwords and other sensitive data. What I want to cover in this post is some information on how to protect the security of your data sources.

Windows Mobile Widgets are great for visualising existing web data sources.  By this I mean that instead of relying on users browsing to your website on their mobile device, you can build a widget that mashes up rss, web service and other data to build a rich local user experience. Unfortunately, if you only want your data sources to be consumed by the widget you build there are a couple of things you should be aware of:

  • Widgets are just plain text html files with or without javascript, which is also plain text – this means that any thing you put in a widget can easily be extracted.  Sure, you can obfuscate the javascript code, but essentially any usernames, passwords or keys will be able to be decoded.
  • Once a widget is installed on the device it is possible for anyone with physical access to the device to extract the widget files. This means that any user who downloads and installs your widget from the Marketplace will be able to connect to their device and view (and in fact edit) the widget files. [This is assumed based on the behaviour of the Windows Mobile 6.5 emulator and may be different with an RTM build of Windows Mobile where they may work out how to protect widget files]
  • With access to the usernames, passwords or keys from your widget files any developer will be able to create their own widget that accesses your data sources without your permission.

Ok, so I mentioned something about protecting your data sources…. well, here is a proposed strategy for ensuring your data is only accessed by your widget running on a Windows Mobile device:

  • If you look at the headers for a request coming from a widget you will notice a couple of things:
    • Referer: x-widget1:///\\Program%20Files\\Widgets\\User\\2\\Products.htm
      • The \Program Files\Widgets\User\2\Products.htm is the name of the file on the Windows Mobile device that is loaded into the widget. Widgets get installed into the \Program Files\Widgets\User folder by expanding .widget zip file onto the device.
    • User-Agent: WMWidgets 1.0 (MSIE6.0;Windows CE;http://www.builttoroam.com/ProductWidget)
      • The agent string “WMWidgets 1.0” confirms that this request is coming from a Windows Mobile device, whilst the “http://www.builttoroam.com/ProductWidget” is the id of my widget defined within the config.xml file eg:
      • <?xml version="1.0" encoding="utf-8" ?>
        <widget xmlns="
        http://www.w3.org/ns/widgets"
                id="http://www.builttoroam.com/ProductWidget"
                version="1.0">

  • You can make use of this header information in two ways.
    1. The first, and probably simpler, way is to verify these headers as part of the call to your data source. This is the easiest way if you own and are able to change the way that the data sources work.
    2. If you can’t modify the data source and they take a username, password or key to access them, then you need to protect these pieces of information. Do not insert them into the widget html or javascript itself (as outlined above they are very easy to extract off the device). Instead build a simple web service that will return the required username, password or key upon request.  As part of the request you would of course verify the headers to ensure it is coming from your widget running on a Windows Mobile device.

This sounds all very well but what’s to stop someone from getting my widget, copying the code into their own widget and using the same id and/or filenames. Well the answer to this is that I’m assuming that the id specified in the config.xml file will be unique on the Windows Mobile Marketplace [yet to be confirmed by Microsoft but that would seem logical since there needs to be a way in Marketplace to uniquely identify your widget]. This would mean that another party can’t create a widget using your id and upload it to Marketplace.

But what’s to stop someone from creating a widget with the same id and publishing it on their website. The answer is that there is nothing to stop them from doing that. However, no non-cracked Windows Mobile device will be able to install that widget – Windows Mobile Marketplace is the only way to install widgets on a non-cracked Windows Mobile 6.5 device. On the Windows Mobile 6.5 emulators, you can simply copy a .widget file onto the device and then install it by clicking on it in File Explorer on the device. This is not possible on a non-cracked Windows Mobile device as the file extension .widget is not recognised by the device (By setting the following registry keys will enable this feature on your device and thus “cracking” your device: [HKEY_CLASSES_ROOT\riapp] "EditFlags"=dword:00010000,  [HKEY_CLASSES_ROOT\riapp\Shell\Open\Command] @="wmwidgetinstaller.exe %1").

Why is the HTML version in Windows Mobile 6.5 in 4.01 and Not 5?

One of the questions asked at the Sydney Windows Mobile Debug Day was what version of HTML does Windows Mobile 6.5 Internet Explorer Mobile, and subsequently Widgets, support? The answer is that IE Mobile 6 supports HTML v4.01 (as well as javascript 5.7). This received a bit of debate since the Palm Pre offers support for HTML 5 and as such wouldn’t Microsoft want to future proof their platform.

I think one thing to remember is that most OEMs who build and sell Windows Mobile phones do not offer regular or frequent updates to the ROMs, and unlike some platforms, Microsoft is unable to push out updates when they make changes to the core platform.  Why is this relevant? Well, if you are across the HTML standards you will know that HTML 4.01 is “a stable document and may be used as reference material or cited as a normative reference from another document” (from the W3C HTML 4.01 Specification), where as the HTML 5 “specification is not stable” (from the W3C HTML 5 Specification) and they further state:

Implementors who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation stage should join the aforementioned mailing lists and take part in the discussions.

Whilst I’m sure that Microsoft has representation on the appropriate committees/lists, I would suggest that it is a little presumptuous to implement HTML 5 on a device that is no easily updatable. I guess we can only hope that the HTML 5 specification is locked down in time for the next version of Windows Mobile.

Adding Maps to your Windows Mobile Widget

One of the questions that came up at the Windows Mobile Debug Days (part of the Codemasons’ Guild) was how to integrate mapping into your application.  This prompted me to experiment with integrating the Bing Map (javascript) Control into a Windows Mobile Widget.  Whilst the javascript engine in Windows Mobile 6.5 has been given a massive injection of life, I was very skeptical that the javascript control would work out of the box. I was proven wrong with the map working with no modifications.  The following code is taken out of the online visual sdk, with the only change being the height and width of the map div to fit into the widget.

<html>
   <head>
      <title></title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <script type="text/javascript" src="
http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
      <script type="text/javascript">
          var map = null;

          function GetMap() {
              map = new VEMap('myMap');
              map.LoadMap();
          }  
      </script>
   </head>
   <body onload="GetMap();">
      <div id='myMap' style="position:relative; width:240px; height:260px;"></div>
   </body>
</html>

The only issues I found with the javascript control relate to the gesture support within the widgets in Windows Mobile 6.5. When you pan your finger within a widget it attempts to scroll the widget itself.  Unfortunately this means that you can pan the map by dragging your finger across the map.  Instead you have to use the navigation control in the top left corner.

clip_image002

Windows Mobile Happenings

This post covers a number of “happenings” in the Windows Mobile space, so I’ll start with a list:

Wow, what a list.  Well let’s start at the top…

Codemasons’ Guild

The Codemasons’ Guild is the brainchild of the Australian Windows Mobile team.  These guys rock; at a time where I’m continually astounded by the lack of action from Microsoft in the Windows Mobile space, the action that’s coming out of the Australian team is inspiring!  Anyhow, back on topic…. the Guild is a local Australian portal for developers interested in building applications for Windows Mobile.  The idea is that by bringing together some industry names (James, Rog, Nick, Dave, and myself), partners in the Windows Mobile space, events and a forum, we can build a local community that will foster a whole industry around device applications.

Jumpstart Series

Next on the list is the Jumpstart series.  This is actually a sub-item of the Codemasons’ Guild as all the information about this series is available via the Guild website. To date there have been three sessions:

  1. Session 1: View the recorded introduction session to Windows Mobile 6.5 and Marketplace
  2. Session 2: Getting started developing for Windows Mobile 6.5
  3. Session 3: Widgets: the new Internet Application (recording not yet available)

At this stage the team is looking at how this series can be continued.  If you have any topic that you would especially like to be covered, please let one of the WinMo Oz team know.

Marketplace

The Windows Mobile Marketplace has opened for application submissions, including the Race To Market challenge. You should definitely take a read of the following documents to get ready for submitting your application to marketplace.

Note that in order to submit your application you will need to register ($99 US/$150AUD); you will then have to wait for your digital certificate from GeoTrust for application signing.  The upshot – don’t assume you’ll be able to sign up and submit in one sitting.

Debug Days

Next week the Australian Windows Mobile team, coupled with the team from the Guild are hosting three Debug Days. Well, they’re actually evenings – from 4:30pm through to around 9pm we’ll be helping you with any issues you may have in designing, building, debugging, deploying….. or anything else related to Windows Mobile development.  The idea is that you can either let us know before hand, or just rock up and surprise us, with anything you want help with.  This is going to be a fully interactive session – the more thinking/prep you do before hand, the more value you’ll get out of the session.

Anyhow, the registration links are now open and I look forward to catching up with as many of you as possible next week:

Competitions

As if I haven’t covered enough, there are a bunch of competitions you can enter related to WM6.5 and Marketplace:

Sign up now and get your mobile application on the shelves of Windows Marketplace for Mobile. When you submit your app, you’ll also be eligible to win the Race to Market Challenge and exciting prizes, including:

  • A Microsoft Surface table
  • Online marketing and promotion
  • A one-of-a-kind trophy

Ready for the challenge?

For developers - do you have a clever concept for a Windows® phone application? We’d like to help you turn it into a best-seller on Windows Marketplace for Mobile. Interested? Simply submit your brilliant concept by August 21, 2009 and you could win one of three prize packs worth $5,000 each! You can enter as many times as you like, as long as each entry is different.

The each of the 3 prizes includes a fully paid ticket to TechEd Australia on the Gold Coast (with 3 nights accom & flights).  The 3 prize winners will have their app showcased at TechEd, including at Smackdown – where we will judge the Master Codemason Application.

Are you a Student? Got a great idea for a mobile app but no way to make it happen? Submit your idea by August 17, 2009 and you could win one of two developer mentorships and share in prizes! You can enter as many times as you like, as long as each entry is different

SDNUG

If you didn’t see the Livemeeting last week on Windows Mobile 6.5 and Widgets, I’m doing a session at the Sydney Deep .NET User Group tomorrow evening.  Full details:

SDNUG Double Header:

Web and application access: supporting the needs of people with disabilities by Dr Scott Hollier

WM6.5, Widgets and Marketplace by me :-)

Thursday 6 Aug 2009 6.00pm - 8.00pm (add to calendar)

Speed Up Your Windows Mobile Builds

One of the things that continually frustrates me about building Windows Mobile applications in Visual Studio is that for some reason it takes so long to do a build. Behind the scenes there is a lot that goes on and I always forget that there is one part of the build that you can mostly do without, the platform verification.  As pointed out by the now quite old post by the Visual Studio for Devices team you can disable this part of the build quite easily:

1) Open the file %windir%\Microsoft.NET\Framework\v2.0.50727\Microsoft.CompactFramework.Common.Targets for editing.

2) Go to the line which reads:
Name="PlatformVerificationTask">
and change it to:
Name="PlatformVerificationTask" Condition="'$(SkipPlatformVerification)' != 'true'">

3) Add the SkipPlatformVerification environment variable to the system and set it to "true" (To re-enable Platform Verification set the environment variable to "false")

4) Restart Visual Studio for the changes to take effect (If building from the command line using MSBuild, add /p:SkipPlatformVerification=true to your command line to turn off the task. You can specify the variable in the project file also, so that this information is persisted across sessions).

As noted by Thomas you may just want to disable this functionality, instead of having it contingent on an environment variable.  In this case just change the PlatformVerificationTask line to:

Name="PlatformVerificationTask" Condition="false">

Doing this you should notice that your mobile projects build just as fast as other projects within Visual Studio.  Note: This is still relevant in Visual Studio 2008 with SP1.

WCF on Windows Mobile and .NET Compact Framework

I was just listening to Don, Dave and James on the second of the Jumpstart series for the Codemasons’ Guild and the topic of communicating via WCF came up.  Now typically when I build mobile apps I don’t go through all the pain of using WCF, I simply use a regular asmx web service and then use Add Web Reference to add it to my mobile project.  To secure it, I just communicate over SSL. If you do want/need to use WCF on the server side, there are a couple of options to do this.

Before we jump into how you use WCF, let me point out a couple of useful powertoys:

The Power Toys for .NET CF include NetCFSvcUtil.exe which is a device equivalent of SvcUtil.exe and is needed in order generate the WCF proxy.

Firstly, you need to be aware that the .NET CF has some severe limitations when it comes to WCF.  Unfortunately the only binding that is supported (excluding the much over-hyped WCF via Exchange) is basicHttpBinding. For the WCF service you want to consume you need to change it from using the default wsHttpBinding.  This can be done by launching the Tools > WCF Service Configuration Editor from Visual Studio. Open the web.config file for the WCF Service project.  Under Endpoints, adjust the Binding to basicHttpBinding.

image

Save this change and run the WCF Service.

Now to the options…..

1) The first option is to use Add Web Reference.  This is by far the simplest approach as you can click Browse to: Web services in this solution.  Select your service and click Add Reference. 

image

Once you have added the reference you can call your service method the same way you would from a regular desktop application:

localhost.Service1 service = new localhost.Service1();
service.Url = service.Url.Replace("localhost", "192.168.1.2");
return service.GetData(5, true);

Note: You have to change the “localhost” host name to something that can be resolved by the device.  I typically just use the ip address of the development machine.  Clearly for production you will want to specify this in a configuration file or make it a configurable setting within your application.

2) The second option is to use NetCFSvcUtil.exe to generate the appropriate WCF proxy information. Start by opening up the Visual Studio command prompt (Start > Microsoft Visual Studio 2008 > Visual Studio Tools > Visual Studio 2008 Command Prompt) and adding the path to the Compact Framework power toys:

>> set path=%path%;C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin

Navigate to the folder where you want the proxy files to be created and then use NetCFSvcUtil.  I figured this would be quite simple but it appears that somewhere between Vista SP1 and SP2 (and there are reports of this problem on Windows 7 too) a bug in NETCFSvcUtil surfaced preventing it from working.

image

As you can see the error message is really helpful:

Error: An error occurred in the tool.
Error: Error in the application.

Currently, there doesn’t seem to be a workaround for this.  Some people have had varied success by changing the parameters and return types of the service methods.  The one strategy I used that appears to work is to use a combination of SvcUtil and NetCFSvcUtil.

>> svcutil.exe c:\temp\WindowsMobileServices\MyDataServices\bin\MyDataServices.dll

Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation.  All rights reserved.

Generating metadata files...
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.wsdl
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.xsd
C:\temp\WindowsMobileServices\WindowsMobileServices\schemas.microsoft.com.2003.1
0.Serialization.xsd

>> netcfsvcutil.exe tempuri.org.wsd tempuri.org.xsd
Microsoft (R) .NET Compact Framework Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.5.0.0]
Copyright (c) Microsoft Corporation.  All rights reserved.

Generating files...
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.cs
C:\temp\WindowsMobileServices\WindowsMobileServices\CFClientBase.cs

Now, include all the generated file into your mobile project.  You will also need to add references to System.ServiceModel and System.Runtime.Serialization.

In order to call your service method you can now write the following:

var binding = System.ServiceModel.BasicHttpBinding();
var endpoint = System.ServiceModel.EndpointAddress("http://192.168.1.2:6323/Service1.svc");
Service1Client client = new Service1Client(new binding, new endpoint
);
return client.GetData(5);

So, the question is why would you go the second option?  Well if you actually look at the generated code, adding the WCF service using Add Web Reference adds a bunch of unnecessary fields.  When calling the method GetData there is a second parameter called “valueSpecified” which is little more than a flag to indicate if the first parameter was specified or not.  This is not required if you use the second option to generate the proxy information.

Late Notice: Windows Mobile Jumpstart #2

Jumpstart #2:

Getting started developing for Windows Mobile 6.5: developer tools, technologies, debugging and testing
Learn how to get started developing for Windows Mobile 6.5, what can be done with the new technologies and how to use your existing skills to start coding apps for Windows Mobile 6.5 phones via Windows Marketplace.
Register for the second Live Meeting

Date/Time: Friday, July 17 (today!!), 2009 1:00 PM Australia (East)

 

And so that you’re ready for the next session:

Jumpstart #3

Widgets: the new internet application
Get insight into the Widgets platform for Windows Mobile 6.5 and discover how to develop powerful Widgets that leverage today’s web experience in a new way.
Register for the third Live Meeting

Date/Time: Friday, 31 July 2009 12:30 PM Australia (East)

Spb Mobile Shell 3.0

Frank posted about this a few weeks ago now but I didn’t want to repost it until I’d had a chance to use the new Spb Mobile Shell 3.0. If you’ve ever felt that the Windows Mobile interface is tired and stayed then this product will give your device a new lease of life.

3D carousel

Unlike some of the other interfaces that OEMs such as HTC have been working on, Spb Mobile Shell doesn’t seem to completely drain the system resources.  It works nicely on Windows Mobile 6, 6.1 and 6.5 from what I’ve seen and means that just about everything you want to do is just a few clicks or slides away.  At $29.95 this is a must!

Codemasons’ Guild for Windows Mobile Developer

Don has just posted some of the Q&A from last weeks jumpstart Live Meeting session.  I had a brief listen to the recorded session as I wasn’t able to attend the event and it provides a good roundup of what’s included in Windows Mobile 6.5 and the Marketplace.  The one question that I love, and we’ll hear again and again, is the following:

Q: If the application is available for free? what will Microsoft charge developer?

A: It will be charged as one of your signings

Microsoft’s stance on this one appears to be that there is a cost associated with signing and verifying your application.  Whilst they are prepared to cover 5 of these included in your registration for marketplace, there after you’re on your own, even for free applications. Unlike the iPhone where you have to use the AppStore to distribute your application, with Windows Mobile you can at least make the cab file available as a download from your website.  My suggestion is that if you are going to build free applications for Windows Mobile, build a simple application that provides an catalogue of all your free applications and provides a mechanism for downloading them.  Add this application to marketplace (using only 1 of your free apps) and there after just post your free applications to your blog, website etc and update the catalogue.

Oh, and the other question I love is:

Q: Is this a once off fees ?

A: 99 USD charge fee for 5 app signings

This answer is actually incorrect.  It’s $150 AUD!!! Why they can’t use the current exchange rate I don’t know but $150 is way too much imho for signing up to this service (For comparison, the AppStore I think was $115 AUD).

Windows Phone 7, Android and iOS with Mono III: Shared Libraries

Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Windows Phone 7, Android and iOS with Mono III: Shared Libraries

We’ve got the basics functioning across all three platforms but at the moment we’ve written all our code within the confines of three different projects with no code shared between them. Of course, one of the reasons for using MonoTouch/MonoDroid is to try to reuse and share as much code as possible between the platforms. That and hopefully we don’t need to spend too much time writing obnoxious Java or Objective-C code.

Now we could simply share files between the three projects, after it’s just .NET code but I want to be a little more adventurous. I’m going to create a class library that each of my three platform projects can access. Now I’m actually going to have platform specific class library project files, but the rest of the files contained within the project will be common across all projects.

Unfortunately I don’t know of an easy way to do this, so I’ll show you the long way – if someone does know an easier way to do this, please let me know because this way is painful.

Windows Phone 7

- Add a new project to your Windows Phone 7 application based on the Windows Phone Class Library template. In my case my shared library is going to be called BuiltToRoam.General but rather than leave it in the solution folder, I’m going to go up a level, create a folder called Shared and create the project in there. This will give me a folder structure as follows:

\CrossPlatform
    \Shared
        \BuiltToRoam.General
    \NicksNetTravels
    \NicksNetTravelsAndroid
    \NicksNetTravelsiOS

- In the new project rename Class1.cs to Utility.cs and add the following simple extension method:

namespace BuiltToRoam.General {     
  public static class Utility {         
    public static T EnumParse<T>(this string enumValue, bool ignoreCase = true) where T : struct {
             try {
                 if (string.IsNullOrEmpty(enumValue)) return default(T);
                 return (T)Enum.Parse(typeof(T), enumValue, ignoreCase);
             }
             catch {
                 return default(T);
             }
         }
     }
}

As you can probably work out all this method does is parse a string and return the appropriate enumeration value (strongly typed of course!).

- Right-click your Windows Phone 7 application project in the Solution Explorer window and select Add Reference; Select the BuiltToRoam.General project and add it as a reference.

- Now, just to test that this works, modify the MainPage constructor (in MainPage.xaml.cs) as follows:

public enum Wp7EnumTest {
First,
Second
}
 
public MainPage() {
InitializeComponent();

var test = ("First").EnumParse<Wp7EnumTest>();
}
 

- Build and run. Set a breakpoint after the EnumParse line to check that this works.

 

iOS

This is where it starts to get messy. Essentially you want to create an empty project stub for both iOS and Android. You can’t call them the same as the WP7 project because then you’ll end up with a file conflict; yet if you give them a different name they’ll create a new project folder which won’t contain the same files. There are actually two options here: Option 1 is that you just live with the fact that you’ll have multiple project folders – Visual Studio allows you to add files as links which means that you can have the files in one project (eg the Windows Phone 7 project you just created) and then as links in the iOS and Android projects. Option 2, which I prefer, is to create the project stubs with a slightly different name, and then move them into the same directory as the Windows Phone 7 project file. Then you can add literally the same files as the projects will be in the same folder. Here I’m going to go with Option 2, so if it didn’t make sense when I described it, then hopefully it will by the end of this.

- In MonoDevelop, right-click the Solution node in the Solution window and select Add > Add New Project. Select the MonoTouch Library Project from under the C# > iPhone and iPad node. Give you project a name, in this case BuiltToRoam.General.iOS, and click Forward to create the new project.

- Right-click the newly created project and select Delete (this might seem odd since what we actually want to do is to remove it from the solution, rather than deleting the project). When prompted, select “Remove” from the dialog (if you select “Delete from Disk” it will do just that, which is not what we want).

- In Finder, locate the newly created project file (in this case BuiltToRoam.Genera.iOS.csproj) and move it from where it was created into the shared project folder (in this case \Shared\BuiltToRoam.General).

- Back in MonoDevelop, right-click the Solution node. This time select Add > Add Existing Project and navigate to the shared project folder and select the project you just moved.

- Right-click the References folder of your iOS application project (ie NicksNetTravelsiOS) and select Edit References. Select the Projects tab and check the box alongside the shared project you just added (ie BuiltToRoam.General.iOS).

- Right-click the shared project and select Add > Add Files. Select Utilities.cs and click Open to add the file to your project

- Modify the code in Main.cs to include the following code to test the code in the referenced assembly works.

public enum iOSEnumTest{
    First,
    Second
}
 
public class Application  {
  static void Main (string[] args) {
   var test = ("First").EnumParse<iOSEnumTest>();

   UIApplication.Main (args);           
  }
}

- Again, set a breakpoint after the EnumParse line to validate that this code works.

Note: You may find that your breakpoints aren’t hit for some reason. This may be because you’re running without attaching the debugger. To run with the debugger select Run > Run With > Mono Soft Debugger for iPhone

 

Android

Ok, last one. We’re going to follow basically the same process over again. Perhaps I should make a project template for this….. volunteers?

- File > Add > New Project. Select the Mono for Android Class Library from the Mono for Android node. Give your project a name, in this case BuiltToRoam.General.Android, and click OK to create the class library.

- Like we did in the iOS case: Right-click on the newly created project and click Remove. Find the project file (ie BuiltToRoam.General.Android.csproj) in Windows Explorer and move it into the shared project folder.

- Right-click the solution node in Solution Explorer and select Add > Existing Project. Locate the shared project file and click Open to add it to the solution.

- Right-click the Android application project and select Add Reference. Select the Projects tab, select the BuiltToRoam.General.Android project and add it as a reference.

- Right-click the shared project file and select Add > Existing Item. Find Utilities.cs and click Add.

- To verify that this works, add the following code to Activity1.cs

public enum AndroidEnumTest {
    First,
    Second
}

protected override void OnCreate(Bundle bundle) {
    var test = ("First").EnumParse<AndroidEnumTest>();
    ...
}

- Set a break point after the EnumParse line to verify this works.

So there you have it, the ability to share whole projects between WP7, Android and iOS. It wasn’t that hard after all.

Comments (2) -

  • Jeff

    4/23/2011 8:16:47 PM |

    Are you using a Mac or a PC or both for these projects?  It looks like from the examples you are using one platform but I can't figure out how you can do that.

  • Nick

    4/23/2011 8:45:34 PM |

    Unfortunately you have to use a Mac to do ios dev. I'm using a PC to do the WP7 and Android development. I'm also using svn as my source code repository to allow me to easily share code between the platforms.

Pingbacks and trackbacks (2)+

Comments are closed