Serving Silverlight applications on client PCs without a web server using WCF 3.5

There are scenarios where you don’t want to have IIS installed on a machine and you still want to serve a Silverlight application for local or remote users. One of this scenario is an embedded machine that controls some machinery that needs to have a local UI and a remote UI.
I have lately run in one of this scenario where an embedded PC needs to control some external switches and motors that sould have a local (touch panel) UI and a couple remote PC, eventually even on mobile devices. Although IIS can be installed on the machine it seems a bit of overkill for serving a .xap file only.
To note that Silverlight is not supported on Windows XP embedded but it will be in the next release called Windows Embedded Standard that should ship in the next few months.
View that you probably don’t have PLC for controlling motors, switches, etc.. in this example I’m building a simple performance monitor with a REST interface and a SL UI that you can be run locally or remotely.
How do I build my own web server? One cool new feature of WCF 3.5 is the new Web Programming Model that is made for building REST services. View that a REST service is nothing more than a HTTP request/response with a XML return I quickly figure out that I could transform it on a custom Web Server. Plus a REST interface would also make sense in this embedded scenario so other devices could easily communicate with the services itself as I will be showing you in this blog.
Basically I need to build a WCF service that has a REST interface for querying the CPU utilization and a service to serve the .HTML page hosting SL and the SL application (.xap)
Let’s start and build the solution:
I first create a console application. Normally you would host your services on a Windows Service but for demo purpose a console app is easier. Now I add a WCF services class called RestService.
In the IRestService.cs I will write the REST interface for the CPU monitoring but also the interface for serving the Html page that will host Silverlight and the .xap. You also need to add a reference to the Sytem.ServiceModel.Web assembly.
IRestService.cs code:
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "")]
Stream GetHtml();

[OperationContract]
[WebGet(UriTemplate = "clientaccesspolicy.xml")]
Message GetPolicy();

[OperationContract]
[WebGet(UriTemplate = "SLCPUClient.xap")]
Stream GetXap();

[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/CPU")]
float GetCPU();

}
Basically this will expose the following url:
http://localhost:8001 will serve the .HTML page hosting the SL plug in. GetHtml method
http://localhost:8001/clientaccesspolicy.xml
http://localhost:8001/SLCPUClient.xap will server the .xap file
http://localhost:8001/CPU is the REST interface for getting the CPU utilization.
One thing to notice is that the /CPU is returning a plain XML. So it needs to be parsed in SL. Now for a single float value is not a big issue but if you would return a List containing multiple values it could be a tedious work. One way would be to expose it as a SOAP service but I would like to keep a simple REST interface that should be fully operable even from a browser url. The solution is to use JSON serialization, view that Silverlight has a JSON serializer this should be fairly easy to implement. So we need to add an additional method for getting a JSON payload:
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/CPU?Json=true")]
float GetCPUJson();
Now let’s implement the service itself in the RestSerice.cs:
public class RestService : IRestService
{

public static PerformanceCounter pc = new PerformanceCounter(”Processor”, “% Processor Time”, “_Total”);

public Stream GetHtml()
{
return new FileStream(”TestPage.html”, FileMode.Open, FileAccess.Read);

}

public Stream GetXap()
{
WebOperationContext.Current.OutgoingResponse.ContentType = “application/x-silverlight”;
return new FileStream(”SLCPUClient.xap”, FileMode.Open, FileAccess.Read);
}

public Message GetPolicy()
{

TextReader reader = new StringReader(@”







“);

XmlReader xmlreader = XmlReader.Create(reader);

return Message.CreateMessage(MessageVersion.None, “”, xmlreader);
}

public float GetCPU()
{
WebOperationContext.Current.OutgoingResponse.Headers["Cache-Control"] = “no-cache”;

return pc.NextValue();
}

public float GetCPUJson()
{
return GetCPU();
}

}

You can see that is fairly easy to implement the html and .xap serving methods. One small notice is that you need to set the MIME type for .xap:
WebOperationContext.Current.OutgoingResponse.ContentType = “application/x-silverlight”;

Another very important piece of code that you need to add is the caching directive. If you omit that your browser, so indirectly the SL client, will cache the /CPU request:
WebOperationContext.Current.OutgoingResponse.Headers["Cache-Control"] = “no-cache”;
Now we simply need to add the code for starting the WCF services. If you use only the Web Programmability Model you can skip completely the config file so you should remove or comment out the services part in the app.config file.
Let’s add the code for starting the service on the console startup, Program.cs:
static void Main(string[] args)
{
string url = “http://localhost:8001″;

WebServiceHost host = new WebServiceHost(typeof(RestService), new Uri(url));

host.Open();

Console.WriteLine(”Ready”);

Console.ReadLine();
}

The cool thing about the new web programmability of WCF 3.5 is that you can basically build your own web server in a few line of code.
Now we can try the rest interface by simply starting the console app and open a browser with this url http://localhost:8001/CPU hit F5 a couple of time and you will see the CPU value of your machine.
Now is time to create the Silverlight client that I call SLCPUClient. You can create a project without a web site but it is handy to have one as an easy way to debug.Remeber to add a reference to System.ServiceModel.Web assembly where the JSON serializer class is located.
Normally at this point I right click on the Page.xaml and open it in Blend to build the UI. Here the XAML code that I create with Blend:





Now is time to wire up some code on the Silverlight client:
DispatcherTimer dt = new DispatcherTimer();

public Page()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{

dt.Tick += new EventHandler(dt_Tick);
dt.Interval = new TimeSpan(0,0,0,0,200);
dt.Start();

}

void dt_Tick(object sender, EventArgs e)
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(”http://localhost:8001/CPU?Json=true”));

}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
float cpuValue;
using (MemoryStream ms = new MemoryStream())
{
byte[] bytes = UnicodeEncoding.Unicode.GetBytes(e.Result);
ms.Write(bytes, 0, bytes.Length);
ms.Position = 0;
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(float));
cpuValue = (float)serializer.ReadObject(ms);

}

PctRectangle.Width = cpuValue * 2;
CpuLabel.Text = ((int)cpuValue).ToString() + ” %”;
}

Basically I have a timer that every 200 ms will call the service with the ?Json=true and deserialize back the return as float. Then I simply change the width of a green rectangle and display the value as a label too. You could easily have a List to be serialized and deserialized through Json if you need to pass a collection of complex type.
The last thing to do is to copy the .HTML and the .XAP where the console app is executed.you can grab the TestPage.html and the SLCPUClient.xap here WCFSelfHostBlog\SLCPUClient\Bin\Debug (or the release folder) and copy on the debug (or release folder) of the console app: WCFSelfHostBlog\bin\Debug. You could even setup Visual Studio to copy these two files at each compilation.
Now if you run the console application and you open your browser on http://localhost:8001/ you will get the Silverlight apps displaying your CPU usage.
One last touch is to add this code on the console Main method to automatically start Internet Explorer in kiosk mode when you start your console app:
ProcessStartInfo si = new ProcessStartInfo(”iexplore.exe”, “-k \”http://localhost:8001\”");
Process.Start(si);

Pretty cool and easy. Normally I would recommend using WPF for this sort of apps but if you need to control a machinery locally and remotely by different type of PC or MAC or even mobile, Silverlight and WCF 3.5 is surely a viable solution.
Attached you find the full solution. Simply start the WCFSelfHostBlog project and you can test the app.
Have fun
Ronnie Saurenmann

Tags:

Comments are closed.